pax_global_header00006660000000000000000000000064150035400300014500gustar00rootroot0000000000000052 comment=d423e7addc501738bccd663497bd374bf8bd3166 opensnitch-1.6.9/000077500000000000000000000000001500354003000136675ustar00rootroot00000000000000opensnitch-1.6.9/.github/000077500000000000000000000000001500354003000152275ustar00rootroot00000000000000opensnitch-1.6.9/.github/FUNDING.yml000066400000000000000000000012161500354003000170440ustar00rootroot00000000000000# These are supported funding model platforms github: gustavo-iniguez-goya patreon: # Replace with a single patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] opensnitch-1.6.9/.github/ISSUE_TEMPLATE/000077500000000000000000000000001500354003000174125ustar00rootroot00000000000000opensnitch-1.6.9/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000037161500354003000221130ustar00rootroot00000000000000--- name: 🞠Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- Please, check the FAQ and Known Problems pages before creating the bug report: https://github.com/evilsocket/opensnitch/wiki/FAQs GUI related issues: https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems Daemon related issues: - Run `opensnitchd -check-requirements` to see if your kernel is compatible. - https://github.com/evilsocket/opensnitch/wiki/daemon-known-problems **Describe the bug** A clear and concise description of what the bug is. Include the following information: - OpenSnitch version. - OS: [e.g. Debian GNU/Linux, ArchLinux, Slackware, ...] - Version [e.g. Buster, 10.3, 20.04] - Window Manager: [e.g. GNOME Shell, KDE, enlightenment, i3wm, ...] - Kernel version: echo $(uname -a) **To Reproduce** Describe in detail as much as you can what happened. Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Post error logs:** If it's a crash of the GUI: - Launch it from a terminal and reproduce the issue. - Post the errors logged to the terminal. If the daemon doesn't start or doesn't intercept connections: - Run `opensnitchd -check-requirements` to see if your kernel is compatible. - Post last 15 lines of the log file `/var/log/opensnitchd.log` - Or launch it from a terminal as root (`# /usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules`) and post the errors logged to the terminal. If the deb or rpm packages fail to install: - Install them from a terminal (`$ sudo dpkg -i opensnitch*` / `$ sudo yum install opensnitch*`), and post the errors logged to stdout. **Expected behavior (optional)** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots or videos to help explain your problem. It may help to understand the issue much better. **Additional context** Add any other context about the problem here. opensnitch-1.6.9/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002131500354003000213760ustar00rootroot00000000000000contact_links: - name: 🙋 Question url: https://github.com/evilsocket/opensnitch/discussions/new about: Ask your question here opensnitch-1.6.9/.github/ISSUE_TEMPLATE/feature-request.md000066400000000000000000000004341500354003000230560ustar00rootroot00000000000000--- name: 💡 Feature request about: Suggest an idea title: '[Feature Request] ' labels: feature assignees: '' --- <!-- Note: Please, use the search box to see if this feature has already been requested. --> ### Summary: <!-- A concise description of the new feature. --> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/.github/workflows/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017264�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/.github/workflows/build_ebpf_modules.yml�������������������������������������������0000664�0000000�0000000�00000004573�15003540030�0023643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This is a basic workflow to help you get started with Actions name: CI - build eBPF modules # Controls when the workflow will run on: # Triggers the workflow on push or pull request events but only for the "master" branch push: paths: - 'ebpf_prog/*' - '.github/workflows/build_ebpf_modules.yml' pull_request: paths: - 'ebpf_prog/*' - '.github/workflows/build_ebpf_modules.yml' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" # The matrix configuration will execute the steps, once per dimension defined: # kernel 5.8 + tag 1.5.0 # kernel 5.8 + tag master # kernel 6.0 + tag 1.5.0, etc build: strategy: matrix: kernel: ["6.0"] tag: ["1.6.0"] runs-on: ubuntu-22.04 steps: - name: Check out code into the Go module directory uses: actions/checkout@v3 with: # ref: can be a branch name, tag, commit, etc ref: ${{ matrix.tag }} - name: Get dependencies run: | sudo apt-get install git dpkg-dev rpm flex bison ca-certificates wget python3 rsync bc libssl-dev clang llvm libelf-dev libzip-dev git libnetfilter-queue-dev libpcap-dev protobuf-compiler python3-pip dh-golang golang-any golang-golang-x-net-dev golang-google-grpc-dev golang-goprotobuf-dev libmnl-dev golang-github-vishvananda-netlink-dev golang-github-evilsocket-ftrace-dev golang-github-google-gopacket-dev golang-github-fsnotify-fsnotify-dev linux-headers-$(uname -r) - name: Download kernel sources and compile eBPF modules run: | kernel_version="${{ matrix.kernel }}" if [ ! -d utils/packaging/ ]; then mkdir -p utils/packaging/ fi wget https://raw.githubusercontent.com/evilsocket/opensnitch/master/utils/packaging/build_modules.sh -O utils/packaging/build_modules.sh bash utils/packaging/build_modules.sh $kernel_version sha1sum ebpf_prog/modules/opensnitch*o > ebpf_prog/modules/checksums.txt - uses: actions/upload-artifact@v4 with: name: opensnitch-ebpf-modules-${{ matrix.kernel }}-${{ matrix.tag }} path: ebpf_prog/modules/* �������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/.github/workflows/generic_validations.yml������������������������������������������0000664�0000000�0000000�00000001536�15003540030�0024025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: Test resources validation on: # Trigger this workflow only when ebpf modules changes. push: paths: - 'ui/resources/*' - '.github/workflows/generic.yml' pull_request: paths: - 'ui/resources/*' - '.github/workflows/generic.yml' # Allow to run this workflow manually from the Actions tab workflow_dispatch: jobs: build: name: Install tools runs-on: ubuntu-latest steps: - name: Check out git code uses: actions/checkout@v2 - name: Get and prepare dependencies run: | set -e set -x sudo apt install desktop-file-utils appstream - name: Validate resources run: | set -e set -x desktop-file-validate ui/resources/opensnitch_ui.desktop appstreamcli validate ui/resources/io.github.evilsocket.opensnitch.appdata.xml ������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/.github/workflows/go.yml�����������������������������������������������������������0000664�0000000�0000000�00000002436�15003540030�0020421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: Build status on: push: paths: - 'daemon/**' - '.github/workflows/go.yml' pull_request: paths: - 'daemon/**' - '.github/workflows/go.yml' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: build: name: Build runs-on: ubuntu-latest steps: - name: Set up Go 1.20.10 uses: actions/setup-go@v3 with: go-version: 1.20.10 id: go - name: Check out code into the Go module directory uses: actions/checkout@v3 - name: Get dependencies run: | sudo apt-get install git libnetfilter-queue-dev libmnl-dev libpcap-dev protobuf-compiler export GOPATH=~/go export PATH=$PATH:$GOPATH/bin go install github.com/golang/protobuf/protoc-gen-go@latest go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.1 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.0 cd proto make ../daemon/ui/protocol/ui.pb.go cd ../daemon go mod tidy; go mod vendor - name: Build run: | cd daemon go build -v . - name: Test run: | cd daemon sudo PRIVILEGED_TESTS=1 NETLINK_TESTS=1 go test ./... ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/.gitignore�������������������������������������������������������������������������0000664�0000000�0000000�00000000027�15003540030�0015656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.sock *.pyc *.profile ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/LICENSE����������������������������������������������������������������������������0000664�0000000�0000000�00000104515�15003540030�0014702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> 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. 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 for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <https://www.gnu.org/licenses/why-not-lgpl.html>. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/Makefile���������������������������������������������������������������������������0000664�0000000�0000000�00000001527�15003540030�0015334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������all: protocol opensnitch_daemon gui install: @cd daemon && make install @cd ui && make install protocol: @cd proto && make opensnitch_daemon: @cd daemon && make gui: @cd ui && make clean: @cd daemon && make clean @cd proto && make clean @cd ui && make clean run: cd ui && pip3 install --upgrade . && cd .. opensnitch-ui --socket unix:///tmp/osui.sock & ./daemon/opensnitchd -rules-path /etc/opensnitchd/rules -ui-socket unix:///tmp/osui.sock -cpu-profile cpu.profile -mem-profile mem.profile test: clear make clean clear mkdir -p rules make clear make run adblocker: clear make clean clear make clear python make_ads_rules.py clear cd ui && pip3 install --upgrade . && cd .. opensnitch-ui --socket unix:///tmp/osui.sock & ./daemon/opensnitchd -rules-path /etc/opensnitchd/rules -ui-socket unix:///tmp/osui.sock �������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/README.md��������������������������������������������������������������������������0000664�0000000�0000000�00000010016�15003540030�0015144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<p align="center"> <img alt="opensnitch" src="https://raw.githubusercontent.com/evilsocket/opensnitch/master/ui/opensnitch/res/icon.png" height="160" /> <p align="center"> <img src="https://github.com/evilsocket/opensnitch/workflows/Build%20status/badge.svg" /> <a href="https://github.com/evilsocket/opensnitch/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/evilsocket/opensnitch.svg?style=flat-square"></a> <a href="https://github.com/evilsocket/opensnitch/blob/master/LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-GPL3-brightgreen.svg?style=flat-square"></a> <a href="https://goreportcard.com/report/github.com/evilsocket/opensnitch/daemon"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/evilsocket/opensnitch/daemon?style=flat-square"></a> <a href="https://repology.org/project/opensnitch/versions"><img src="https://repology.org/badge/tiny-repos/opensnitch.svg" alt="Packaging status"></a> </p> </p> <p align="center"><strong>OpenSnitch</strong> is a GNU/Linux application firewall.</p> <p align="center">•• <a href="#key-features">Key Features</a> • <a href="#download">Download</a> • <a href="#installation">Installation</a> • <a href="#opensnitch-in-action">Usage examples</a> • <a href="#in-the-press">In the press</a> ••</p> <p align="center"> <img src="https://user-images.githubusercontent.com/2742953/85205382-6ba9cb00-b31b-11ea-8e9a-bd4b8b05a236.png" alt="OpenSnitch"/> </p> ## Key features * Interactive outbound connections filtering. * [Block ads, trackers or malware domains](https://github.com/evilsocket/opensnitch/wiki/block-lists) system wide. * Ability to [configure system firewall](https://github.com/evilsocket/opensnitch/wiki/System-rules) from the GUI (nftables). - Configure input policy, allow inbound services, etc. * Manage [multiple nodes](https://github.com/evilsocket/opensnitch/wiki/Nodes) from a centralized GUI. * [SIEM integration](https://github.com/evilsocket/opensnitch/wiki/SIEM-integration) ## Download Download deb/rpm packages for your system from https://github.com/evilsocket/opensnitch/releases ## Installation #### deb > $ sudo apt install ./opensnitch*.deb ./python3-opensnitch-ui*.deb #### rpm > $ sudo yum localinstall opensnitch-1*.rpm; sudo yum localinstall opensnitch-ui*.rpm Then run: `$ opensnitch-ui` or launch the GUI from the Applications menu. Please, refer to [the documentation](https://github.com/evilsocket/opensnitch/wiki/Installation) for detailed information. ## OpenSnitch in action Examples of OpenSnitch intercepting unexpected connections: https://github.com/evilsocket/opensnitch/discussions/categories/show-and-tell Have you seen a connection you didn't expect? [submit it!](https://github.com/evilsocket/opensnitch/discussions/new?category=show-and-tell) ## In the press - 2017 [PenTest Magazine](https://twitter.com/pentestmag/status/857321886807605248) - 11/2019 [It's Foss](https://itsfoss.com/opensnitch-firewall-linux/) - 03/2020 [Linux Format #232](https://www.linux-magazine.com/Issues/2020/232/Firewalld-and-OpenSnitch) - 08/2020 [Linux Magazine Polska #194](https://linux-magazine.pl/archiwum/wydanie/387) - 08/2021 [Linux Format #280](https://github.com/evilsocket/opensnitch/discussions/631) - 02/2022 [Linux User](https://www.linux-community.de/magazine/linuxuser/2022/03/) - 06/2022 [Linux Magazine #259](https://www.linux-magazine.com/Issues/2022/259/OpenSnitch) ## Donations If you find OpenSnitch useful and want to donate to the dedicated developers, you can do it from the **Sponsor this project** section on the right side of this repository. You can see here who are the current maintainers of OpenSnitch: https://github.com/evilsocket/opensnitch/commits/master ## Contributors [See the list](https://github.com/evilsocket/opensnitch/graphs/contributors) ## Translating <a href="https://hosted.weblate.org/engage/opensnitch/"> <img src="https://hosted.weblate.org/widgets/opensnitch/-/glossary/multi-auto.svg" alt="Translation status" /> </a> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015132�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/.gitignore������������������������������������������������������������������0000664�0000000�0000000�00000000023�15003540030�0017115�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitchd vendor �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/Gopkg.toml������������������������������������������������������������������0000664�0000000�0000000�00000000540�15003540030�0017075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[[constraint]] name = "github.com/fsnotify/fsnotify" version = "1.4.7" [[constraint]] name = "github.com/google/gopacket" version = "~1.1.14" [[constraint]] name = "google.golang.org/grpc" version = "~1.11.2" [[constraint]] name = "github.com/evilsocket/ftrace" version = "~1.2.0" [prune] go-tests = true unused-packages = true ����������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/Makefile��������������������������������������������������������������������0000664�0000000�0000000�00000001160�15003540030�0016570�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#SRC contains all *.go *.c *.h files in daemon/ and its subfolders SRC := $(shell find . -type f -name '*.go' -o -name '*.h' -o -name '*.c') PREFIX?=/usr/local all: opensnitchd install: @mkdir -p $(DESTDIR)/etc/opensnitchd/rules @install -Dm755 opensnitchd \ -t $(DESTDIR)$(PREFIX)/bin/ @install -Dm644 opensnitchd.service \ -t $(DESTDIR)/etc/systemd/system/ @install -Dm644 default-config.json \ -t $(DESTDIR)/etc/opensnitchd/ @install -Dm644 system-fw.json \ -t $(DESTDIR)/etc/opensnitchd/ @systemctl daemon-reload opensnitchd: $(SRC) @go get @go build -o opensnitchd . clean: @rm -rf opensnitchd ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/conman/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016405�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/conman/connection.go��������������������������������������������������������0000664�0000000�0000000�00000022220�15003540030�0021071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package conman import ( "errors" "fmt" "net" "os" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/dns" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/netfilter" "github.com/evilsocket/opensnitch/daemon/netlink" "github.com/evilsocket/opensnitch/daemon/netstat" "github.com/evilsocket/opensnitch/daemon/procmon" "github.com/evilsocket/opensnitch/daemon/procmon/audit" "github.com/evilsocket/opensnitch/daemon/procmon/ebpf" "github.com/evilsocket/opensnitch/daemon/ui/protocol" "github.com/google/gopacket/layers" ) // Connection represents an outgoing connection. type Connection struct { Entry *netstat.Entry Process *procmon.Process Pkt *netfilter.Packet Protocol string DstHost string SrcIP net.IP DstIP net.IP SrcPort uint DstPort uint } var showUnknownCons = false // Parse extracts the IP layers from a network packet to determine what // process generated a connection. func Parse(nfp netfilter.Packet, interceptUnknown bool) *Connection { showUnknownCons = interceptUnknown if nfp.IsIPv4() { con, err := NewConnection(&nfp) if err != nil { log.Debug("%s", err) return nil } else if con == nil { return nil } return con } if core.IPv6Enabled == false { return nil } con, err := NewConnection6(&nfp) if err != nil { log.Debug("%s", err) return nil } else if con == nil { return nil } return con } func newConnectionImpl(nfp *netfilter.Packet, c *Connection, protoType string) (cr *Connection, err error) { // no errors but not enough info neither if c.parseDirection(protoType) == false { return nil, nil } log.Debug("new connection %s => %d:%v -> %v (%s):%d uid: %d, mark: %x", c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstHost, c.DstPort, nfp.UID, nfp.Mark) c.Entry = &netstat.Entry{ Proto: c.Protocol, SrcIP: c.SrcIP, SrcPort: c.SrcPort, DstIP: c.DstIP, DstPort: c.DstPort, UserId: -1, INode: -1, } pid := -1 uid := -1 if procmon.MethodIsEbpf() { swap := false c.Process, swap, err = ebpf.GetPid(c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstPort) if swap { c.swapFields() } if c.Process != nil { c.Entry.UserId = c.Process.UID return c, nil } if err != nil { log.Debug("ebpf warning: %v", err) return nil, nil } } else if procmon.MethodIsAudit() { if aevent := audit.GetEventByPid(pid); aevent != nil { audit.Lock.RLock() c.Process = procmon.NewProcess(pid, aevent.ProcName) c.Process.Path = aevent.ProcPath c.Process.ReadCmdline() c.Process.CWD = aevent.ProcDir audit.Lock.RUnlock() // if the proc dir contains non alhpa-numeric chars the field is empty if c.Process.CWD == "" { c.Process.ReadCwd() } c.Process.ReadEnv() c.Process.CleanPath() procmon.AddToActivePidsCache(uint64(pid), c.Process) return c, nil } } // Sometimes when using eBPF, the PID is not found by the connection's parameters, // but falling back to legacy methods helps to find it and avoid "unknown/kernel pop-ups". // // One of the reasons is because after coming back from suspend state, for some reason (bug?), // gobpf/libbpf is unable to delete ebpf map entries, so when they reach the maximum capacity no // more entries are added, nor updated. if pid < 0 { // 0. lookup uid and inode via netlink. Can return several inodes. // 1. lookup uid and inode using /proc/net/(udp|tcp|udplite) // 2. lookup pid by inode // 3. if this is coming from us, just accept // 4. lookup process info by pid var inodeList []int uid, inodeList = netlink.GetSocketInfo(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort) if len(inodeList) == 0 { procmon.GetInodeFromNetstat(c.Entry, &inodeList, c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort) } for n, inode := range inodeList { pid = procmon.GetPIDFromINode(inode, fmt.Sprint(inode, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort)) if pid != -1 { log.Debug("[%d] PID found %d [%d]", n, pid, inode) c.Entry.INode = inode break } } } if pid == os.Getpid() { // return a Process object with our PID, to be able to exclude our own connections // (to the UI on a local socket for example) c.Process = procmon.NewProcess(pid, "") return c, nil } if nfp.UID != 0xffffffff { uid = int(nfp.UID) } c.Entry.UserId = uid if c.Process == nil { if c.Process = procmon.FindProcess(pid, showUnknownCons); c.Process == nil { return nil, fmt.Errorf("Could not find process by its pid %d for: %s", pid, c) } } return c, nil } // NewConnection creates a new Connection object, and returns the details of it. func NewConnection(nfp *netfilter.Packet) (c *Connection, err error) { ipv4 := nfp.Packet.Layer(layers.LayerTypeIPv4) if ipv4 == nil { return nil, errors.New("Error getting IPv4 layer") } ip, ok := ipv4.(*layers.IPv4) if !ok { return nil, errors.New("Error getting IPv4 layer data") } c = &Connection{ SrcIP: ip.SrcIP, DstIP: ip.DstIP, DstHost: dns.HostOr(ip.DstIP, ""), Pkt: nfp, } return newConnectionImpl(nfp, c, "") } // NewConnection6 creates a IPv6 new Connection object, and returns the details of it. func NewConnection6(nfp *netfilter.Packet) (c *Connection, err error) { ipv6 := nfp.Packet.Layer(layers.LayerTypeIPv6) if ipv6 == nil { return nil, errors.New("Error getting IPv6 layer") } ip, ok := ipv6.(*layers.IPv6) if !ok { return nil, errors.New("Error getting IPv6 layer data") } c = &Connection{ SrcIP: ip.SrcIP, DstIP: ip.DstIP, DstHost: dns.HostOr(ip.DstIP, ""), Pkt: nfp, } return newConnectionImpl(nfp, c, "6") } func (c *Connection) parseDirection(protoType string) bool { ret := false if tcpLayer := c.Pkt.Packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { if tcp, ok := tcpLayer.(*layers.TCP); ok == true && tcp != nil { c.Protocol = "tcp" + protoType c.DstPort = uint(tcp.DstPort) c.SrcPort = uint(tcp.SrcPort) ret = true if tcp.DstPort == 53 { c.getDomains(c.Pkt, c) } } } else if udpLayer := c.Pkt.Packet.Layer(layers.LayerTypeUDP); udpLayer != nil { if udp, ok := udpLayer.(*layers.UDP); ok == true && udp != nil { c.Protocol = "udp" + protoType c.DstPort = uint(udp.DstPort) c.SrcPort = uint(udp.SrcPort) ret = true if udp.DstPort == 53 { c.getDomains(c.Pkt, c) } } } else if udpliteLayer := c.Pkt.Packet.Layer(layers.LayerTypeUDPLite); udpliteLayer != nil { if udplite, ok := udpliteLayer.(*layers.UDPLite); ok == true && udplite != nil { c.Protocol = "udplite" + protoType c.DstPort = uint(udplite.DstPort) c.SrcPort = uint(udplite.SrcPort) ret = true } } else if sctpLayer := c.Pkt.Packet.Layer(layers.LayerTypeSCTP); sctpLayer != nil { if sctp, ok := sctpLayer.(*layers.SCTP); ok == true && sctp != nil { c.Protocol = "sctp" + protoType c.DstPort = uint(sctp.DstPort) c.SrcPort = uint(sctp.SrcPort) ret = true } } else if icmpLayer := c.Pkt.Packet.Layer(layers.LayerTypeICMPv4); icmpLayer != nil { if icmp, ok := icmpLayer.(*layers.ICMPv4); ok == true && icmp != nil { c.Protocol = "icmp" c.DstPort = 0 c.SrcPort = 0 ret = true } } else if icmp6Layer := c.Pkt.Packet.Layer(layers.LayerTypeICMPv6); icmp6Layer != nil { if icmp6, ok := icmp6Layer.(*layers.ICMPv6); ok == true && icmp6 != nil { c.Protocol = "icmp" + protoType c.DstPort = 0 c.SrcPort = 0 ret = true } } return ret } // swapFields swaps connection's fields. // Used to workaround an issue where outbound connections // have the fields swapped (procmon/ebpf/find.go). func (c *Connection) swapFields() { oEntry := c.Entry c.Entry = &netstat.Entry{ Proto: c.Protocol, SrcIP: oEntry.DstIP, DstIP: oEntry.SrcIP, SrcPort: oEntry.DstPort, DstPort: oEntry.SrcPort, UserId: oEntry.UserId, INode: oEntry.INode, } c.SrcIP = oEntry.DstIP c.DstIP = oEntry.SrcIP c.DstPort = oEntry.SrcPort c.SrcPort = oEntry.DstPort } func (c *Connection) getDomains(nfp *netfilter.Packet, con *Connection) { domains := dns.GetQuestions(nfp) if len(domains) < 1 { return } for _, dns := range domains { con.DstHost = dns } } // To returns the destination host of a connection. func (c *Connection) To() string { if c.DstHost == "" { return c.DstIP.String() } return fmt.Sprintf("%s (%s)", c.DstHost, c.DstIP) } func (c *Connection) String() string { if c.Entry == nil { return fmt.Sprintf("%d:%s ->(%s)-> %s:%d", c.SrcPort, c.SrcIP, c.Protocol, c.To(), c.DstPort) } if c.Process == nil { return fmt.Sprintf("%d:%s (uid:%d) ->(%s)-> %s:%d", c.SrcPort, c.SrcIP, c.Entry.UserId, c.Protocol, c.To(), c.DstPort) } return fmt.Sprintf("%s (%d) -> %s:%d (proto:%s uid:%d)", c.Process.Path, c.Process.ID, c.To(), c.DstPort, c.Protocol, c.Entry.UserId) } // Serialize returns a connection serialized. func (c *Connection) Serialize() *protocol.Connection { return &protocol.Connection{ Protocol: c.Protocol, SrcIp: c.SrcIP.String(), SrcPort: uint32(c.SrcPort), DstIp: c.DstIP.String(), DstHost: c.DstHost, DstPort: uint32(c.DstPort), UserId: uint32(c.Entry.UserId), ProcessId: uint32(c.Process.ID), ProcessPath: c.Process.Path, ProcessArgs: c.Process.Args, ProcessEnv: c.Process.Env, ProcessCwd: c.Process.CWD, } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/conman/connection_test.go���������������������������������������������������0000664�0000000�0000000�00000007042�15003540030�0022135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package conman import ( "fmt" "net" "testing" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/evilsocket/opensnitch/daemon/netfilter" ) // Adding new packets: // wireshark -> right click -> Copy as HexDump -> create []byte{} func NewTCPPacket() gopacket.Packet { // 47676:192.168.1.100 -> 1.1.1.1:23 testTCPPacket := []byte{0x4c, 0x6e, 0x6e, 0xd5, 0x79, 0xbf, 0x00, 0x28, 0x9d, 0x43, 0x7f, 0xd7, 0x08, 0x00, 0x45, 0x10, 0x00, 0x3c, 0x1d, 0x07, 0x40, 0x00, 0x40, 0x06, 0x59, 0x8e, 0xc0, 0xa8, 0x01, 0x6d, 0x01, 0x01, 0x01, 0x01, 0xba, 0x3c, 0x00, 0x17, 0x47, 0x7e, 0xf3, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0xfa, 0xf0, 0x4c, 0x27, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x91, 0xfb, 0xb5, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x0a} return gopacket.NewPacket(testTCPPacket, layers.LinkTypeEthernet, gopacket.Default) } func NewUDPPacket() gopacket.Packet { // 29517:192.168.1.109 -> 1.0.0.1:53 testUDPPacketDNS := []byte{ 0x4c, 0x6e, 0x6e, 0xd5, 0x79, 0xbf, 0x00, 0x28, 0x9d, 0x43, 0x7f, 0xd7, 0x08, 0x00, 0x45, 0x00, 0x00, 0x40, 0x54, 0x1a, 0x40, 0x00, 0x3f, 0x11, 0x24, 0x7d, 0xc0, 0xa8, 0x01, 0x6d, 0x01, 0x00, 0x00, 0x01, 0x73, 0x4d, 0x00, 0x35, 0x00, 0x2c, 0xf1, 0x17, 0x05, 0x51, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x70, 0x69, 0x04, 0x68, 0x6f, 0x6c, 0x65, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, } return gopacket.NewPacket(testUDPPacketDNS, layers.LinkTypeEthernet, gopacket.Default) } func EstablishConnection(proto, dst string) (net.Conn, error) { c, err := net.Dial(proto, dst) if err != nil { fmt.Println(err) return nil, err } return c, nil } func ListenOnPort(proto, port string) (net.Listener, error) { l, err := net.Listen(proto, port) if err != nil { fmt.Println(err) return nil, err } return l, nil } func NewPacket(pkt gopacket.Packet) *netfilter.Packet { return &netfilter.Packet{ Packet: pkt, UID: 666, NetworkProtocol: netfilter.IPv4, } } func NewDummyConnection(src, dst net.IP) *Connection { return &Connection{ SrcIP: src, DstIP: dst, } } // Test TCP parseDirection() func TestParseTCPDirection(t *testing.T) { srcIP := net.IP{192, 168, 1, 100} dstIP := net.IP{1, 1, 1, 1} c := NewDummyConnection(srcIP, dstIP) // 47676:192.168.1.100 -> 1.1.1.1:23 pkt := NewPacket(NewTCPPacket()) c.Pkt = pkt // parseDirection extracts the src and dst port from a network packet. if c.parseDirection("") == false { t.Error("parseDirection() should not be false") t.Fail() } if c.SrcPort != 47676 { t.Error("parseDirection() SrcPort mismatch:", c) t.Fail() } if c.DstPort != 23 { t.Error("parseDirection() DstPort mismatch:", c) t.Fail() } if c.Protocol != "tcp" { t.Error("parseDirection() Protocol mismatch:", c) t.Fail() } } // Test UDP parseDirection() func TestParseUDPDirection(t *testing.T) { srcIP := net.IP{192, 168, 1, 100} dstIP := net.IP{1, 0, 0, 1} c := NewDummyConnection(srcIP, dstIP) // 29517:192.168.1.109 -> 1.0.0.1:53 pkt := NewPacket(NewUDPPacket()) c.Pkt = pkt // parseDirection extracts the src and dst port from a network packet. if c.parseDirection("") == false { t.Error("parseDirection() should not be false") t.Fail() } if c.SrcPort != 29517 { t.Error("parseDirection() SrcPort mismatch:", c) t.Fail() } if c.DstPort != 53 { t.Error("parseDirection() DstPort mismatch:", c) t.Fail() } if c.Protocol != "udp" { t.Error("parseDirection() Protocol mismatch:", c) t.Fail() } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/core/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016062�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/core/core.go����������������������������������������������������������������0000664�0000000�0000000�00000003177�15003540030�0017351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package core import ( "fmt" "os" "os/exec" "os/user" "path/filepath" "strings" "time" ) const ( defaultTrimSet = "\r\n\t " ) // Trim remove trailing spaces from a string. func Trim(s string) string { return strings.Trim(s, defaultTrimSet) } // Exec spawns a new process and reurns the output. func Exec(executable string, args []string) (string, error) { path, err := exec.LookPath(executable) if err != nil { return "", err } raw, err := exec.Command(path, args...).CombinedOutput() if err != nil { return "", err } return Trim(string(raw)), nil } // Exists checks if a path exists. func Exists(path string) bool { if _, err := os.Stat(path); os.IsNotExist(err) { return false } return true } // ExpandPath replaces '~' shorthand with the user's home directory. func ExpandPath(path string) (string, error) { // Check if path is empty if path != "" { if strings.HasPrefix(path, "~") { usr, err := user.Current() if err != nil { return "", err } // Replace only the first occurrence of ~ path = strings.Replace(path, "~", usr.HomeDir, 1) } return filepath.Abs(path) } return "", nil } // IsAbsPath verifies if a path is absolute or not func IsAbsPath(path string) bool { return path[0] == 47 // 47 == '/' } // GetFileModTime checks if a file has been modified. func GetFileModTime(filepath string) (time.Time, error) { fi, err := os.Stat(filepath) if err != nil || fi.IsDir() { return time.Now(), fmt.Errorf("GetFileModTime() Invalid file") } return fi.ModTime(), nil } // ConcatStrings joins the provided strings. func ConcatStrings(args ...string) string { return strings.Join(args, "") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/core/ebpf.go����������������������������������������������������������������0000664�0000000�0000000�00000002534�15003540030�0017331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package core import ( "fmt" "github.com/evilsocket/opensnitch/daemon/log" "github.com/iovisor/gobpf/elf" ) // LoadEbpfModule loads the given eBPF module, from the given path if specified. // Otherwise t'll try to load the module from several default paths. func LoadEbpfModule(module, path string) (m *elf.Module, err error) { var ( modulesDir = "/opensnitchd/ebpf" paths = []string{ fmt.Sprint("/usr/local/lib", modulesDir), fmt.Sprint("/usr/lib", modulesDir), fmt.Sprint("/etc/opensnitchd"), // Deprecated: will be removed in future versions. } ) // if path has been specified, try to load the module from there. if path != "" { paths = []string{path} } modulePath := "" moduleError := fmt.Errorf(`Module not found (%s) in any of the paths. You may need to install the corresponding package`, module) for _, p := range paths { modulePath = fmt.Sprint(p, "/", module) log.Debug("[eBPF] trying to load %s", modulePath) if !Exists(modulePath) { continue } m = elf.NewModule(modulePath) if m.Load(nil) == nil { log.Info("[eBPF] module loaded: %s", modulePath) return m, nil } moduleError = fmt.Errorf(` unable to load eBPF module (%s). Your kernel version (%s) might not be compatible. If this error persists, change process monitor method to 'proc'`, module, GetKernelVersion()) } return m, moduleError } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/core/gzip.go����������������������������������������������������������������0000664�0000000�0000000�00000000635�15003540030�0017366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package core import ( "compress/gzip" "io/ioutil" "os" ) // ReadGzipFile reads a gzip to text. func ReadGzipFile(filename string) ([]byte, error) { fd, err := os.Open(filename) if err != nil { return nil, err } defer fd.Close() gz, err := gzip.NewReader(fd) if err != nil { return nil, err } defer gz.Close() s, err := ioutil.ReadAll(gz) if err != nil { return nil, err } return s, nil } ���������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/core/system.go��������������������������������������������������������������0000664�0000000�0000000�00000013112�15003540030�0017733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package core import ( "encoding/json" "fmt" "io" "io/ioutil" "regexp" "strings" "github.com/evilsocket/opensnitch/daemon/log" ) var ( // IPv6Enabled indicates if IPv6 protocol is enabled in the system IPv6Enabled = Exists("/proc/sys/net/ipv6") ) // GetHostname returns the name of the host where the daemon is running. func GetHostname() string { hostname, _ := ioutil.ReadFile("/proc/sys/kernel/hostname") return strings.Replace(string(hostname), "\n", "", -1) } // GetKernelVersion returns the kernel version. func GetKernelVersion() string { version, _ := ioutil.ReadFile("/proc/sys/kernel/osrelease") return strings.Replace(string(version), "\n", "", -1) } // CheckSysRequirements checks system features we need to work properly func CheckSysRequirements() { type checksT struct { RegExps []string Reason string } type ReqsList struct { Item string Checks checksT } kVer := GetKernelVersion() log.Raw("\n\t%sChecking system requirements for kernel version %s%s\n", log.FG_WHITE+log.BG_LBLUE, kVer, log.RESET) log.Raw("%s------------------------------------------------------------------------------%s\n\n", log.FG_WHITE+log.BG_LBLUE, log.RESET) confPaths := []string{ fmt.Sprint("/boot/config-", kVer), "/proc/config.gz", // Fedora SilverBlue fmt.Sprint("/usr/lib/modules/", kVer, "/config"), } var fileContent []byte var err error for _, confFile := range confPaths { if !Exists(confFile) { err = fmt.Errorf("%s not found", confFile) log.Debug(err.Error()) continue } if confFile[len(confFile)-2:] == "gz" { fileContent, err = ReadGzipFile(confFile) } else { fileContent, err = ioutil.ReadFile(confFile) } if err == nil { break } } if err != nil { fmt.Printf("\n\t%s kernel config not found (%s) in any of the expected paths.\n", log.Bold(log.Red("✘")), kVer) fmt.Printf("\tPlease, open a new issue on github specifying your kernel and distro version (/etc/os-release).\n\n") return } // TODO: check loaded/configured modules (nfnetlink, nfnetlink_queue, xt_NFQUEUE, etc) // Other items to check: // CONFIG_NETFILTER_NETLINK // CONFIG_NETFILTER_NETLINK_QUEUE const reqsList = ` [ { "Item": "kprobes", "Checks": { "Regexps": [ "CONFIG_KPROBES=y", "CONFIG_KPROBES_ON_FTRACE=y", "CONFIG_HAVE_KPROBES=y", "CONFIG_HAVE_KPROBES_ON_FTRACE=y", "CONFIG_KPROBE_EVENTS=y" ], "Reason": " - KPROBES not fully supported by this kernel." } }, { "Item": "uprobes", "Checks": { "Regexps": [ "CONFIG_UPROBES=y", "CONFIG_UPROBE_EVENTS=y" ], "Reason": " * UPROBES not supported. Common error => cannot open uprobe_events: open /sys/kernel/debug/tracing/uprobe_events" } }, { "Item": "ftrace", "Checks": { "Regexps": [ "CONFIG_FTRACE=y" ], "Reason": " - CONFIG_TRACE=y not set. Common error => Error while loading kprobes: invalid argument." } }, { "Item": "syscalls", "Checks": { "Regexps": [ "CONFIG_HAVE_SYSCALL_TRACEPOINTS=y", "CONFIG_FTRACE_SYSCALLS=y" ], "Reason": " - CONFIG_FTRACE_SYSCALLS or CONFIG_HAVE_SYSCALL_TRACEPOINTS not set. Common error => error enabling tracepoint tracepoint/syscalls/sys_enter_execve: cannot read tracepoint id" } }, { "Item": "nfqueue", "Checks": { "Regexps": [ "CONFIG_NETFILTER_NETLINK_QUEUE=[my]", "CONFIG_NFT_QUEUE=[my]", "CONFIG_NETFILTER_XT_TARGET_NFQUEUE=[my]" ], "Reason": " * NFQUEUE netfilter extensions not supported by this kernel (CONFIG_NETFILTER_NETLINK_QUEUE, CONFIG_NFT_QUEUE, CONFIG_NETFILTER_XT_TARGET_NFQUEUE)." } }, { "Item": "netlink", "Checks": { "Regexps": [ "CONFIG_NETFILTER_NETLINK=[my]", "CONFIG_NETFILTER_NETLINK_QUEUE=[my]", "CONFIG_NETFILTER_NETLINK_ACCT=[my]" ], "Reason": " * NETLINK extensions not supported by this kernel (CONFIG_NETFILTER_NETLINK, CONFIG_NETFILTER_NETLINK_QUEUE, CONFIG_NETFILTER_NETLINK_ACCT)." } }, { "Item": "net diagnostics", "Checks": { "Regexps": [ "CONFIG_INET_DIAG=[my]", "CONFIG_INET_TCP_DIAG=[my]", "CONFIG_INET_UDP_DIAG=[my]", "CONFIG_INET_DIAG_DESTROY=[my]" ], "Reason": " * One or more socket monitoring interfaces are not enabled (CONFIG_INET_DIAG, CONFIG_INET_TCP_DIAG, CONFIG_INET_UDP_DIAG, CONFIG_DIAG_DESTROY (Reject feature))." } } ] ` reqsFullfiled := true dec := json.NewDecoder(strings.NewReader(reqsList)) for { var reqs []ReqsList if err := dec.Decode(&reqs); err == io.EOF { break } else if err != nil { log.Error("%s", err) break } for _, req := range reqs { checkOk := true for _, trex := range req.Checks.RegExps { fmt.Printf("\tChecking => %s\n", trex) re, err := regexp.Compile(trex) if err != nil { fmt.Printf("\t%s %s\n", log.Bold(log.Red("Invalid regexp =>")), log.Red(trex)) continue } if re.Find(fileContent) == nil { fmt.Printf("\t%s\n", log.Red(req.Checks.Reason)) checkOk = false } } if checkOk { fmt.Printf("\n\t* %s\t %s\n", log.Bold(log.Green(req.Item)), log.Bold(log.Green("✔"))) } else { reqsFullfiled = false fmt.Printf("\n\t* %s\t %s\n", log.Bold(log.Red(req.Item)), log.Bold(log.Red("✘"))) } fmt.Println() } } if !reqsFullfiled { log.Raw("\n%sWARNING:%s Your kernel doesn't support some of the features OpenSnitch needs:\nRead more: https://github.com/evilsocket/opensnitch/issues/774\n", log.FG_WHITE+log.BG_YELLOW, log.RESET) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/core/version.go�������������������������������������������������������������0000664�0000000�0000000�00000000310�15003540030�0020070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package core // version related consts const ( Name = "opensnitch-daemon" Version = "1.6.9" Author = "Simone 'evilsocket' Margaritelli" Website = "https://github.com/evilsocket/opensnitch" ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/data/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016043�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/data/rules/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017175�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/data/rules/000-allow-localhost.json�����������������������������������������0000664�0000000�0000000�00000001030�15003540030�0023463�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2023-07-05T10:46:47.904024069+01:00", "updated": "2023-07-05T10:46:47.921828104+01:00", "name": "000-allow-localhost", "description": "Allow connections to localhost. See this link for more information:\nhttps://github.com/evilsocket/opensnitch/wiki/Rules#localhost-connections", "enabled": true, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "regexp", "operand": "dest.ip", "sensitive": false, "data": "^(127\\.0\\.0\\.1|::1)$", "list": [] } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/default-config.json���������������������������������������������������������0000664�0000000�0000000�00000001077�15003540030�0020721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "Server": { "Address":"unix:///tmp/osui.sock", "LogFile":"/var/log/opensnitchd.log" }, "DefaultAction": "allow", "DefaultDuration": "once", "InterceptUnknown": false, "ProcMonitorMethod": "ebpf", "LogLevel": 2, "LogUTC": true, "LogMicro": false, "Firewall": "nftables", "Rules": { "Path": "/etc/opensnitchd/rules/" }, "Stats": { "MaxEvents": 150, "MaxStats": 25, "Workers": 6 }, "Internal": { "GCPercent": 100, "FlushConnsOnStart": true } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/dns/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015716�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/dns/ebpfhook.go�������������������������������������������������������������0000664�0000000�0000000�00000011717�15003540030�0020051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package dns import ( "bytes" "debug/elf" "encoding/binary" "errors" "fmt" "net" "os" "os/signal" "strings" "syscall" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" bpf "github.com/iovisor/gobpf/elf" ) /* #cgo LDFLAGS: -ldl #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <link.h> #include <dlfcn.h> #include <string.h> char* find_libc() { void *handle; struct link_map * map; handle = dlopen(NULL, RTLD_NOW); if (handle == NULL) { fprintf(stderr, "EBPF-DNS dlopen() failed: %s\n", dlerror()); return NULL; } if (dlinfo(handle, RTLD_DI_LINKMAP, &map) == -1) { fprintf(stderr, "EBPF-DNS: dlinfo failed: %s\n", dlerror()); return NULL; } while(1){ if(map == NULL){ break; } if(strstr(map->l_name, "libc.so")){ fprintf(stderr,"found %s\n", map->l_name); return map->l_name; } map = map->l_next; } return NULL; } */ import "C" type nameLookupEvent struct { AddrType uint32 IP [16]uint8 Host [252]byte } func findLibc() (string, error) { ret := C.find_libc() if ret == nil { return "", errors.New("Could not find path to libc.so") } str := C.GoString(ret) return str, nil } // Iterates over all symbols in an elf file and returns the offset matching the provided symbol name. func lookupSymbol(elffile *elf.File, symbolName string) (uint64, error) { symbols, err := elffile.DynamicSymbols() if err != nil { return 0, err } for _, symb := range symbols { if symb.Name == symbolName { return symb.Value, nil } } return 0, fmt.Errorf("Symbol: '%s' not found", symbolName) } // ListenerEbpf starts listening for DNS events. func ListenerEbpf(ebpfModPath string) error { m, err := core.LoadEbpfModule("opensnitch-dns.o", ebpfModPath) if err != nil { log.Error("[eBPF DNS]: %s", err) return err } defer m.Close() // libbcc resolves the offsets for us. without bcc the offset for uprobes must parsed from the elf files // some how 0 must be replaced with the offset of getaddrinfo bcc does this using bcc_resolve_symname // Attaching to uprobe using perf open might be a better aproach requires https://github.com/iovisor/gobpf/pull/277 libcFile, err := findLibc() if err != nil { log.Error("EBPF-DNS: Failed to find libc.so: %v", err) return err } libcElf, err := elf.Open(libcFile) if err != nil { log.Error("EBPF-DNS: Failed to open %s: %v", libcFile, err) return err } probesAttached := 0 for uprobe := range m.IterUprobes() { probeFunction := strings.Replace(uprobe.Name, "uretprobe/", "", 1) probeFunction = strings.Replace(probeFunction, "uprobe/", "", 1) offset, err := lookupSymbol(libcElf, probeFunction) if err != nil { log.Warning("EBPF-DNS: Failed to find symbol for uprobe %s (offset: %d): %s\n", uprobe.Name, offset, err) continue } err = bpf.AttachUprobe(uprobe, libcFile, offset) if err != nil { log.Warning("EBPF-DNS: Failed to attach uprobe %s : %s, (%s, %d)\n", uprobe.Name, err, libcFile, offset) continue } probesAttached++ } if probesAttached == 0 { log.Warning("EBPF-DNS: Failed to find symbols for uprobes.") return errors.New("Failed to find symbols for uprobes") } // Reading Events channel := make(chan []byte) //log.Warning("EBPF-DNS: %+v\n", m) perfMap, err := bpf.InitPerfMap(m, "events", channel, nil) if err != nil { log.Error("EBPF-DNS: Failed to init perf map: %s\n", err) return err } sig := make(chan os.Signal, 1) exitChannel := make(chan bool) signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGQUIT) for i := 0; i < 5; i++ { go spawnDNSWorker(i, channel, exitChannel) } perfMap.PollStart() <-sig log.Info("EBPF-DNS: Received signal: terminating ebpf dns hook.") perfMap.PollStop() for i := 0; i < 5; i++ { exitChannel <- true } return nil } func spawnDNSWorker(id int, channel chan []byte, exitChannel chan bool) { log.Debug("dns worker initialized #%d", id) var event nameLookupEvent for { select { case <-time.After(1 * time.Millisecond): continue case <-exitChannel: goto Exit default: data := <-channel if len(data) > 0 { log.Debug("(%d) EBPF-DNS: LookupEvent %d %x %x %x", id, len(data), data[:4], data[4:20], data[20:]) } err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event) if err != nil { log.Warning("(%d) EBPF-DNS: Failed to decode ebpf nameLookupEvent: %s\n", id, err) continue } // Convert C string (null-terminated) to Go string host := string(event.Host[:bytes.IndexByte(event.Host[:], 0)]) var ip net.IP // 2 -> AF_INET (ipv4) if event.AddrType == 2 { ip = net.IP(event.IP[:4]) } else { ip = net.IP(event.IP[:]) } log.Debug("(%d) EBPF-DNS: Tracking Resolved Message: %s -> %s\n", id, host, ip.String()) Track(ip.String(), host) } } Exit: log.Debug("DNS worker #%d closed", id) } �������������������������������������������������opensnitch-1.6.9/daemon/dns/parse.go����������������������������������������������������������������0000664�0000000�0000000�00000000777�15003540030�0017372�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package dns import ( "github.com/evilsocket/opensnitch/daemon/netfilter" "github.com/google/gopacket/layers" ) // GetQuestions retrieves the domain names a process is trying to resolve. func GetQuestions(nfp *netfilter.Packet) (questions []string) { dnsLayer := nfp.Packet.Layer(layers.LayerTypeDNS) if dnsLayer == nil { return questions } dns, _ := dnsLayer.(*layers.DNS) for _, dnsQuestion := range dns.Questions { questions = append(questions, string(dnsQuestion.Name)) } return questions } �opensnitch-1.6.9/daemon/dns/systemd/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017406�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/dns/systemd/monitor.go������������������������������������������������������0000664�0000000�0000000�00000014567�15003540030�0021441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Package systemd defines several utilities to interact with systemd. // // ResolvedMonitor: // * To debug systemd-resolved queries and inspect the protocol: // - resolvectl monitor // * Resources: // - https://github.com/systemd/systemd/blob/main/src/resolve/resolvectl.c // - The protocol used to send and receive data is varlink: // https://github.com/varlink/go // https://github.com/systemd/systemd/blob/main/src/resolve/resolved-varlink.c // - https://systemd.io/RESOLVED-VPNS/ package systemd import ( "context" "errors" "fmt" "sync" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" "github.com/varlink/go/varlink" ) // whenever there's a new DNS response, this callback will be invoked. // the second parameter is a MonitorResponse struct that will be filled with // data. type resolvedCallback func(context.Context, interface{}) (uint64, error) const ( // SuccessState is the string returned by systemd-resolved when a DNS query is successful. // Other states: https://github.com/systemd/systemd/blob/main/src/resolve/resolved-dns-transaction.c#L3608 SuccessState = "success" socketPath = "/run/systemd/resolve/io.systemd.Resolve.Monitor" resolvedSubscribeMethod = "io.systemd.Resolve.Monitor.SubscribeQueryResults" // DNSTypeA A DNSTypeA = 1 // DNSTypeAAAA AAAA DNSTypeAAAA = 28 // DNSTypeCNAME cname DNSTypeCNAME = 5 ) // QuestionMonitorResponse represents a DNS query // "question": [{"class": 1, "type": 28,"name": "images.site.com"}], type QuestionMonitorResponse struct { Name string `json:"name"` Class int `json:"class"` Type int `json:"type"` } // KeyType holds question that generated the answer /*answer: [{ "rr": { "key": { "class": 1, "type": 28, "name": "images.site.com" }, "address": [100, 13, 45, 111] }, "raw": "DFJFKE343443EFKEREKET=", "ifindex": 3 }]*/ type KeyType struct { Name string `json:"name"` Class int `json:"class"` Type int `json:"type"` } // RRType represents a DNS answer // if the response is a CNAME, Address will be nil, and Name a domain name. type RRType struct { Key QuestionMonitorResponse `json:"key"` Address []byte `json:"address"` Name string `json:"name"` } // AnswerMonitorResponse represents the DNS answer of a DNS query. type AnswerMonitorResponse struct { RR RRType `json:"rr"` Raw string `json:"raw"` Ifindex int `json:"ifindex"` } // MonitorResponse represents the systemd-resolved protocol message // sent over the wire, that holds the answer to a DNS query. type MonitorResponse struct { State string `json:"state"` Question []QuestionMonitorResponse `json:"question"` // CollectedQuestions // "collectedQuestions":[{"class":1,"type":1,"name":"translate.google.com"}] Answer []AnswerMonitorResponse `json:"answer"` Continues bool `json:"continues"` } // ResolvedMonitor represents a systemd-resolved monitor type ResolvedMonitor struct { mu *sync.RWMutex Ctx context.Context Cancel context.CancelFunc // connection with the systemd-resolved unix socket: // /run/systemd/resolve/io.systemd.Resolve.Monitor Conn *varlink.Connection // channel where all the DNS respones will be sent ChanResponse chan *MonitorResponse // error channel to signal any problem ChanConnError chan error // callback that is emited when systemd-resolved resolves a domain name. receiverCb resolvedCallback connected bool } // NewResolvedMonitor returns a new ResolvedMonitor object. // With this object you can passively read DNS answers. func NewResolvedMonitor() (*ResolvedMonitor, error) { if core.Exists(socketPath) == false { return nil, fmt.Errorf("%s doesn't exist", socketPath) } ctx, cancel := context.WithCancel(context.Background()) return &ResolvedMonitor{ mu: &sync.RWMutex{}, Ctx: ctx, Cancel: cancel, ChanResponse: make(chan *MonitorResponse), ChanConnError: make(chan error), }, nil } // Connect opens a unix socket with systemd-resolved func (r *ResolvedMonitor) Connect() (*varlink.Connection, error) { r.mu.Lock() defer r.mu.Unlock() var err error r.Conn, err = varlink.NewConnection(r.Ctx, fmt.Sprintf("unix://%s", socketPath)) if err != nil { return nil, err } r.connected = true go r.connPoller() return r.Conn, nil } // if we're connected to the unix socket, check every few seconds if we're still // connected, and if not, reconnect, to survive to systemd-resolved restarts. func (r *ResolvedMonitor) connPoller() { for { select { case <-time.After(5 * time.Second): if r.isConnected() { continue } log.Debug("ResolvedMonitor not connected") if _, err := r.Connect(); err == nil { r.Subscribe() } goto Exit } } Exit: log.Debug("ResolvedMonitor connection poller exit.") } // Subscribe sends the instruction to systemd-resolved to start monitoring // DNS answers. func (r *ResolvedMonitor) Subscribe() error { if r.isConnected() == false { return errors.New("Not connected") } var err error type emptyT struct{} empty := &emptyT{} r.receiverCb, err = r.Conn.Send(r.Ctx, resolvedSubscribeMethod, empty, varlink.Continues|varlink.More) if err != nil { return err } go r.monitor(r.Ctx, r.ChanResponse, r.ChanConnError, r.receiverCb) return nil } // monitor will listen for DNS answers from systemd-resolved. func (r *ResolvedMonitor) monitor(ctx context.Context, chanResponse chan *MonitorResponse, chanConnError chan error, callback resolvedCallback) { for { m := &MonitorResponse{} continues, err := callback(ctx, m) if err != nil { chanConnError <- err goto Exit } if continues != varlink.Continues { goto Exit } log.Debug("ResolvedMonitor >> new response: %#v", m) chanResponse <- m } Exit: r.mu.Lock() r.connected = false r.mu.Unlock() log.Debug("ResolvedMonitor.monitor() exit.") } // GetDNSResponses returns a channel that you can use to read responses. func (r *ResolvedMonitor) GetDNSResponses() chan *MonitorResponse { return r.ChanResponse } // Exit returns a channel to listen for connection errors. func (r *ResolvedMonitor) Exit() chan error { return r.ChanConnError } // Close closes the unix socket with systemd-resolved func (r *ResolvedMonitor) Close() { r.ChanConnError <- nil r.Cancel() } func (r *ResolvedMonitor) isConnected() bool { r.mu.RLock() defer r.mu.RUnlock() return r.connected } �����������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/dns/track.go����������������������������������������������������������������0000664�0000000�0000000�00000004133�15003540030�0017352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package dns import ( "net" "sync" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) var ( responses = make(map[string]string, 0) lock = sync.RWMutex{} ) // TrackAnswers obtains the resolved domains of a DNS query. // If the packet is UDP DNS, the domain names are added to the list of resolved domains. func TrackAnswers(packet gopacket.Packet) bool { udpLayer := packet.Layer(layers.LayerTypeUDP) if udpLayer == nil { return false } udp, ok := udpLayer.(*layers.UDP) if ok == false || udp == nil { return false } if udp.SrcPort != 53 { return false } dnsLayer := packet.Layer(layers.LayerTypeDNS) if dnsLayer == nil { return false } dnsAns, ok := dnsLayer.(*layers.DNS) if ok == false || dnsAns == nil { return false } for _, ans := range dnsAns.Answers { if ans.Name != nil { if ans.IP != nil { Track(ans.IP.String(), string(ans.Name)) } else if ans.CNAME != nil { Track(string(ans.CNAME), string(ans.Name)) } } } return true } // Track adds a resolved domain to the list. func Track(resolved string, hostname string) { lock.Lock() defer lock.Unlock() if len(resolved) > 3 && resolved[0:4] == "127." { return } if resolved == "::1" || resolved == hostname { return } responses[resolved] = hostname log.Debug("New DNS record: %s -> %s", resolved, hostname) } // Host returns if a resolved domain is in the list. func Host(resolved string) (host string, found bool) { lock.RLock() defer lock.RUnlock() host, found = responses[resolved] return } // HostOr checks if an IP has a domain name already resolved. // If the domain is in the list it's returned, otherwise the IP will be returned. func HostOr(ip net.IP, or string) string { if host, found := Host(ip.String()); found == true { // host might have been CNAME; go back until we reach the "root" seen := make(map[string]bool) // prevent possibility of loops for { orig, had := Host(host) if seen[orig] { break } if !had { break } seen[orig] = true host = orig } return host } return or } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016737�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/common/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020227�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/common/common.go���������������������������������������������������0000664�0000000�0000000�00000007322�15003540030�0022052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package common import ( "sync" "time" "github.com/evilsocket/opensnitch/daemon/log" ) // default arguments for various functions var ( EnableRule = true DoLogErrors = true ForcedDelRules = true ReloadRules = true RestoreChains = true BackupChains = true ReloadConf = true ) type ( callback func() callbackBool func() bool // Common holds common fields and functionality of both firewalls, // iptables and nftables. Common struct { RulesChecker *time.Ticker ErrChan chan string QueueNum uint16 stopChecker chan bool Running bool Intercepting bool FwEnabled bool sync.RWMutex } ) // ErrorsChan returns the channel where the errors are sent to. func (c *Common) ErrorsChan() <-chan string { return c.ErrChan } // ErrChanEmpty checks if the errors channel is empty. func (c *Common) ErrChanEmpty() bool { return len(c.ErrChan) == 0 } // SendError sends an error to the channel of errors. func (c *Common) SendError(err string) { log.Warning("%s", err) if len(c.ErrChan) >= cap(c.ErrChan) { log.Debug("fw errors channel full, emptying errChan") for e := range c.ErrChan { log.Warning("%s", e) if c.ErrChanEmpty() { break } } return } select { case c.ErrChan <- err: case <-time.After(100 * time.Millisecond): log.Warning("SendError() channel locked? REVIEW") } } // SetQueueNum sets the queue number used by the firewall. // It's the queue where all intercepted connections will be sent. func (c *Common) SetQueueNum(qNum *int) { c.Lock() defer c.Unlock() if qNum != nil { c.QueueNum = uint16(*qNum) } } // IsRunning returns if the firewall is running or not. func (c *Common) IsRunning() bool { c.RLock() defer c.RUnlock() return c != nil && c.Running } // IsFirewallEnabled returns if the firewall is running or not. func (c *Common) IsFirewallEnabled() bool { c.RLock() defer c.RUnlock() return c != nil && c.FwEnabled } // IsIntercepting returns if the firewall is running or not. func (c *Common) IsIntercepting() bool { c.RLock() defer c.RUnlock() return c != nil && c.Intercepting } // NewRulesChecker starts monitoring interception rules. // We expect to have 2 rules loaded: one to intercept DNS responses and another one // to intercept network traffic. func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callback) { c.Lock() defer c.Unlock() if c.RulesChecker != nil { c.RulesChecker.Stop() select { case c.stopChecker <- true: case <-time.After(5 * time.Millisecond): log.Error("NewRulesChecker: timed out stopping monitor rules") } } c.stopChecker = make(chan bool, 1) c.RulesChecker = time.NewTicker(time.Second * 10) go startCheckingRules(c.stopChecker, c.RulesChecker, areRulesLoaded, reloadRules) } // StartCheckingRules monitors if our rules are loaded. // If the rules to intercept traffic are not loaded, we'll try to insert them again. func startCheckingRules(exitChan <-chan bool, rulesChecker *time.Ticker, areRulesLoaded callbackBool, reloadRules callback) { for { select { case <-exitChan: goto Exit case _, active := <-rulesChecker.C: if !active { goto Exit } if areRulesLoaded() == false { reloadRules() } } } Exit: log.Info("exit checking firewall rules") } // StopCheckingRules stops checking if firewall rules are loaded. func (c *Common) StopCheckingRules() { c.Lock() defer c.Unlock() if c.RulesChecker != nil { select { case c.stopChecker <- true: close(c.stopChecker) case <-time.After(5 * time.Millisecond): // We should not arrive here log.Error("StopCheckingRules: timed out stopping monitor rules") } c.RulesChecker.Stop() c.RulesChecker = nil } } func (c *Common) reloadCallback(callback func()) { callback() } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/config/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020204�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/config/config.go���������������������������������������������������0000664�0000000�0000000�00000014505�15003540030�0022005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Package config provides functionality to load and monitor the system // firewall rules. // It's inherited by the different firewall packages (iptables, nftables). // // The firewall rules defined by the user are reloaded in these cases: // - When the file system-fw.json changes. // - When the firewall rules are not present when listing them. package config import ( "encoding/json" "io/ioutil" "os" "sync" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/log" "github.com/fsnotify/fsnotify" ) // ExprValues holds the statements' options: // "Name": "ct", // "Values": [ // { // "Key": "state", // "Value": "established" // }, // { // "Key": "state", // "Value": "related" // }] type ExprValues struct { Key string Value string } // ExprStatement holds the definition of matches to use against connections. //{ // "Op": "!=", // "Name": "tcp", // "Values": [ // { // "Key": "dport", // "Value": "443" // } // ] //} type ExprStatement struct { Op string // ==, !=, ... Only one per expression set. Name string // tcp, udp, ct, daddr, log, ... Values []*ExprValues // dport 8000 } // Expressions holds the array of expressions that create the rules type Expressions struct { Statement *ExprStatement } // FwRule holds the fields of a rule type FwRule struct { *sync.RWMutex // we need to keep old fields in the struct. Otherwise when receiving a conf from the GUI, the legacy rules would be deleted. Chain string // TODO: deprecated, remove Table string // TODO: deprecated, remove Parameters string // TODO: deprecated, remove UUID string Description string Target string TargetParameters string Expressions []*Expressions Position uint64 `json:",string"` Enabled bool } // FwChain holds the information that defines a firewall chain. // It also contains the firewall table definition that it belongs to. type FwChain struct { // table fields Table string Family string // chain fields Name string Description string Priority string Type string Hook string Policy string Rules []*FwRule } // IsInvalid checks if the chain has been correctly configured. func (fc *FwChain) IsInvalid() bool { return fc.Name == "" || fc.Family == "" || fc.Table == "" } type rulesList struct { Rule *FwRule } type chainsList struct { Rule *FwRule // TODO: deprecated, remove Chains []*FwChain } // SystemConfig holds the list of rules to be added to the system type SystemConfig struct { SystemRules []*chainsList sync.RWMutex Version uint32 Enabled bool } // Config holds the functionality to re/load the firewall configuration from disk. // This is the configuration to manage the system firewall (iptables, nftables). type Config struct { watcher *fsnotify.Watcher monitorExitChan chan bool // preload will be called after daemon startup, whilst reload when a modification is performed. preloadCallback func() // reloadCallback is called after the configuration is written. reloadCallback func() file string SysConfig SystemConfig sync.Mutex } // NewSystemFwConfig initializes config fields func (c *Config) NewSystemFwConfig(preLoadCb, reLoadCb func()) (*Config, error) { var err error watcher, err := fsnotify.NewWatcher() if err != nil { log.Warning("Error creating firewall config watcher: %s", err) return nil, err } c.Lock() defer c.Unlock() c.file = "/etc/opensnitchd/system-fw.json" c.monitorExitChan = make(chan bool, 1) c.preloadCallback = preLoadCb c.reloadCallback = reLoadCb c.watcher = watcher return c, nil } func (c *Config) SetFile(file string) { c.file = file } // LoadDiskConfiguration reads and loads the firewall configuration from disk func (c *Config) LoadDiskConfiguration(reload bool) error { c.Lock() defer c.Unlock() raw, err := ioutil.ReadFile(c.file) if err != nil { log.Error("Error reading firewall configuration from disk %s: %s", c.file, err) return err } if err = c.loadConfiguration(raw); err != nil { return err } // we need to monitor the configuration file for changes, regardless if it's // malformed or not. c.watcher.Remove(c.file) if err := c.watcher.Add(c.file); err != nil { log.Error("Could not watch firewall configuration: %s", err) return err } if reload { c.reloadCallback() return nil } go c.monitorConfigWorker() return nil } // loadConfigutation reads the system firewall rules from disk. // Then the rules are added based on the configuration defined. func (c *Config) loadConfiguration(rawConfig []byte) error { c.SysConfig.Lock() defer c.SysConfig.Unlock() // delete old system rules, that may be different from the new ones c.preloadCallback() if err := json.Unmarshal(rawConfig, &c.SysConfig); err != nil { // we only log the parser error, giving the user a chance to write a valid config log.Error("Error parsing firewall configuration %s: %s", c.file, err) return err } log.Info("fw configuration loaded") return nil } // SaveConfiguration saves configuration to disk. // This event dispatches a reload of the configuration. func (c *Config) SaveConfiguration(rawConfig string) error { conf, err := json.MarshalIndent([]byte(rawConfig), " ", " ") if err != nil { log.Error("saving json firewall configuration: %s %s", err, conf) return err } if err = os.Chmod(c.file, 0600); err != nil { log.Warning("unable to set system-fw.json permissions: %s", err) } if err = ioutil.WriteFile(c.file, []byte(rawConfig), 0600); err != nil { log.Error("writing firewall configuration to disk: %s", err) return err } return nil } // StopConfigWatcher stops the configuration watcher and stops the subroutine. func (c *Config) StopConfigWatcher() { c.Lock() defer c.Unlock() if c.monitorExitChan != nil { c.monitorExitChan <- true close(c.monitorExitChan) } if c.watcher != nil { c.watcher.Remove(c.file) c.watcher.Close() } } func (c *Config) monitorConfigWorker() { for { select { case <-c.monitorExitChan: goto Exit case event := <-c.watcher.Events: if (event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Remove == fsnotify.Remove) { c.LoadDiskConfiguration(common.ReloadConf) } } } Exit: log.Debug("stop monitoring firewall config file") c.Lock() c.monitorExitChan = nil c.Unlock() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/iptables/����������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020542�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/iptables/iptables.go�����������������������������������������������0000664�0000000�0000000�00000012361�15003540030�0022677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iptables import ( "bytes" "encoding/json" "os/exec" "regexp" "strings" "sync" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/ui/protocol" "github.com/golang/protobuf/jsonpb" ) // Action is the modifier we apply to a rule. type Action string const ( // Name is the name that identifies this firewall Name = "iptables" // SystemRulePrefix prefix added to each system rule SystemRulePrefix = "opensnitch-filter" ) // Actions we apply to the firewall. const ( ADD = Action("-A") INSERT = Action("-I") DELETE = Action("-D") FLUSH = Action("-F") NEWCHAIN = Action("-N") DELCHAIN = Action("-X") POLICY = Action("-P") DROP = Action("DROP") ACCEPT = Action("ACCEPT") ) // SystemRule blabla type SystemRule struct { Rule *config.FwRule Table string Chain string } // SystemChains keeps track of the fw rules that have been added to the system. type SystemChains struct { Rules map[string]*SystemRule sync.RWMutex } // Iptables struct holds the fields of the iptables fw type Iptables struct { regexRulesQuery *regexp.Regexp regexSystemRulesQuery *regexp.Regexp bin string bin6 string chains SystemChains common.Common config.Config sync.Mutex } // Fw initializes a new Iptables object func Fw() (*Iptables, error) { if err := IsAvailable(); err != nil { return nil, err } reRulesQuery, _ := regexp.Compile(`NFQUEUE.*ctstate NEW,RELATED.*NFQUEUE num.*bypass`) reSystemRulesQuery, _ := regexp.Compile(SystemRulePrefix + ".*") ipt := &Iptables{ bin: "iptables", bin6: "ip6tables", regexRulesQuery: reRulesQuery, regexSystemRulesQuery: reSystemRulesQuery, chains: SystemChains{ Rules: make(map[string]*SystemRule), }, } return ipt, nil } // Name returns the firewall name func (ipt *Iptables) Name() string { return Name } // Init inserts the firewall rules and starts monitoring for firewall // changes. func (ipt *Iptables) Init(qNum *int) { if ipt.IsRunning() { return } ipt.SetQueueNum(qNum) ipt.ErrChan = make(chan string, 100) // In order to clean up any existing firewall rule before start, // we need to load the fw configuration first to know what rules // were configured. ipt.NewSystemFwConfig(ipt.preloadConfCallback, ipt.reloadRulesCallback) ipt.LoadDiskConfiguration(!common.ReloadConf) // start from a clean state ipt.CleanRules(false) ipt.EnableInterception() ipt.AddSystemRules(!common.ReloadRules, common.BackupChains) ipt.Running = true } // Stop deletes the firewall rules, allowing network traffic. func (ipt *Iptables) Stop() { if ipt.Running == false { return } ipt.StopConfigWatcher() ipt.StopCheckingRules() ipt.CleanRules(log.GetLogLevel() == log.DEBUG) ipt.Running = false } // IsAvailable checks if iptables is installed in the system. // If it's not, we'll default to nftables. func IsAvailable() error { _, err := exec.Command("iptables", []string{"-V"}...).CombinedOutput() if err != nil { return err } return nil } // EnableInterception adds fw rules to intercept connections. func (ipt *Iptables) EnableInterception() { if err4, err6 := ipt.QueueConnections(common.EnableRule, true); err4 != nil || err6 != nil { log.Fatal("Error while running conntrack firewall rule: %s %s", err4, err6) } else if err4, err6 = ipt.QueueDNSResponses(common.EnableRule, true); err4 != nil || err6 != nil { log.Error("Error while running DNS firewall rule: %s %s", err4, err6) } // start monitoring firewall rules to intercept network traffic ipt.NewRulesChecker(ipt.AreRulesLoaded, ipt.reloadRulesCallback) } // DisableInterception removes firewall rules to intercept outbound connections. func (ipt *Iptables) DisableInterception(logErrors bool) { ipt.StopCheckingRules() ipt.QueueDNSResponses(!common.EnableRule, logErrors) ipt.QueueConnections(!common.EnableRule, logErrors) } // CleanRules deletes the rules we added. func (ipt *Iptables) CleanRules(logErrors bool) { ipt.DisableInterception(logErrors) ipt.DeleteSystemRules(common.ForcedDelRules, common.BackupChains, logErrors) } // Serialize converts the configuration from json to protobuf func (ipt *Iptables) Serialize() (*protocol.SysFirewall, error) { sysfw := &protocol.SysFirewall{} jun := jsonpb.Unmarshaler{ AllowUnknownFields: true, } rawConfig, err := json.Marshal(&ipt.SysConfig) if err != nil { log.Error("nfables.Serialize() struct to string error: %s", err) return nil, err } // string to proto if err := jun.Unmarshal(strings.NewReader(string(rawConfig)), sysfw); err != nil { log.Error("nfables.Serialize() string to protobuf error: %s", err) return nil, err } return sysfw, nil } // Deserialize converts a protocolbuffer structure to json. func (ipt *Iptables) Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) { jun := jsonpb.Marshaler{ OrigName: true, EmitDefaults: false, Indent: " ", } var b bytes.Buffer if err := jun.Marshal(&b, sysfw); err != nil { log.Error("nfables.Deserialize() error 2: %s", err) return nil, err } return b.Bytes(), nil //return nil, fmt.Errorf("iptables.Deserialize() not implemented") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/iptables/monitor.go������������������������������������������������0000664�0000000�0000000�00000004014�15003540030�0022557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iptables import ( "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/log" ) // AreRulesLoaded checks if the firewall rules for intercept traffic are loaded. func (ipt *Iptables) AreRulesLoaded() bool { var outMangle6 string outMangle, err := core.Exec("iptables", []string{"-n", "-L", "OUTPUT", "-t", "mangle"}) if err != nil { return false } if core.IPv6Enabled { outMangle6, err = core.Exec("ip6tables", []string{"-n", "-L", "OUTPUT", "-t", "mangle"}) if err != nil { return false } } systemRulesLoaded := true ipt.chains.RLock() if len(ipt.chains.Rules) > 0 { for _, rule := range ipt.chains.Rules { if chainOut4, err4 := core.Exec("iptables", []string{"-n", "-L", rule.Chain, "-t", rule.Table}); err4 == nil { if ipt.regexSystemRulesQuery.FindString(chainOut4) == "" { systemRulesLoaded = false break } } if core.IPv6Enabled { if chainOut6, err6 := core.Exec("ip6tables", []string{"-n", "-L", rule.Chain, "-t", rule.Table}); err6 == nil { if ipt.regexSystemRulesQuery.FindString(chainOut6) == "" { systemRulesLoaded = false break } } } } } ipt.chains.RUnlock() result := ipt.regexRulesQuery.FindString(outMangle) != "" && systemRulesLoaded if core.IPv6Enabled { result = result && ipt.regexRulesQuery.FindString(outMangle6) != "" } return result } // reloadRulesCallback gets called when the interception rules are not present or after the configuration file changes. func (ipt *Iptables) reloadRulesCallback() { log.Important("firewall rules changed, reloading") ipt.CleanRules(false) ipt.AddSystemRules(common.ReloadRules, common.BackupChains) ipt.EnableInterception() } // preloadConfCallback gets called before the fw configuration is reloaded func (ipt *Iptables) preloadConfCallback() { log.Info("iptables config changed, reloading") ipt.DeleteSystemRules(common.ForcedDelRules, common.BackupChains, log.GetLogLevel() == log.DEBUG) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/iptables/rules.go��������������������������������������������������0000664�0000000�0000000�00000004302�15003540030�0022222�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iptables import ( "fmt" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" "github.com/vishvananda/netlink" ) // RunRule inserts or deletes a firewall rule. func (ipt *Iptables) RunRule(action Action, enable bool, logError bool, rule []string) (err4, err6 error) { if enable == false { action = "-D" } rule = append([]string{string(action)}, rule...) ipt.Lock() defer ipt.Unlock() if _, err4 = core.Exec(ipt.bin, rule); err4 != nil { if logError { log.Error("Error while running firewall rule, ipv4 err: %s", err4) log.Error("rule: %s", rule) } } // On some systems IPv6 is disabled if core.IPv6Enabled { if _, err6 = core.Exec(ipt.bin6, rule); err6 != nil { if logError { log.Error("Error while running firewall rule, ipv6 err: %s", err6) log.Error("rule: %s", rule) } } } return } // QueueDNSResponses redirects DNS responses to us, in order to keep a cache // of resolved domains. // INPUT --protocol udp --sport 53 -j NFQUEUE --queue-num 0 --queue-bypass func (ipt *Iptables) QueueDNSResponses(enable bool, logError bool) (err4, err6 error) { return ipt.RunRule(INSERT, enable, logError, []string{ "INPUT", "--protocol", "udp", "--sport", "53", "-j", "NFQUEUE", "--queue-num", fmt.Sprintf("%d", ipt.QueueNum), "--queue-bypass", }) } // QueueConnections inserts the firewall rule which redirects connections to us. // Connections are queued until the user denies/accept them, or reaches a timeout. // OUTPUT -t mangle -m conntrack --ctstate NEW,RELATED -j NFQUEUE --queue-num 0 --queue-bypass func (ipt *Iptables) QueueConnections(enable bool, logError bool) (error, error) { err4, err6 := ipt.RunRule(ADD, enable, logError, []string{ "OUTPUT", "-t", "mangle", "-m", "conntrack", "--ctstate", "NEW,RELATED", "-j", "NFQUEUE", "--queue-num", fmt.Sprintf("%d", ipt.QueueNum), "--queue-bypass", }) if enable { // flush conntrack as soon as netfilter rule is set. This ensures that already-established // connections will go to netfilter queue. if err := netlink.ConntrackTableFlush(netlink.ConntrackTable); err != nil { log.Error("error in ConntrackTableFlush %s", err) } } return err4, err6 } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/iptables/system.go�������������������������������������������������0000664�0000000�0000000�00000011411�15003540030�0022413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iptables import ( "strings" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/firewall/config" ) // CreateSystemRule creates the custom firewall chains and adds them to the system. func (ipt *Iptables) CreateSystemRule(rule *config.FwRule, table, chain, hook string, logErrors bool) bool { ipt.chains.Lock() defer ipt.chains.Unlock() if rule == nil { return false } if table == "" { table = "filter" } if hook == "" { hook = rule.Chain } chainName := SystemRulePrefix + "-" + hook if _, ok := ipt.chains.Rules[table+"-"+chainName]; ok { return false } ipt.RunRule(NEWCHAIN, common.EnableRule, logErrors, []string{chainName, "-t", table}) // Insert the rule at the top of the chain if err4, err6 := ipt.RunRule(INSERT, common.EnableRule, logErrors, []string{hook, "-t", table, "-j", chainName}); err4 == nil && err6 == nil { ipt.chains.Rules[table+"-"+chainName] = &SystemRule{ Table: table, Chain: chain, Rule: rule, } } return true } // AddSystemRules creates the system firewall from configuration. func (ipt *Iptables) AddSystemRules(reload, backupExistingChains bool) { // Version 0 has no Enabled field, so it'd be always false if ipt.SysConfig.Enabled == false && ipt.SysConfig.Version > 0 { return } for _, cfg := range ipt.SysConfig.SystemRules { if cfg.Rule != nil { ipt.CreateSystemRule(cfg.Rule, cfg.Rule.Table, cfg.Rule.Chain, cfg.Rule.Chain, common.EnableRule) ipt.AddSystemRule(ADD, cfg.Rule, cfg.Rule.Table, cfg.Rule.Chain, common.EnableRule) continue } if cfg.Chains != nil { for _, chn := range cfg.Chains { if chn.Hook != "" && chn.Type != "" { ipt.ConfigureChainPolicy(chn.Type, chn.Hook, chn.Policy, true) } } } } } // DeleteSystemRules deletes the system rules. // If force is false and the rule has not been previously added, // it won't try to delete the rules. Otherwise it'll try to delete them. func (ipt *Iptables) DeleteSystemRules(force, backupExistingChains, logErrors bool) { ipt.chains.Lock() defer ipt.chains.Unlock() for _, fwCfg := range ipt.SysConfig.SystemRules { if fwCfg.Rule == nil { continue } chain := SystemRulePrefix + "-" + fwCfg.Rule.Chain if _, ok := ipt.chains.Rules[fwCfg.Rule.Table+"-"+chain]; !ok && !force { continue } ipt.RunRule(FLUSH, common.EnableRule, false, []string{chain, "-t", fwCfg.Rule.Table}) ipt.RunRule(DELETE, !common.EnableRule, logErrors, []string{fwCfg.Rule.Chain, "-t", fwCfg.Rule.Table, "-j", chain}) ipt.RunRule(DELCHAIN, common.EnableRule, false, []string{chain, "-t", fwCfg.Rule.Table}) delete(ipt.chains.Rules, fwCfg.Rule.Table+"-"+chain) for _, chn := range fwCfg.Chains { if chn.Table == "" { chn.Table = "filter" } chain := SystemRulePrefix + "-" + chn.Hook if _, ok := ipt.chains.Rules[chn.Type+"-"+chain]; !ok && !force { continue } ipt.RunRule(FLUSH, common.EnableRule, logErrors, []string{chain, "-t", chn.Type}) ipt.RunRule(DELETE, !common.EnableRule, logErrors, []string{chn.Hook, "-t", chn.Type, "-j", chain}) ipt.RunRule(DELCHAIN, common.EnableRule, logErrors, []string{chain, "-t", chn.Type}) delete(ipt.chains.Rules, chn.Type+"-"+chain) } } } // DeleteSystemRule deletes a new rule. func (ipt *Iptables) DeleteSystemRule(action Action, rule *config.FwRule, table, chain string, enable bool) (err4, err6 error) { chainName := SystemRulePrefix + "-" + chain if table == "" { table = "filter" } r := []string{chainName, "-t", table} if rule.Parameters != "" { r = append(r, strings.Split(rule.Parameters, " ")...) } r = append(r, []string{"-j", rule.Target}...) if rule.TargetParameters != "" { r = append(r, strings.Split(rule.TargetParameters, " ")...) } return ipt.RunRule(action, enable, true, r) } // AddSystemRule inserts a new rule. func (ipt *Iptables) AddSystemRule(action Action, rule *config.FwRule, table, chain string, enable bool) (err4, err6 error) { if rule == nil { return nil, nil } ipt.RLock() defer ipt.RUnlock() chainName := SystemRulePrefix + "-" + chain if table == "" { table = "filter" } r := []string{chainName, "-t", table} if rule.Parameters != "" { r = append(r, strings.Split(rule.Parameters, " ")...) } r = append(r, []string{"-j", rule.Target}...) if rule.TargetParameters != "" { r = append(r, strings.Split(rule.TargetParameters, " ")...) } return ipt.RunRule(ADD, enable, true, r) } // ConfigureChainPolicy configures chains policy. func (ipt *Iptables) ConfigureChainPolicy(table, hook, policy string, logError bool) { // TODO: list all policies before modify them, and restore the original state on exit. // still, if we exit abruptly, we might left the system badly configured. ipt.RunRule(POLICY, true, logError, []string{ hook, strings.ToUpper(policy), "-t", table, }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/����������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020535�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/chains.go�������������������������������������������������0000664�0000000�0000000�00000013750�15003540030�0022337�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "fmt" "strings" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" ) // getChainKey returns the identifier that will be used to link chains and rules. // When adding a new chain the key is stored, then later when adding a rule we get // the chain that the rule belongs to by this key. func getChainKey(name string, table *nftables.Table) string { if table == nil { return "" } return fmt.Sprintf("%s-%s-%d", name, table.Name, table.Family) } // GetChain gets an existing chain func GetChain(name string, table *nftables.Table) *nftables.Chain { key := getChainKey(name, table) if ch, ok := sysChains.Load(key); ok { return ch.(*nftables.Chain) } return nil } // AddChain adds a new chain to nftables. // https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook func (n *Nft) AddChain(name, table, family string, priority *nftables.ChainPriority, ctype nftables.ChainType, hook *nftables.ChainHook, policy nftables.ChainPolicy) *nftables.Chain { if family == "" { family = exprs.NFT_FAMILY_INET } tbl := n.GetTable(table, family) if tbl == nil { log.Error("%s addChain, Error getting table: %s, %s", logTag, table, family) return nil } var chain *nftables.Chain // Verify if the chain already exists, and reuse it if it does. // In some systems it fails adding a chain when it already exists, whilst in others // it doesn't. key := getChainKey(name, tbl) chain = n.GetChain(name, tbl, family) if chain != nil { if _, exists := sysChains.Load(key); exists { sysChains.Delete(key) } chain.Policy = &policy n.Conn.AddChain(chain) } else { // nft list chains chain = n.Conn.AddChain(&nftables.Chain{ Name: strings.ToLower(name), Table: tbl, Type: ctype, Hooknum: hook, Priority: priority, Policy: &policy, }) if chain == nil { log.Debug("%s AddChain() chain == nil", logTag) return nil } } sysChains.Store(key, chain) return chain } // GetChain checks if a chain in the given table exists. func (n *Nft) GetChain(name string, table *nftables.Table, family string) *nftables.Chain { if chains, err := n.Conn.ListChains(); err == nil { for _, c := range chains { if name == c.Name && table.Name == c.Table.Name && GetFamilyCode(family) == c.Table.Family { return c } } } return nil } // regular chains are user-defined chains, to better organize fw rules. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Adding_regular_chains func (n *Nft) addRegularChain(name, table, family string) error { tbl := n.GetTable(table, family) if tbl == nil { return fmt.Errorf("%s addRegularChain, Error getting table: %s, %s", logTag, table, family) } chain := n.Conn.AddChain(&nftables.Chain{ Name: name, Table: tbl, }) if chain == nil { return fmt.Errorf("%s error adding regular chain: %s", logTag, name) } key := getChainKey(name, tbl) sysChains.Store(key, chain) return nil } // AddInterceptionChains adds the needed chains to intercept traffic. func (n *Nft) AddInterceptionChains() error { var filterPolicy nftables.ChainPolicy var manglePolicy nftables.ChainPolicy filterPolicy = nftables.ChainPolicyAccept manglePolicy = nftables.ChainPolicyAccept tbl := n.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) if tbl != nil { key := getChainKey(exprs.NFT_HOOK_INPUT, tbl) ch, found := sysChains.Load(key) if key != "" && found { filterPolicy = *ch.(*nftables.Chain).Policy } } tbl = n.GetTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) if tbl != nil { key := getChainKey(exprs.NFT_HOOK_OUTPUT, tbl) ch, found := sysChains.Load(key) if key != "" && found { manglePolicy = *ch.(*nftables.Chain).Policy } } // nft list tables n.AddChain(exprs.NFT_HOOK_INPUT, exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, filterPolicy) if !n.Commit() { return fmt.Errorf("Error adding DNS interception chain input-filter-inet") } n.AddChain(exprs.NFT_HOOK_OUTPUT, exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET, nftables.ChainPriorityMangle, nftables.ChainTypeRoute, nftables.ChainHookOutput, manglePolicy) if !n.Commit() { log.Error("(1) Error adding interception chain mangle-output-inet, trying with type Filter instead of Route") // Workaround for kernels 4.x and maybe others. // @see firewall/nftables/utils.go:GetChainPriority() chainPrio, chainType := GetChainPriority(exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT) n.AddChain(exprs.NFT_HOOK_OUTPUT, exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET, chainPrio, chainType, nftables.ChainHookOutput, manglePolicy) if !n.Commit() { return fmt.Errorf("(2) Error adding interception chain mangle-output-inet with type Filter. Report it on github please, specifying the distro and the kernel") } } return nil } // DelChain deletes a chain from the system. func (n *Nft) DelChain(chain *nftables.Chain) error { n.Conn.DelChain(chain) sysChains.Delete(getChainKey(chain.Name, chain.Table)) if !n.Commit() { return fmt.Errorf("[nftables] error deleting chain %s, %s", chain.Name, chain.Table.Name) } return nil } // backupExistingChains saves chains with Accept policy. // If the user configures the chain policy to Drop, we need to set it back to Accept, // in order not to block incoming connections. func (n *Nft) backupExistingChains() { if chains, err := n.Conn.ListChains(); err == nil { for _, c := range chains { if c.Policy != nil && *c.Policy == nftables.ChainPolicyAccept { log.Debug("%s backing up existing chain with policy ACCEPT: %s, %s", logTag, c.Name, c.Table.Name) origSysChains[getChainKey(c.Name, c.Table)] = c } } } } func (n *Nft) restoreBackupChains() { for _, c := range origSysChains { log.Debug("%s Restoring chain policy to accept: %s, %s", logTag, c.Name, c.Table.Name) *c.Policy = nftables.ChainPolicyAccept n.Conn.AddChain(c) } n.Commit() } ������������������������opensnitch-1.6.9/daemon/firewall/nftables/chains_test.go��������������������������������������������0000664�0000000�0000000�00000004447�15003540030�0023401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables_test import ( "testing" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables" ) func TestChains(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn if nftest.Fw.AddInterceptionTables() != nil { t.Error("Error adding interception tables") } t.Run("AddChain", func(t *testing.T) { filterPolicy := nftables.ChainPolicyAccept chn := nftest.Fw.AddChain( exprs.NFT_HOOK_INPUT, exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, filterPolicy) if chn == nil { t.Error("chain input-filter-inet not created") } if !nftest.Fw.Commit() { t.Error("error adding input-filter-inet chain") } }) t.Run("getChain", func(t *testing.T) { tblfilter := nftest.Fw.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) if tblfilter == nil { t.Error("table filter-inet not created") } chn := nftest.Fw.GetChain(exprs.NFT_HOOK_INPUT, tblfilter, exprs.NFT_FAMILY_INET) if chn == nil { t.Error("chain input-filter-inet not added") } }) t.Run("delChain", func(t *testing.T) { tblfilter := nftest.Fw.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) if tblfilter == nil { t.Error("table filter-inet not created") } chn := nftest.Fw.GetChain(exprs.NFT_HOOK_INPUT, tblfilter, exprs.NFT_FAMILY_INET) if chn == nil { t.Error("chain input-filter-inet not added") } if err := nftest.Fw.DelChain(chn); err != nil { t.Error("error deleting chain input-filter-inet") } }) nftest.Fw.DelSystemTables() } // TestAddInterceptionChains checks if the needed tables and chains have been created. // We use 2: output-mangle-inet for intercepting outbound connections, and input-filter-inet for DNS responses interception func TestAddInterceptionChains(t *testing.T) { nftest.SkipIfNotPrivileged(t) if err := nftest.Fw.AddInterceptionTables(); err != nil { t.Errorf("Error adding interception tables: %s", err) } if err := nftest.Fw.AddInterceptionChains(); err != nil { t.Errorf("Error adding interception chains: %s", err) } nftest.Fw.DelSystemTables() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/����������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0021676�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/counter.go������������������������������������������0000664�0000000�0000000�00000000377�15003540030�0023713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "github.com/google/nftables/expr" ) // NewExprCounter returns a counter for packets or bytes. func NewExprCounter(counterName string) *[]expr.Any { return &[]expr.Any{ &expr.Objref{ Type: 1, Name: counterName, }, } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/counter_test.go�������������������������������������0000664�0000000�0000000�00000002433�15003540030�0024745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "testing" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables" ) func TestExprNamedCounter(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn // we must create the table before the counter object. tbl, _ := nftest.Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) nftest.Fw.Conn.AddObj( &nftables.CounterObj{ Table: &nftables.Table{ Name: "yyy", Family: nftables.TableFamilyINet, }, Name: "xxx-counter", Bytes: 0, Packets: 0, }, ) r, _ := nftest.AddTestRule(t, conn, exprs.NewExprCounter("xxx-counter")) if r == nil { t.Error("Error adding counter rule") return } objs, err := nftest.Fw.Conn.GetObjects(tbl) if err != nil { t.Errorf("Error retrieving objects from table %s: %s", tbl.Name, err) } if len(objs) != 1 { t.Errorf("%d objects found, expected 1", len(objs)) } counter, ok := objs[0].(*nftables.CounterObj) if !ok { t.Errorf("returned Obj is not CounterObj: %+v", objs[0]) } if counter.Name != "xxx-counter" { t.Errorf("CounterObj name differs: %s, expected 'xxx-counter'", counter.Name) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/ct.go�����������������������������������������������0000664�0000000�0000000�00000005540�15003540030�0022637�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "strconv" "strings" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" ) // Example https://github.com/google/nftables/blob/master/nftables_test.go#L1234 // https://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation // NewExprCtMark returns a new ct expression. // # set // # nft --debug netlink add rule filter output mark set 1 // ip filter output // [ immediate reg 1 0x00000001 ] // [ meta set mark with reg 1 ] // // match mark: // nft --debug netlink add rule mangle prerouting ct mark 123 // [ ct load mark => reg 1 ] // [ cmp eq reg 1 0x0000007b ] func NewExprCtMark(setMark bool, value string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { mark, err := strconv.Atoi(value) if err != nil { return nil, fmt.Errorf("Invalid conntrack mark: %s (%s)", err, value) } exprCtMark := []expr.Any{} exprCtMark = append(exprCtMark, []expr.Any{ &expr.Immediate{ Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(mark)), }, &expr.Ct{ Key: expr.CtKeyMARK, Register: 1, SourceRegister: setMark, }, }...) if setMark == false { exprCtMark = append(exprCtMark, []expr.Any{ &expr.Cmp{Op: *cmpOp, Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(mark))}, }...) } return &exprCtMark, nil } // NewExprCtState returns a new ct expression. func NewExprCtState(ctFlags []*config.ExprValues) (*[]expr.Any, error) { mask := uint32(0) for _, flag := range ctFlags { found, msk, err := parseInlineCtStates(flag.Value) if err != nil { return nil, err } if found { mask |= msk continue } msk, err = getCtState(flag.Value) if err != nil { return nil, err } mask |= msk } return &[]expr.Any{ &expr.Ct{ Register: 1, SourceRegister: false, Key: expr.CtKeySTATE, }, &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Mask: binaryutil.NativeEndian.PutUint32(mask), Xor: binaryutil.NativeEndian.PutUint32(0), }, }, nil } func parseInlineCtStates(flags string) (found bool, mask uint32, err error) { // a "state" flag may be compounded of multiple values, separated by commas: // related,established fgs := strings.Split(flags, ",") if len(fgs) > 0 { for _, fg := range fgs { msk, err := getCtState(fg) if err != nil { return false, 0, err } mask |= msk found = true } } return } func getCtState(flag string) (mask uint32, err error) { switch strings.ToLower(flag) { case CT_STATE_NEW: mask |= expr.CtStateBitNEW case CT_STATE_ESTABLISHED: mask |= expr.CtStateBitESTABLISHED case CT_STATE_RELATED: mask |= expr.CtStateBitRELATED case CT_STATE_INVALID: mask |= expr.CtStateBitINVALID default: return 0, fmt.Errorf("Invalid conntrack flag: %s", flag) } return } ����������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/ct_test.go������������������������������������������0000664�0000000�0000000�00000011755�15003540030�0023703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "fmt" "testing" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" ) func TestExprCtMark(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn type ctTestsT struct { nftest.TestsT setMark bool } cmp := expr.CmpOpEq tests := []ctTestsT{ { TestsT: nftest.TestsT{ Name: "test-ct-set-mark-666", Parms: "666", ExpectedExprsNum: 2, ExpectedExprs: []interface{}{ &expr.Immediate{ Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(666)), }, &expr.Ct{ Key: expr.CtKeyMARK, Register: 1, SourceRegister: true, }, }, }, setMark: true, }, { TestsT: nftest.TestsT{ Name: "test-ct-check-mark-666", Parms: "666", ExpectedExprsNum: 3, ExpectedExprs: []interface{}{ &expr.Immediate{ Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(666)), }, &expr.Ct{ Key: expr.CtKeyMARK, Register: 1, SourceRegister: false, }, &expr.Cmp{ Op: cmp, Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(666)), }, }, }, setMark: false, }, { TestsT: nftest.TestsT{ Name: "test-invalid-ct-check-mark", Parms: "0x29a", ExpectedExprsNum: 3, ExpectedExprs: []interface{}{}, ExpectedFail: true, }, setMark: false, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { quotaExpr, err := exprs.NewExprCtMark(test.setMark, test.TestsT.Parms, &cmp) if err != nil && !test.ExpectedFail { t.Errorf("Error creating expr Ct: %s", quotaExpr) return } else if err != nil && test.ExpectedFail { return } r, _ := nftest.AddTestRule(t, conn, quotaExpr) if r == nil && !test.ExpectedFail { t.Error("Error adding rule with Ct expression") } if !nftest.AreExprsValid(t, &test.TestsT, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } func TestExprCtState(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn type ctTestsT struct { nftest.TestsT setMark bool } tests := []nftest.TestsT{ { Name: "test-ct-single-state", Parms: "", Values: []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_CT_STATE, Value: exprs.CT_STATE_NEW, }, }, ExpectedExprsNum: 2, ExpectedExprs: []interface{}{ &expr.Ct{ Register: 1, SourceRegister: false, Key: expr.CtKeySTATE, }, &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitNEW), Xor: binaryutil.NativeEndian.PutUint32(0), }, }, ExpectedFail: false, }, { Name: "test-ct-multiple-states", Parms: "", Values: []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_CT_STATE, Value: fmt.Sprint(exprs.CT_STATE_NEW, ",", exprs.CT_STATE_ESTABLISHED), }, }, ExpectedExprsNum: 2, ExpectedExprs: []interface{}{ &expr.Ct{ Register: 1, SourceRegister: false, Key: expr.CtKeySTATE, }, &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitNEW | expr.CtStateBitESTABLISHED), Xor: binaryutil.NativeEndian.PutUint32(0), }, }, ExpectedFail: false, }, { Name: "test-invalid-ct-state", Parms: "", Values: []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_CT_STATE, Value: "xxx", }, }, ExpectedExprsNum: 2, ExpectedExprs: []interface{}{}, ExpectedFail: true, }, { Name: "test-invalid-ct-states", Parms: "", Values: []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_CT_STATE, Value: "new,xxx", }, }, ExpectedExprsNum: 2, ExpectedExprs: []interface{}{}, ExpectedFail: true, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { quotaExpr, err := exprs.NewExprCtState(test.Values) if err != nil && !test.ExpectedFail { t.Errorf("Error creating expr Ct: %s", quotaExpr) return } else if err != nil && test.ExpectedFail { return } r, _ := nftest.AddTestRule(t, conn, quotaExpr) if r == nil && !test.ExpectedFail { t.Error("Error adding rule with Quota expression") } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } �������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/enums.go��������������������������������������������0000664�0000000�0000000�00000012710�15003540030�0023355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs // keywords used in the configuration to define rules. const ( // https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook NFT_CHAIN_MANGLE = "mangle" NFT_CHAIN_FILTER = "filter" NFT_CHAIN_RAW = "raw" NFT_CHAIN_SECURITY = "security" NFT_CHAIN_NATDEST = "natdest" NFT_CHAIN_NATSOURCE = "natsource" NFT_CHAIN_CONNTRACK = "conntrack" NFT_CHAIN_SELINUX = "selinux" NFT_HOOK_INPUT = "input" NFT_HOOK_OUTPUT = "output" NFT_HOOK_PREROUTING = "prerouting" NFT_HOOK_POSTROUTING = "postrouting" NFT_HOOK_INGRESS = "ingress" NFT_HOOK_EGRESS = "egress" NFT_HOOK_FORWARD = "forward" NFT_TABLE_INET = "inet" NFT_TABLE_NAT = "nat" // TODO NFT_TABLE_ARP = "arp" NFT_TABLE_BRIDGE = "bridge" NFT_TABLE_NETDEV = "netdev" NFT_FAMILY_IP = "ip" NFT_FAMILY_IP6 = "ip6" NFT_FAMILY_INET = "inet" NFT_FAMILY_BRIDGE = "bridge" NFT_FAMILY_ARP = "arp" NFT_FAMILY_NETDEV = "netdev" VERDICT_ACCEPT = "accept" VERDICT_DROP = "drop" VERDICT_REJECT = "reject" VERDICT_RETURN = "return" VERDICT_QUEUE = "queue" VERDICT_JUMP = "jump" // TODO VERDICT_GOTO = "goto" VERDICT_STOP = "stop" VERDICT_STOLEN = "stolen" VERDICT_CONTINUE = "continue" VERDICT_MASQUERADE = "masquerade" VERDICT_DNAT = "dnat" VERDICT_SNAT = "snat" VERDICT_REDIRECT = "redirect" VERDICT_TPROXY = "tproxy" NFT_PARM_TO = "to" NFT_QUEUE_NUM = "num" NFT_QUEUE_BY_PASS = "queue-bypass" NFT_MASQ_RANDOM = "random" NFT_MASQ_FULLY_RANDOM = "fully-random" NFT_MASQ_PERSISTENT = "persistent" NFT_PROTOCOL = "protocol" NFT_SPORT = "sport" NFT_DPORT = "dport" NFT_SADDR = "saddr" NFT_DADDR = "daddr" NFT_ICMP_CODE = "code" NFT_ICMP_TYPE = "type" NFT_ETHER = "ether" NFT_IIFNAME = "iifname" NFT_OIFNAME = "oifname" NFT_LOG = "log" NFT_LOG_PREFIX = "prefix" // TODO NFT_LOG_LEVEL = "level" NFT_LOG_LEVEL_EMERG = "emerg" NFT_LOG_LEVEL_ALERT = "alert" NFT_LOG_LEVEL_CRIT = "crit" NFT_LOG_LEVEL_ERR = "err" NFT_LOG_LEVEL_WARN = "warn" NFT_LOG_LEVEL_NOTICE = "notice" NFT_LOG_LEVEL_INFO = "info" NFT_LOG_LEVEL_DEBUG = "debug" NFT_LOG_LEVEL_AUDIT = "audit" NFT_LOG_FLAGS = "flags" NFT_CT = "ct" NFT_CT_STATE = "state" NFT_CT_SET_MARK = "set" NFT_CT_MARK = "mark" CT_STATE_NEW = "new" CT_STATE_ESTABLISHED = "established" CT_STATE_RELATED = "related" CT_STATE_INVALID = "invalid" NFT_NOTRACK = "notrack" NFT_QUOTA = "quota" NFT_QUOTA_UNTIL = "until" NFT_QUOTA_OVER = "over" NFT_QUOTA_USED = "used" NFT_QUOTA_UNIT_BYTES = "bytes" NFT_QUOTA_UNIT_KB = "kbytes" NFT_QUOTA_UNIT_MB = "mbytes" NFT_QUOTA_UNIT_GB = "gbytes" NFT_COUNTER = "counter" NFT_COUNTER_NAME = "name" NFT_COUNTER_PACKETS = "packets" NFT_COUNTER_BYTES = "bytes" NFT_LIMIT = "limit" NFT_LIMIT_OVER = "over" NFT_LIMIT_BURST = "burst" NFT_LIMIT_UNITS_RATE = "rate-units" NFT_LIMIT_UNITS_TIME = "time-units" NFT_LIMIT_UNITS = "units" NFT_LIMIT_UNIT_SECOND = "second" NFT_LIMIT_UNIT_MINUTE = "minute" NFT_LIMIT_UNIT_HOUR = "hour" NFT_LIMIT_UNIT_DAY = "day" NFT_LIMIT_UNIT_KBYTES = "kbytes" NFT_LIMIT_UNIT_MBYTES = "mbytes" NFT_META = "meta" NFT_META_MARK = "mark" NFT_META_SET_MARK = "set" NFT_META_PRIORITY = "priority" NFT_META_NFTRACE = "nftrace" NFT_META_SET = "set" NFT_META_SKUID = "skuid" NFT_META_SKGID = "skgid" NFT_META_L4PROTO = "l4proto" NFT_META_PROTOCOL = "protocol" NFT_PROTO_UDP = "udp" NFT_PROTO_UDPLITE = "udplite" NFT_PROTO_TCP = "tcp" NFT_PROTO_SCTP = "sctp" NFT_PROTO_DCCP = "dccp" NFT_PROTO_ICMP = "icmp" NFT_PROTO_ICMPX = "icmpx" NFT_PROTO_ICMPv6 = "icmpv6" NFT_PROTO_AH = "ah" NFT_PROTO_ETHERNET = "ethernet" NFT_PROTO_GRE = "gre" NFT_PROTO_IP = "ip" NFT_PROTO_IPIP = "ipip" NFT_PROTO_L2TP = "l2tp" NFT_PROTO_COMP = "comp" NFT_PROTO_IGMP = "igmp" NFT_PROTO_ESP = "esp" NFT_PROTO_RAW = "raw" NFT_PROTO_ENCAP = "encap" ICMP_NO_ROUTE = "no-route" ICMP_PROT_UNREACHABLE = "prot-unreachable" ICMP_PORT_UNREACHABLE = "port-unreachable" ICMP_NET_UNREACHABLE = "net-unreachable" ICMP_ADDR_UNREACHABLE = "addr-unreachable" ICMP_HOST_UNREACHABLE = "host-unreachable" ICMP_NET_PROHIBITED = "net-prohibited" ICMP_HOST_PROHIBITED = "host-prohibited" ICMP_ADMIN_PROHIBITED = "admin-prohibited" ICMP_REJECT_ROUTE = "reject-route" ICMP_REJECT_POLICY_FAIL = "policy-fail" ICMP_ECHO_REPLY = "echo-reply" ICMP_ECHO_REQUEST = "echo-request" ICMP_SOURCE_QUENCH = "source-quench" ICMP_DEST_UNREACHABLE = "destination-unreachable" ICMP_REDIRECT = "redirect" ICMP_TIME_EXCEEDED = "time-exceeded" ICMP_INFO_REQUEST = "info-request" ICMP_INFO_REPLY = "info-reply" ICMP_PARAMETER_PROBLEM = "parameter-problem" ICMP_TIMESTAMP_REQUEST = "timestamp-request" ICMP_TIMESTAMP_REPLY = "timestamp-reply" ICMP_ROUTER_ADVERTISEMENT = "router-advertisement" ICMP_ROUTER_SOLICITATION = "router-solicitation" ICMP_ADDRESS_MASK_REQUEST = "address-mask-request" ICMP_ADDRESS_MASK_REPLY = "address-mask-reply" ICMP_PACKET_TOO_BIG = "packet-too-big" ICMP_NEIGHBOUR_SOLICITATION = "neighbour-solicitation" ICMP_NEIGHBOUR_ADVERTISEMENT = "neighbour-advertisement" ) ��������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/ether.go��������������������������������������������0000664�0000000�0000000�00000002617�15003540030�0023342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "encoding/hex" "fmt" "strings" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/google/nftables/expr" ) // NewExprEther creates a new expression to match ethernet MAC addresses func NewExprEther(values []*config.ExprValues) (*[]expr.Any, error) { etherExpr := []expr.Any{} macDir := uint32(6) for _, eth := range values { if eth.Key == NFT_DADDR { macDir = uint32(0) } else { macDir = uint32(6) } macaddr, err := parseMACAddr(eth.Value) if err != nil { return nil, err } etherExpr = append(etherExpr, []expr.Any{ &expr.Meta{Key: expr.MetaKeyIIFTYPE, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x01, 0x00}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseLLHeader, Offset: macDir, Len: 6, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: macaddr, }, }...) } return ðerExpr, nil } func parseMACAddr(macValue string) ([]byte, error) { mac := strings.Split(macValue, ":") macaddr := make([]byte, 0) if len(mac) != 6 { return nil, fmt.Errorf("Invalid MAC address: %s", macValue) } for i, m := range mac { mm, err := hex.DecodeString(m) if err != nil { return nil, fmt.Errorf("Invalid MAC byte: %c (%s)", mm[i], macValue) } macaddr = append(macaddr, mm[0]) } return macaddr, nil } �����������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/ether_test.go���������������������������������������0000664�0000000�0000000�00000005273�15003540030�0024402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "bytes" "reflect" "testing" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/expr" ) func TestExprEther(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn values := []*config.ExprValues{ &config.ExprValues{ Key: "ether", Value: "de:ad:be:af:ca:fe", }, } etherExpr, err := exprs.NewExprEther(values) if err != nil { t.Errorf("Error creating Ether expression: %s, %+v", err, values) } r, _ := nftest.AddTestRule(t, conn, etherExpr) if r == nil { t.Error("Error adding Ether rule") return } if len(r.Exprs) != 4 { t.Errorf("invalid rule created, we expected 4 expressions, got: %d", len(r.Exprs)) } /* expr Meta expr Cmp expr Payload expr Cmp */ t.Run("test-ether-expr meta", func(t *testing.T) { e := r.Exprs[0] // meta if reflect.TypeOf(e).String() != "*expr.Meta" { t.Errorf("first expression should be *expr.Meta, instead of: %s", reflect.TypeOf(e)) } lMeta, ok := e.(*expr.Meta) if !ok { t.Errorf("invalid meta expr: %T", e) } if lMeta.Key != expr.MetaKeyIIFTYPE { t.Errorf("invalid meta Key: %d, instead of %d", lMeta.Key, expr.MetaKeyIIFTYPE) } }) t.Run("test-ether-expr cmp", func(t *testing.T) { e := r.Exprs[1] // cmp if reflect.TypeOf(e).String() != "*expr.Cmp" { t.Errorf("second expression should be *expr.Cmp, instead of: %s", reflect.TypeOf(e)) } lCmp, ok := e.(*expr.Cmp) if !ok { t.Errorf("invalid cmp expr: %T", e) } if !bytes.Equal(lCmp.Data, []byte{0x01, 0x00}) { t.Errorf("invalid cmp data: %v", lCmp.Data) } }) t.Run("test-ether-expr payload", func(t *testing.T) { e := r.Exprs[2] // payload if reflect.TypeOf(e).String() != "*expr.Payload" { t.Errorf("third expression should be *expr.Payload, instead of: %s", reflect.TypeOf(e)) } lPayload, ok := e.(*expr.Payload) if !ok { t.Errorf("invalid payload expr: %T", e) } if lPayload.Base != expr.PayloadBaseLLHeader || lPayload.Offset != 6 || lPayload.Len != 6 { t.Errorf("invalid payload data: %v", lPayload) } }) t.Run("test-ether-expr cmp", func(t *testing.T) { e := r.Exprs[3] // cmp if reflect.TypeOf(e).String() != "*expr.Cmp" { t.Errorf("fourth expression should be *expr.Cmp, instead of: %s", reflect.TypeOf(e)) } lCmp, ok := e.(*expr.Cmp) if !ok { t.Errorf("invalid cmp expr: %T", e) } if !bytes.Equal(lCmp.Data, []byte{222, 173, 190, 175, 202, 254}) { t.Errorf("invalid cmp data: %q", lCmp.Data) } }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/iface.go��������������������������������������������0000664�0000000�0000000�00000001262�15003540030�0023275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "github.com/google/nftables/expr" ) // NewExprIface returns a new network interface expression func NewExprIface(iface string, isOut bool, cmpOp expr.CmpOp) *[]expr.Any { keyDev := expr.MetaKeyIIFNAME if isOut { keyDev = expr.MetaKeyOIFNAME } return &[]expr.Any{ &expr.Meta{Key: keyDev, Register: 1}, &expr.Cmp{ Op: cmpOp, Register: 1, Data: ifname(iface), }, } } // https://github.com/google/nftables/blob/master/nftables_test.go#L81 func ifname(n string) []byte { buf := make([]byte, 16) length := len(n) // allow wildcards if n[length-1:] == "*" { return []byte(n[:length-1]) } copy(buf, []byte(n+"\x00")) return buf } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/iface_test.go���������������������������������������0000664�0000000�0000000�00000004342�15003540030�0024336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "bytes" "reflect" "testing" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/expr" ) // https://github.com/evilsocket/opensnitch/blob/master/daemon/firewall/nftables/exprs/iface.go#L22 func ifname(n string) []byte { buf := make([]byte, 16) length := len(n) // allow wildcards if n[length-1:] == "*" { return []byte(n[:length-1]) } copy(buf, []byte(n+"\x00")) return buf } func TestExprIface(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn type ifaceTestsT struct { name string iface string out bool } tests := []ifaceTestsT{ {"test-in-iface-xxx", "in-iface0", false}, {"test-out-iface-xxx", "out-iface0", true}, {"test-out-iface-xxx-wildcard", "out-iface*", true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ifaceExpr := exprs.NewExprIface(test.iface, test.out, expr.CmpOpEq) r, _ := nftest.AddTestRule(t, conn, ifaceExpr) if r == nil { t.Error("Error adding rule with iface expression") } if total := len(r.Exprs); total != 2 { t.Errorf("expected 2 expressions, got %d: %+v", total, r.Exprs) } e := r.Exprs[0] if reflect.TypeOf(e).String() != "*expr.Meta" { t.Errorf("first expression should be *expr.Meta, instead of: %s", reflect.TypeOf(e)) } lExpr, ok := e.(*expr.Meta) if !ok { t.Errorf("invalid iface meta expr: %T", e) } if test.out && lExpr.Key != expr.MetaKeyOIFNAME { t.Errorf("iface Key should be MetaKeyOIFNAME instead of: %+v", lExpr) } else if !test.out && lExpr.Key != expr.MetaKeyIIFNAME { t.Errorf("iface Key should be MetaKeyIIFNAME instead of: %+v", lExpr) } e = r.Exprs[1] if reflect.TypeOf(e).String() != "*expr.Cmp" { t.Errorf("second expression should be *expr.Cmp, instead of: %s", reflect.TypeOf(e)) } lCmp, ok := e.(*expr.Cmp) if !ok { t.Errorf("invalid iface cmp expr: %T", e) } if !bytes.Equal(lCmp.Data, ifname(test.iface)) { t.Errorf("iface Cmp does not match: %v, expected: %v", lCmp.Data, ifname(test.iface)) } }) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/ip.go�����������������������������������������������0000664�0000000�0000000�00000007477�15003540030�0022654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "net" "strings" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) // NewExprIP returns a new IP expression. // You can use multiple statements to specify daddr + saddr, or combine them // in a single statement expression: // Example 1 (filtering by source and dest address): // "Name": "ip", // "Values": [ {"Key": "saddr": "Value": "1.2.3.4"},{"Key": "daddr": "Value": "1.2.3.5"} ] // Example 2 (filtering by multiple dest addrs IPs): // "Name": "ip", // "Values": [ // {"Key": "daddr": "Value": "1.2.3.4"}, // {"Key": "daddr": "Value": "1.2.3.5"} // ] // Example 3 (filtering by network range): // "Name": "ip", // "Values": [ // {"Key": "daddr": "Value": "1.2.3.4-1.2.9.254"} // ] // TODO (filter by multiple dest addrs separated by commas): // "Values": [ // {"Key": "daddr": "Value": "1.2.3.4,1.2.9.254"} // ] func NewExprIP(family string, ipOptions []*config.ExprValues, cmpOp expr.CmpOp) (*[]expr.Any, error) { var exprIP []expr.Any // if the table family is inet, we need to specify the protocol of the IP being added. if family == NFT_FAMILY_INET { exprIP = append(exprIP, &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}) exprIP = append(exprIP, &expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.NFPROTO_IPV4}}) } for _, ipOpt := range ipOptions { // TODO: ipv6 switch ipOpt.Key { case NFT_SADDR, NFT_DADDR: payload := getExprIPPayload(ipOpt.Key) exprIP = append(exprIP, payload) if strings.Index(ipOpt.Value, "-") == -1 { exprIPtemp, err := getExprIP(ipOpt.Value, cmpOp) if err != nil { return nil, err } exprIP = append(exprIP, *exprIPtemp...) } else { exprIPtemp, err := getExprRangeIP(ipOpt.Value, cmpOp) if err != nil { return nil, err } exprIP = append(exprIP, *exprIPtemp...) } case NFT_PROTOCOL: payload := getExprIPPayload(ipOpt.Key) exprIP = append(exprIP, payload) protoCode, err := getProtocolCode(ipOpt.Value) if err != nil { return nil, err } exprIP = append(exprIP, []expr.Any{ &expr.Cmp{ Op: cmpOp, Register: 1, Data: []byte{byte(protoCode)}, }, }...) } } return &exprIP, nil } func getExprIPPayload(what string) *expr.Payload { switch what { case NFT_PROTOCOL: return &expr.Payload{ DestRegister: 1, Offset: 9, // daddr Base: expr.PayloadBaseNetworkHeader, Len: 1, // 16 ipv6 } case NFT_DADDR: // NOTE 1: if "what" is daddr and SourceRegister is part of the Payload{} expression, // the rule is not added. return &expr.Payload{ DestRegister: 1, Offset: 16, // daddr Base: expr.PayloadBaseNetworkHeader, Len: 4, // 16 ipv6 } default: return &expr.Payload{ SourceRegister: 1, DestRegister: 1, Offset: 12, // saddr Base: expr.PayloadBaseNetworkHeader, Len: 4, // 16 ipv6 } } } // Supported IP types: a.b.c.d, a.b.c.d-w.x.y.z // TODO: support IPs separated by commas: a.b.c.d, e.f.g.h,... func getExprIP(value string, cmpOp expr.CmpOp) (*[]expr.Any, error) { ip := net.ParseIP(value) if ip == nil { return nil, fmt.Errorf("Invalid IP: %s", value) } return &[]expr.Any{ &expr.Cmp{ Op: cmpOp, Register: 1, Data: ip.To4(), }, }, nil } // Supported IP types: a.b.c.d, a.b.c.d-w.x.y.z // TODO: support IPs separated by commas: a.b.c.d, e.f.g.h,... func getExprRangeIP(value string, cmpOp expr.CmpOp) (*[]expr.Any, error) { ips := strings.Split(value, "-") ipSrc := net.ParseIP(ips[0]) ipDst := net.ParseIP(ips[1]) if ipSrc == nil || ipDst == nil { return nil, fmt.Errorf("Invalid IPs range: %v", ips) } return &[]expr.Any{ &expr.Range{ Op: cmpOp, Register: 1, FromData: ipSrc.To4(), ToData: ipDst.To4(), }, }, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/ip_test.go������������������������������������������0000664�0000000�0000000�00000014433�15003540030�0023701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "net" "testing" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) func TestExprIP(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn tests := []nftest.TestsT{ { "test-ip-daddr", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1.1.1.1", }, }, 2, []interface{}{ &expr.Payload{ SourceRegister: 0, DestRegister: 1, Offset: 16, Base: expr.PayloadBaseNetworkHeader, Len: 4, }, &expr.Cmp{ Data: net.ParseIP("1.1.1.1").To4(), }, }, false, }, { "test-ip-saddr", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: "saddr", Value: "1.1.1.1", }, }, 2, []interface{}{ &expr.Payload{ SourceRegister: 1, DestRegister: 1, Offset: 12, Base: expr.PayloadBaseNetworkHeader, Len: 4, }, &expr.Cmp{ Data: net.ParseIP("1.1.1.1").To4(), }, }, false, }, { "test-inet-daddr", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1.1.1.1", }, }, 4, []interface{}{ &expr.Meta{ Key: expr.MetaKeyNFPROTO, Register: 1, }, &expr.Cmp{ Data: []byte{unix.NFPROTO_IPV4}, }, &expr.Payload{ SourceRegister: 0, DestRegister: 1, Offset: 16, Base: expr.PayloadBaseNetworkHeader, Len: 4, }, &expr.Cmp{ Data: net.ParseIP("1.1.1.1").To4(), }, }, false, }, { "test-ip-daddr-invalid", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1.1.1", }, }, 0, []interface{}{}, true, }, { "test-ip-daddr-invalid", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1..1.1.1", }, }, 0, []interface{}{}, true, }, { "test-ip-daddr-invalid", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "www.test.com", }, }, 0, []interface{}{}, true, }, { "test-ip-daddr-invalid", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "", }, }, 0, []interface{}{}, true, }, { "test-inet-saddr", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "saddr", Value: "1.1.1.1", }, }, 4, []interface{}{ &expr.Meta{ Key: expr.MetaKeyNFPROTO, Register: 1, }, &expr.Cmp{ Data: []byte{unix.NFPROTO_IPV4}, }, &expr.Payload{ SourceRegister: 1, DestRegister: 1, Offset: 12, Base: expr.PayloadBaseNetworkHeader, Len: 4, }, &expr.Cmp{ Data: net.ParseIP("1.1.1.1").To4(), }, }, false, }, { "test-inet-daddr-invalid", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1..1.1.1", }, }, 0, []interface{}{}, true, }, { "test-inet-saddr-invalid", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "saddr", Value: "1..1.1.1", }, }, 0, []interface{}{}, true, }, { "test-inet-range-daddr", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1.1.1.1-2.2.2.2", }, }, 4, []interface{}{ &expr.Meta{ Key: expr.MetaKeyNFPROTO, Register: 1, }, &expr.Cmp{ Data: []byte{unix.NFPROTO_IPV4}, }, &expr.Payload{ SourceRegister: 0, DestRegister: 1, Offset: 16, Base: expr.PayloadBaseNetworkHeader, Len: 4, }, &expr.Range{ Register: 1, FromData: net.ParseIP("1.1.1.1").To4(), ToData: net.ParseIP("2.2.2.2").To4(), }, }, false, }, { "test-inet-range-saddr", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "saddr", Value: "1.1.1.1-2.2.2.2", }, }, 4, []interface{}{ &expr.Meta{ Key: expr.MetaKeyNFPROTO, Register: 1, }, &expr.Cmp{ Data: []byte{unix.NFPROTO_IPV4}, }, &expr.Payload{ SourceRegister: 1, DestRegister: 1, Offset: 12, Base: expr.PayloadBaseNetworkHeader, Len: 4, }, &expr.Range{ Register: 1, FromData: net.ParseIP("1.1.1.1").To4(), ToData: net.ParseIP("2.2.2.2").To4(), }, }, false, }, { "test-inet-daddr-range-invalid", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1.1.1.1--2.2.2.2", }, }, 0, []interface{}{}, true, }, { "test-inet-daddr-range-invalid", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", Value: "1.1.1.1-1..2.2.2", }, }, 0, []interface{}{}, true, }, { "test-inet-daddr-range-invalid", exprs.NFT_FAMILY_INET, "", []*config.ExprValues{ &config.ExprValues{ Key: "daddr", // TODO: not supported yet Value: "1.1.1.1/24", }, }, 0, []interface{}{}, true, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { ipExpr, err := exprs.NewExprIP(test.Family, test.Values, expr.CmpOpEq) if err != nil && !test.ExpectedFail { t.Errorf("Error creating expr IP: %s", ipExpr) return } else if err != nil && test.ExpectedFail { return } r, _ := nftest.AddTestRule(t, conn, ipExpr) if r == nil && !test.ExpectedFail { t.Error("Error adding rule with IP expression") } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/limit.go��������������������������������������������0000664�0000000�0000000�00000004014�15003540030�0023342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "strconv" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/google/nftables/expr" ) // NewExprLimit returns a new limit expression. // limit rate [over] 1/second // to express bytes units, we use: 10-mbytes instead of nft's 10 mbytes func NewExprLimit(statement *config.ExprStatement) (*[]expr.Any, error) { var err error exprLimit := &expr.Limit{ Type: expr.LimitTypePkts, Over: false, Unit: expr.LimitTimeSecond, } for _, values := range statement.Values { switch values.Key { case NFT_LIMIT_OVER: exprLimit.Over = true case NFT_LIMIT_UNITS: exprLimit.Rate, err = strconv.ParseUint(values.Value, 10, 64) if err != nil { return nil, fmt.Errorf("Invalid limit rate: %s", values.Value) } case NFT_LIMIT_BURST: limitBurst := 0 limitBurst, err = strconv.Atoi(values.Value) if err != nil || limitBurst == 0 { return nil, fmt.Errorf("Invalid burst limit: %s, err: %s", values.Value, err) } exprLimit.Burst = uint32(limitBurst) case NFT_LIMIT_UNITS_RATE: // units rate must be placed AFTER the rate exprLimit.Type, exprLimit.Rate = getLimitRate(values.Value, exprLimit.Rate) case NFT_LIMIT_UNITS_TIME: exprLimit.Unit = getLimitUnits(values.Value) } } return &[]expr.Any{exprLimit}, nil } func getLimitUnits(units string) (limitUnits expr.LimitTime) { switch units { case NFT_LIMIT_UNIT_MINUTE: limitUnits = expr.LimitTimeMinute case NFT_LIMIT_UNIT_HOUR: limitUnits = expr.LimitTimeHour case NFT_LIMIT_UNIT_DAY: limitUnits = expr.LimitTimeDay default: limitUnits = expr.LimitTimeSecond } return limitUnits } func getLimitRate(units string, rate uint64) (limitType expr.LimitType, limitRate uint64) { switch units { case NFT_LIMIT_UNIT_KBYTES: limitRate = rate * 1024 limitType = expr.LimitTypePktBytes case NFT_LIMIT_UNIT_MBYTES: limitRate = (rate * 1024) * 1024 limitType = expr.LimitTypePktBytes default: limitType = expr.LimitTypePkts limitRate, _ = strconv.ParseUint(units, 10, 64) } return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/log.go����������������������������������������������0000664�0000000�0000000�00000003422�15003540030�0023007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) // NewExprLog returns a new log expression. func NewExprLog(statement *config.ExprStatement) (*[]expr.Any, error) { prefix := "opensnitch" logExpr := expr.Log{ Key: 1 << unix.NFTA_LOG_PREFIX, Data: []byte(prefix), } for _, values := range statement.Values { switch values.Key { case NFT_LOG_PREFIX: if values.Value == "" { return nil, fmt.Errorf("Invalid log prefix, it's empty") } logExpr.Data = []byte(values.Value) case NFT_LOG_LEVEL: lvl, err := getLogLevel(values.Value) if err != nil { log.Warning("%s", err) return nil, err } logExpr.Key |= 1 << unix.NFTA_LOG_LEVEL logExpr.Level = lvl // TODO // https://github.com/google/nftables/blob/main/nftables_test.go#L623 //case exprs.NFT_LOG_FLAGS: //case exprs.NFT_LOG_GROUP: //case exprs.NFT_LOG_QTHRESHOLD: } } return &[]expr.Any{ &logExpr, }, nil } func getLogLevel(what string) (expr.LogLevel, error) { switch what { // https://github.com/google/nftables/blob/main/expr/log.go#L28 case NFT_LOG_LEVEL_EMERG: return expr.LogLevelEmerg, nil case NFT_LOG_LEVEL_ALERT: return expr.LogLevelAlert, nil case NFT_LOG_LEVEL_CRIT: return expr.LogLevelCrit, nil case NFT_LOG_LEVEL_ERR: return expr.LogLevelErr, nil case NFT_LOG_LEVEL_WARN: return expr.LogLevelWarning, nil case NFT_LOG_LEVEL_NOTICE: return expr.LogLevelNotice, nil case NFT_LOG_LEVEL_INFO: return expr.LogLevelInfo, nil case NFT_LOG_LEVEL_DEBUG: return expr.LogLevelDebug, nil case NFT_LOG_LEVEL_AUDIT: return expr.LogLevelAudit, nil } return 0, fmt.Errorf("Invalid log level: %s", what) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/log_test.go�����������������������������������������0000664�0000000�0000000�00000006302�15003540030�0024046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "testing" "github.com/evilsocket/opensnitch/daemon/firewall/config" exprs "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) func TestExprLog(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn type logTestsT struct { nftest.TestsT statem *config.ExprStatement } tests := []logTestsT{ { TestsT: nftest.TestsT{ Name: "test-log-prefix-simple", Values: []*config.ExprValues{ &config.ExprValues{ Key: "prefix", Value: "counter-test", }, }, ExpectedExprs: []interface{}{ &expr.Log{ Key: 1 << unix.NFTA_LOG_PREFIX, Data: []byte("counter-test"), }, }, ExpectedExprsNum: 1, ExpectedFail: false, }, statem: &config.ExprStatement{ Op: "==", Name: "log", }, }, { TestsT: nftest.TestsT{ Name: "test-log-prefix-emerg", Values: []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_LOG_PREFIX, Value: "counter-test-emerg", }, &config.ExprValues{ Key: exprs.NFT_LOG_LEVEL, Value: exprs.NFT_LOG_LEVEL_EMERG, }, }, ExpectedExprs: []interface{}{ &expr.Log{ Key: (1 << unix.NFTA_LOG_PREFIX) | (1 << unix.NFTA_LOG_LEVEL), Level: expr.LogLevelEmerg, Data: []byte("counter-test-emerg"), }, }, ExpectedExprsNum: 1, ExpectedFail: false, }, statem: &config.ExprStatement{ Op: "==", Name: "log", }, }, { TestsT: nftest.TestsT{ Name: "test-invalid-log-prefix", Values: []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_LOG_PREFIX, Value: "", }, &config.ExprValues{ Key: exprs.NFT_LOG_LEVEL, Value: exprs.NFT_LOG_LEVEL_EMERG, }, }, ExpectedExprs: []interface{}{}, ExpectedExprsNum: 0, ExpectedFail: true, }, statem: &config.ExprStatement{ Op: "==", Name: "log", }, }, { TestsT: nftest.TestsT{ Name: "test-invalid-log-level", Values: []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_LOG_PREFIX, Value: "counter-invalid-level", }, &config.ExprValues{ Key: exprs.NFT_LOG_LEVEL, Value: "", }, }, ExpectedExprs: []interface{}{}, ExpectedExprsNum: 0, ExpectedFail: true, }, statem: &config.ExprStatement{ Op: "==", Name: "log", }, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { test.statem.Values = test.TestsT.Values logExpr, err := exprs.NewExprLog(test.statem) if err != nil && !test.ExpectedFail { t.Errorf("Error creating expr Log: %s", logExpr) return } else if err != nil && test.ExpectedFail { return } r, _ := nftest.AddTestRule(t, conn, logExpr) if r == nil { t.Error("Error adding rule with log expression") } if !nftest.AreExprsValid(t, &test.TestsT, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/meta.go���������������������������������������������0000664�0000000�0000000�00000006643�15003540030�0023164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "strconv" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" ) // NewExprMeta creates a new meta selector to match or set packet metainformation. // https://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation func NewExprMeta(values []*config.ExprValues, cmpOp *expr.CmpOp) (*[]expr.Any, error) { setMark := false metaExpr := []expr.Any{} for _, meta := range values { switch meta.Key { case NFT_META_SET_MARK: setMark = true continue case NFT_META_MARK: metaKey, err := getMetaKey(meta.Key) if err != nil { return nil, err } metaVal, err := getMetaValue(meta.Value) if err != nil { return nil, err } if setMark { metaExpr = append(metaExpr, []expr.Any{ &expr.Immediate{ Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(metaVal)), }}...) metaExpr = append(metaExpr, []expr.Any{ &expr.Meta{Key: metaKey, Register: 1, SourceRegister: setMark}}...) } else { metaExpr = append(metaExpr, []expr.Any{ &expr.Meta{Key: metaKey, Register: 1, SourceRegister: setMark}, &expr.Cmp{ Op: *cmpOp, Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(metaVal)), }}...) } setMark = false return &metaExpr, nil case NFT_META_L4PROTO: mexpr, err := NewExprProtocol(meta.Key) if err != nil { return nil, err } metaExpr = append(metaExpr, *mexpr...) return &metaExpr, nil case NFT_META_PRIORITY, NFT_META_SKUID, NFT_META_SKGID, NFT_META_PROTOCOL: metaKey, err := getMetaKey(meta.Key) if err != nil { return nil, err } metaVal, err := getProtocolCode(meta.Value) if err != nil { return nil, err } metaExpr = append(metaExpr, []expr.Any{ &expr.Meta{Key: metaKey, Register: 1, SourceRegister: setMark}, &expr.Cmp{ Op: *cmpOp, Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(metaVal)), }}...) setMark = false return &metaExpr, nil case NFT_META_NFTRACE: mark, err := getMetaValue(meta.Value) if err != nil { return nil, err } if mark != 0 && mark != 1 { return nil, fmt.Errorf("%s Invalid nftrace value: %d. Only 1 or 0 allowed", "nftables", mark) } // TODO: not working yet return &[]expr.Any{ &expr.Meta{Key: expr.MetaKeyNFTRACE, Register: 1}, &expr.Cmp{ Op: *cmpOp, Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(mark)), }, }, nil default: // not supported yet } } return nil, fmt.Errorf("%s meta keyword not supported yet, open a new issue on github", "nftables") } func getMetaValue(value string) (int, error) { metaVal, err := strconv.Atoi(value) if err != nil { return 0, err } return metaVal, nil } // https://github.com/google/nftables/blob/main/expr/expr.go#L168 func getMetaKey(value string) (expr.MetaKey, error) { switch value { case NFT_META_MARK: return expr.MetaKeyMARK, nil case NFT_META_PRIORITY: return expr.MetaKeyPRIORITY, nil case NFT_META_SKUID: return expr.MetaKeySKUID, nil case NFT_META_SKGID: return expr.MetaKeySKGID, nil // ip, ip6, arp, vlan case NFT_META_PROTOCOL: return expr.MetaKeyPROTOCOL, nil case NFT_META_L4PROTO: return expr.MetaKeyL4PROTO, nil } return expr.MetaKeyPRANDOM, fmt.Errorf("meta key %s not supported (yet)", value) } ���������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/meta_test.go����������������������������������������0000664�0000000�0000000�00000007777�15003540030�0024234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "testing" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" ) func TestExprMeta(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn tests := []nftest.TestsT{ { "test-meta-mark", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_META_MARK, Value: "666", }, }, 2, []interface{}{ &expr.Meta{ Key: expr.MetaKeyMARK, Register: 1, SourceRegister: false, }, &expr.Cmp{ Data: binaryutil.NativeEndian.PutUint32(uint32(666)), }, }, false, }, { "test-meta-set-mark", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_META_SET_MARK, Value: "", }, &config.ExprValues{ Key: exprs.NFT_META_MARK, Value: "666", }, }, 2, []interface{}{ &expr.Immediate{ Register: 1, Data: binaryutil.NativeEndian.PutUint32(666), }, &expr.Meta{ Key: expr.MetaKeyMARK, Register: 1, SourceRegister: true, }, }, false, }, { "test-meta-priority", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_META_PRIORITY, Value: "1", }, }, 2, []interface{}{ &expr.Meta{ Key: expr.MetaKeyPRIORITY, Register: 1, SourceRegister: false, }, &expr.Cmp{ Data: binaryutil.NativeEndian.PutUint32(uint32(1)), }, }, false, }, { "test-meta-skuid", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_META_SKUID, Value: "1", }, }, 2, []interface{}{ &expr.Meta{ Key: expr.MetaKeySKUID, Register: 1, SourceRegister: false, }, &expr.Cmp{ Data: binaryutil.NativeEndian.PutUint32(uint32(1)), }, }, false, }, { "test-meta-skgid", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_META_SKGID, Value: "1", }, }, 2, []interface{}{ &expr.Meta{ Key: expr.MetaKeySKGID, Register: 1, SourceRegister: false, }, &expr.Cmp{ Data: binaryutil.NativeEndian.PutUint32(uint32(1)), }, }, false, }, { "test-meta-protocol", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_META_PROTOCOL, Value: "15", }, }, 2, []interface{}{ &expr.Meta{ Key: expr.MetaKeyPROTOCOL, Register: 1, SourceRegister: false, }, &expr.Cmp{ Data: binaryutil.NativeEndian.PutUint32(uint32(15)), }, }, false, }, // tested more in depth in protocol_test.go { "test-meta-l4proto", exprs.NFT_FAMILY_IP, "", []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_META_L4PROTO, Value: "15", }, }, 1, []interface{}{ &expr.Meta{ Key: expr.MetaKeyL4PROTO, Register: 1, SourceRegister: false, }, }, false, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { cmp := expr.CmpOpEq metaExpr, err := exprs.NewExprMeta(test.Values, &cmp) if err != nil && !test.ExpectedFail { t.Errorf("Error creating expr Meta: %s", metaExpr) return } else if err != nil && test.ExpectedFail { return } r, _ := nftest.AddTestRule(t, conn, metaExpr) if r == nil && !test.ExpectedFail { t.Error("Error adding rule with Meta expression") } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } �opensnitch-1.6.9/daemon/firewall/nftables/exprs/nat.go����������������������������������������������0000664�0000000�0000000�00000010067�15003540030�0023013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "net" "strconv" "strings" "github.com/google/nftables" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) // NewExprNATFlags returns the nat flags configured. // common to masquerade, snat and dnat func NewExprNATFlags(parms string) (random, fullrandom, persistent bool) { masqParms := strings.Split(parms, ",") for _, mParm := range masqParms { switch mParm { case NFT_MASQ_RANDOM: random = true case NFT_MASQ_FULLY_RANDOM: fullrandom = true case NFT_MASQ_PERSISTENT: persistent = true } } return } // NewExprNAT parses the redirection of redirect, snat, dnat, tproxy and masquerade verdict: // to x.y.z.a:abcd // If only the IP is specified (to 1.2.3.4), only NAT.RegAddrMin must be present (regAddr == true) // If only the port is specified (to :1234), only NAT.RegPortMin must be present (regPort == true) // If both addr and port are specified (to 1.2.3.4:1234), NAT.RegPortMin and NAT.RegAddrMin must be present. func NewExprNAT(parms, verdict string) (bool, bool, *[]expr.Any, error) { regAddr := false regProto := false exprNAT := []expr.Any{} NATParms := strings.Split(parms, " ") idx := 0 // exclude first parameter if it's "to" if NATParms[idx] == NFT_PARM_TO { idx++ } if idx == len(NATParms) { return regAddr, regProto, &exprNAT, fmt.Errorf("Invalid parms: %s", parms) } dParms := strings.Split(NATParms[idx], ":") // masquerade doesn't allow "to IP" if dParms[0] != "" && verdict != VERDICT_MASQUERADE { dIP := dParms[0] destIP := net.ParseIP(dIP) if destIP == nil { return regAddr, regProto, &exprNAT, fmt.Errorf("Invalid IP: %s", dIP) } exprNAT = append(exprNAT, []expr.Any{ &expr.Immediate{ Register: 1, Data: destIP.To4(), }}...) regAddr = true } if len(dParms) == 2 { dPort := dParms[1] // TODO: support ranges. 9000-9100 destPort, err := strconv.Atoi(dPort) if err != nil { return regAddr, regProto, &exprNAT, fmt.Errorf("Invalid Port: %s", dPort) } reg := uint32(2) toPort := binaryutil.BigEndian.PutUint16(uint16(destPort)) // if reg=1 (RegAddrMin=1) is not set, this error appears listing the rules // "netlink: Error: NAT statement has no proto expression" if verdict == VERDICT_TPROXY || verdict == VERDICT_MASQUERADE || verdict == VERDICT_REDIRECT { // according to https://github.com/google/nftables/blob/8a10f689006bf728a5cff35787713047f68e308a/nftables_test.go#L4871 // Masquerade ports should be specified like this: // toPort = binaryutil.BigEndian.PutUint32(uint32(destPort) << 16) // but then it's not added/listed correctly with nft. reg = 1 } exprNAT = append(exprNAT, []expr.Any{ &expr.Immediate{ Register: reg, Data: toPort, }}...) regProto = true } return regAddr, regProto, &exprNAT, nil } // NewExprMasquerade returns a new masquerade expression. func NewExprMasquerade(toPorts, random, fullRandom, persistent bool) *[]expr.Any { exprMasq := &expr.Masq{ ToPorts: toPorts, Random: random, FullyRandom: fullRandom, Persistent: persistent, } if toPorts { exprMasq.RegProtoMin = 1 } return &[]expr.Any{ exprMasq, } } // NewExprRedirect returns a new redirect expression. func NewExprRedirect() *[]expr.Any { return &[]expr.Any{ // Redirect is a special case of DNAT where the destination is the current machine &expr.Redir{ RegisterProtoMin: 1, }, } } // NewExprSNAT returns a new snat expression. func NewExprSNAT() *expr.NAT { return &expr.NAT{ Type: expr.NATTypeSourceNAT, Family: unix.NFPROTO_IPV4, } } // NewExprDNAT returns a new dnat expression. func NewExprDNAT() *expr.NAT { return &expr.NAT{ Type: expr.NATTypeDestNAT, Family: unix.NFPROTO_IPV4, } } // NewExprTproxy returns a new tproxy expression. // XXX: is "to x.x.x.x:1234" supported by google/nftables lib? or only "to :1234"? // it creates an erronous rule. func NewExprTproxy() *[]expr.Any { return &[]expr.Any{ &expr.TProxy{ Family: byte(nftables.TableFamilyIPv4), TableFamily: byte(nftables.TableFamilyIPv4), RegPort: 1, }} } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/nat_test.go�����������������������������������������0000664�0000000�0000000�00000030042�15003540030�0024045�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "net" "testing" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) func TestExprVerdictSNAT(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn // TODO: test random, permanent, persistent flags. tests := []nftest.TestsT{ { "test-nat-snat-to-127001", exprs.NFT_FAMILY_IP, "to 127.0.0.1", nil, 2, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.NAT{ Type: expr.NATTypeSourceNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, }, }, false, }, { "test-nat-snat-127001", exprs.NFT_FAMILY_IP, "127.0.0.1", nil, 2, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.NAT{ Type: expr.NATTypeSourceNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, }, }, false, }, { "test-nat-snat-to-127001:12345", exprs.NFT_FAMILY_IP, "to 127.0.0.1:12345", nil, 3, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.Immediate{ Register: uint32(2), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.NAT{ Type: expr.NATTypeSourceNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, RegProtoMin: 2, }, }, false, }, { "test-nat-snat-to-:12345", exprs.NFT_FAMILY_IP, "to :12345", nil, 2, []interface{}{ &expr.Immediate{ Register: uint32(2), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.NAT{ Type: expr.NATTypeSourceNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 0, RegProtoMin: 2, }, }, false, }, { "test-nat-snat-127001:12345", exprs.NFT_FAMILY_IP, "127.0.0.1:12345", nil, 3, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.Immediate{ Register: uint32(2), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.NAT{ Type: expr.NATTypeSourceNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, RegProtoMin: 2, }, }, false, }, { "test-invalid-nat-snat-to-", exprs.NFT_FAMILY_IP, "to", nil, 3, []interface{}{}, true, }, { "test-invalid-nat-snat-to-invalid-ip", exprs.NFT_FAMILY_IP, "to 127..0.0.1", nil, 3, []interface{}{}, true, }, { "test-invalid-nat-snat-to-invalid-port", exprs.NFT_FAMILY_IP, "to 127.0.0.1:aaa", nil, 3, []interface{}{}, true, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { verdExpr := exprs.NewExprVerdict(exprs.VERDICT_SNAT, test.Parms) if !test.ExpectedFail && verdExpr == nil { t.Errorf("error creating snat verdict") } else if test.ExpectedFail && verdExpr == nil { return } r, _ := nftest.AddTestSNATRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule") return } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } func TestExprVerdictDNAT(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn tests := []nftest.TestsT{ { "test-nat-dnat-to-127001", exprs.NFT_FAMILY_IP, "to 127.0.0.1", nil, 2, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.NAT{ Type: expr.NATTypeDestNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, }, }, false, }, { "test-nat-dnat-127001", exprs.NFT_FAMILY_IP, "127.0.0.1", nil, 2, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.NAT{ Type: expr.NATTypeDestNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, }, }, false, }, { "test-nat-dnat-to-127001:12345", exprs.NFT_FAMILY_IP, "to 127.0.0.1:12345", nil, 3, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.Immediate{ Register: uint32(2), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.NAT{ Type: expr.NATTypeDestNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, RegProtoMin: 2, }, }, false, }, { "test-nat-dnat-to-:12345", exprs.NFT_FAMILY_IP, "to :12345", nil, 2, []interface{}{ &expr.Immediate{ Register: uint32(2), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.NAT{ Type: expr.NATTypeDestNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 0, RegProtoMin: 2, }, }, false, }, { "test-nat-dnat-127001:12345", exprs.NFT_FAMILY_IP, "127.0.0.1:12345", nil, 3, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.Immediate{ Register: uint32(2), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.NAT{ Type: expr.NATTypeDestNAT, Family: unix.NFPROTO_IPV4, Random: false, FullyRandom: false, Persistent: false, RegAddrMin: 1, RegProtoMin: 2, }, }, false, }, { "test-invalid-nat-dnat-to-", exprs.NFT_FAMILY_IP, "to", nil, 3, []interface{}{}, true, }, { "test-invalid-nat-dnat-to-invalid-ip", exprs.NFT_FAMILY_IP, "to 127..0.0.1", nil, 3, []interface{}{}, true, }, { "test-invalid-nat-dnat-to-invalid-port", exprs.NFT_FAMILY_IP, "to 127.0.0.1:aaa", nil, 3, []interface{}{}, true, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { verdExpr := exprs.NewExprVerdict(exprs.VERDICT_DNAT, test.Parms) if !test.ExpectedFail && verdExpr == nil { t.Errorf("error creating verdict") } else if test.ExpectedFail && verdExpr == nil { return } r, _ := nftest.AddTestDNATRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule") return } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } func TestExprVerdictMasquerade(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn tests := []nftest.TestsT{ { "test-nat-masq-to-:12345", exprs.NFT_FAMILY_IP, "to :12345", nil, 2, []interface{}{ &expr.Immediate{ Register: uint32(1), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.Masq{ ToPorts: true, Random: false, FullyRandom: false, Persistent: false, }, }, false, }, { "test-nat-masq-flags", exprs.NFT_FAMILY_IP, "random,fully-random,persistent", nil, 1, []interface{}{ &expr.Masq{ ToPorts: false, Random: true, FullyRandom: true, Persistent: true, }, }, false, }, { "test-nat-masq-empty", exprs.NFT_FAMILY_IP, "", nil, 1, []interface{}{ &expr.Masq{}, }, false, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { verdExpr := exprs.NewExprVerdict(exprs.VERDICT_MASQUERADE, test.Parms) if !test.ExpectedFail && verdExpr == nil { t.Errorf("error creating verdict") } else if test.ExpectedFail && verdExpr == nil { return } r, _ := nftest.AddTestSNATRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule") return } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } func TestExprVerdictRedirect(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn tests := []nftest.TestsT{ { "test-nat-redir-to-127001:12345", exprs.NFT_FAMILY_IP, "to 127.0.0.1:12345", nil, 3, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.Immediate{ Register: uint32(1), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.Redir{ RegisterProtoMin: 1, }, }, false, }, { "test-nat-redir-to-:12345", exprs.NFT_FAMILY_IP, "to :12345", nil, 2, []interface{}{ &expr.Immediate{ Register: uint32(1), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.Redir{ RegisterProtoMin: 1, }, }, false, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { verdExpr := exprs.NewExprVerdict(exprs.VERDICT_REDIRECT, test.Parms) if !test.ExpectedFail && verdExpr == nil { t.Errorf("error creating verdict") } else if test.ExpectedFail && verdExpr == nil { return } r, _ := nftest.AddTestDNATRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule") return } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } func TestExprVerdictTProxy(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn tests := []nftest.TestsT{ { "test-nat-tproxy-to-127001:12345", exprs.NFT_FAMILY_IP, "to 127.0.0.1:12345", nil, 4, []interface{}{ &expr.Immediate{ Register: 1, Data: net.ParseIP("127.0.0.1").To4(), }, &expr.Immediate{ Register: uint32(1), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.TProxy{ Family: byte(nftables.TableFamilyIPv4), TableFamily: byte(nftables.TableFamilyIPv4), RegPort: 1, }, &expr.Verdict{ Kind: expr.VerdictAccept, }, }, false, }, { "test-nat-tproxy-to-:12345", exprs.NFT_FAMILY_IP, "to :12345", nil, 3, []interface{}{ &expr.Immediate{ Register: uint32(1), Data: binaryutil.BigEndian.PutUint16(uint16(12345)), }, &expr.TProxy{ Family: byte(nftables.TableFamilyIPv4), TableFamily: byte(nftables.TableFamilyIPv4), RegPort: 1, }, &expr.Verdict{ Kind: expr.VerdictAccept, }, }, false, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { verdExpr := exprs.NewExprVerdict(exprs.VERDICT_TPROXY, test.Parms) if !test.ExpectedFail && verdExpr == nil { t.Errorf("error creating verdict") } else if test.ExpectedFail && verdExpr == nil { return } r, _ := nftest.AddTestDNATRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule") return } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/notrack.go������������������������������������������0000664�0000000�0000000�00000000304�15003540030�0023663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import "github.com/google/nftables/expr" // NewNoTrack adds a new expression not to track connections. func NewNoTrack() *[]expr.Any { return &[]expr.Any{ &expr.Notrack{}, } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/operator.go�����������������������������������������0000664�0000000�0000000�00000001107�15003540030�0024057�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "github.com/google/nftables/expr" ) // NewOperator translates a string comparator operator to nftables operator func NewOperator(operator string) expr.CmpOp { switch operator { case "!=": return expr.CmpOpNeq case ">": return expr.CmpOpGt case ">=": return expr.CmpOpGte case "<": return expr.CmpOpLt case "<=": return expr.CmpOpLte } return expr.CmpOpEq } // NewExprOperator returns a new comparator operator func NewExprOperator(op expr.CmpOp) *[]expr.Any { return &[]expr.Any{ &expr.Cmp{ Register: 1, Op: op, }, } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/port.go���������������������������������������������0000664�0000000�0000000�00000004321�15003540030�0023211�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "strconv" "strings" "github.com/google/nftables" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" ) // NewExprPort returns a new port expression with the given matching operator. func NewExprPort(port string, op *expr.CmpOp) (*[]expr.Any, error) { eport, err := strconv.Atoi(port) if err != nil { return nil, err } return &[]expr.Any{ &expr.Cmp{ Register: 1, Op: *op, Data: binaryutil.BigEndian.PutUint16(uint16(eport))}, }, nil } // NewExprPortRange returns a new port range expression. func NewExprPortRange(sport string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { ports := strings.Split(sport, "-") iport, err := strconv.Atoi(ports[0]) if err != nil { return nil, err } eport, err := strconv.Atoi(ports[1]) if err != nil { return nil, err } return &[]expr.Any{ &expr.Range{ Op: *cmpOp, Register: 1, FromData: binaryutil.BigEndian.PutUint16(uint16(iport)), ToData: binaryutil.BigEndian.PutUint16(uint16(eport)), }, }, nil } // NewExprPortSet returns a new set of ports. func NewExprPortSet(portv string) *[]nftables.SetElement { setElements := []nftables.SetElement{} ports := strings.Split(portv, ",") for _, portv := range ports { portExpr := exprPortSubSet(portv) if portExpr != nil { setElements = append(setElements, *portExpr...) } } return &setElements } func exprPortSubSet(portv string) *[]nftables.SetElement { port, err := strconv.Atoi(portv) if err != nil { return nil } return &[]nftables.SetElement{ {Key: binaryutil.BigEndian.PutUint16(uint16(port))}, } } // NewExprPortDirection returns a new expression to match connections based on // the direction of the connection (source, dest) func NewExprPortDirection(direction string) (*expr.Payload, error) { switch direction { case NFT_DPORT: return &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 2, Len: 2, }, nil case NFT_SPORT: return &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 0, Len: 2, }, nil default: return nil, fmt.Errorf("Not valid protocol direction: %s", direction) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/port_test.go����������������������������������������0000664�0000000�0000000�00000006244�15003540030�0024256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "bytes" "fmt" "reflect" "testing" exprs "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" ) type portTestsT struct { port string portVal int cmp expr.CmpOp shouldFail bool } func TestExprPort(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn portTests := []portTestsT{ {"53", 53, expr.CmpOpEq, false}, {"80", 80, expr.CmpOpEq, false}, {"65535", 65535, expr.CmpOpEq, false}, {"45,", 0, expr.CmpOpEq, true}, {"", 0, expr.CmpOpEq, true}, } for _, test := range portTests { t.Run(fmt.Sprint("test-", test.port), func(t *testing.T) { portExpr, err := exprs.NewExprPort(test.port, &test.cmp) if err != nil { if !test.shouldFail { t.Errorf("Error creating expr port: %v, %s", test, err) } return } //fmt.Printf("%s, %+v\n", test.port, *portExpr) r, _ := nftest.AddTestRule(t, conn, portExpr) if r == nil { t.Errorf("Error adding rule with port (%s) expression", test.port) } e := r.Exprs[0] cmp, ok := e.(*expr.Cmp) if !ok { t.Errorf("%s - invalid port expr: %T", test.port, e) } //fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) if reflect.TypeOf(e).String() != "*expr.Cmp" { t.Errorf("%s - first expression should be *expr.Cmp, instead of: %s", test.port, reflect.TypeOf(e)) } portVal := binaryutil.BigEndian.PutUint16(uint16(test.portVal)) if !bytes.Equal(cmp.Data, portVal) { t.Errorf("%s - invalid port in expr.Cmp: %d", test.port, cmp.Data) } }) } } func TestExprPortRange(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn portTests := []portTestsT{ {"53-5353", 53, expr.CmpOpEq, false}, {"80-8080", 80, expr.CmpOpEq, false}, {"1-65535", 65535, expr.CmpOpEq, false}, {"1,45,", 0, expr.CmpOpEq, true}, {"1-2.", 0, expr.CmpOpEq, true}, } for _, test := range portTests { t.Run(fmt.Sprint("test-", test.port), func(t *testing.T) { portExpr, err := exprs.NewExprPortRange(test.port, &test.cmp) if err != nil { if !test.shouldFail { t.Errorf("Error creating expr port range: %v, %s", test, err) } return } //fmt.Printf("%s, %+v\n", test.port, *portExpr) r, _ := nftest.AddTestRule(t, conn, portExpr) if r == nil { t.Errorf("Error adding rule with port range (%s) expression", test.port) } e := r.Exprs[0] _, ok := e.(*expr.Range) if !ok { t.Errorf("%s - invalid port range expr: %T", test.port, e) } fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) if reflect.TypeOf(e).String() != "*expr.Range" { t.Errorf("%s - first expression should be *expr.Cmp, instead of: %s", test.port, reflect.TypeOf(e)) } /*portVal := binaryutil.BigEndian.PutUint16(uint16(test.portVal)) if !bytes.Equal(range.FromData, portVal) { t.Errorf("%s - invalid port range in expr.Cmp: %d", test.port, cmp.Data) }*/ }) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/protocol.go�����������������������������������������0000664�0000000�0000000�00000005361�15003540030�0024073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "strings" "github.com/google/nftables" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) // NewExprProtocol creates a new expression to filter connections by protocol func NewExprProtocol(proto string) (*[]expr.Any, error) { protoExpr := expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1} switch strings.ToLower(proto) { case NFT_META_L4PROTO: return &[]expr.Any{ &protoExpr, }, nil case NFT_PROTO_UDP: return &[]expr.Any{ &protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_UDP}, }, }, nil case NFT_PROTO_TCP: return &[]expr.Any{ &protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_TCP}, }, }, nil case NFT_PROTO_UDPLITE: return &[]expr.Any{ &protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_UDPLITE}, }, }, nil case NFT_PROTO_SCTP: return &[]expr.Any{ &protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_SCTP}, }, }, nil case NFT_PROTO_DCCP: return &[]expr.Any{ &protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_DCCP}, }, }, nil case NFT_PROTO_ICMP: return &[]expr.Any{ &protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_ICMP}, }, }, nil case NFT_PROTO_ICMPv6: return &[]expr.Any{ &protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_ICMPV6}, }, }, nil /*TODO: could be simplified default: proto, err := getProtocolCode(value) if err != nil { return nil, err } return &[]expr.Any{ protoExpr, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(proto)}, }, }, nil */ default: return nil, fmt.Errorf("Not valid protocol rule, invalid or not supported protocol: %s", proto) } } // NewExprProtoSet creates a new list of SetElements{}, to match // multiple protocol values. func NewExprProtoSet(l4prots string) *[]nftables.SetElement { protoList := strings.Split(l4prots, ",") protoSet := []nftables.SetElement{} for _, name := range protoList { pcode, err := getProtocolCode(name) if err != nil { continue } protoSet = append(protoSet, []nftables.SetElement{ {Key: []byte{byte(pcode)}}, }...) } return &protoSet } // NewExprL4Proto returns a new expression to match a protocol. func NewExprL4Proto(name string, cmpOp *expr.CmpOp) *[]expr.Any { proto, _ := getProtocolCode(name) return &[]expr.Any{ &expr.Cmp{ Op: *cmpOp, Register: 1, Data: []byte{byte(proto)}, }, } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/protocol_test.go������������������������������������0000664�0000000�0000000�00000004504�15003540030�0025130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "fmt" "reflect" "testing" exprs "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) func TestExprProtocol(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn testProtos := []string{ exprs.NFT_PROTO_TCP, exprs.NFT_PROTO_UDP, exprs.NFT_PROTO_UDPLITE, exprs.NFT_PROTO_SCTP, exprs.NFT_PROTO_DCCP, exprs.NFT_PROTO_ICMP, exprs.NFT_PROTO_ICMPv6, } protoValues := []byte{ unix.IPPROTO_TCP, unix.IPPROTO_UDP, unix.IPPROTO_UDPLITE, unix.IPPROTO_SCTP, unix.IPPROTO_DCCP, unix.IPPROTO_ICMP, unix.IPPROTO_ICMPV6, } for idx, proto := range testProtos { t.Run(fmt.Sprint("test-protoExpr-", proto), func(t *testing.T) { protoExpr, err := exprs.NewExprProtocol(proto) if err != nil { t.Errorf("%s - Error creating expr Log: %s", proto, protoExpr) return } r, _ := nftest.AddTestRule(t, conn, protoExpr) if r == nil { t.Errorf("Error adding rule with proto %s expression", proto) } if len(r.Exprs) != 2 { t.Errorf("%s - expected 2 Expressions, found %d", proto, len(r.Exprs)) } e := r.Exprs[0] meta, ok := e.(*expr.Meta) if !ok { t.Errorf("%s - invalid proto expr: %T", proto, e) } //fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) if reflect.TypeOf(e).String() != "*expr.Meta" { t.Errorf("%s - first expression should be *expr.Meta, instead of: %s", proto, reflect.TypeOf(e)) } if meta.Key != expr.MetaKeyL4PROTO { t.Errorf("%s - invalid proto expr.Meta.Key: %d", proto, expr.MetaKeyL4PROTO) } e = r.Exprs[1] cmp, ok := e.(*expr.Cmp) if !ok { t.Errorf("%s - invalid proto cmp expr: %T", proto, e) } //fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) if reflect.TypeOf(e).String() != "*expr.Cmp" { t.Errorf("%s - second expression should be *expr.Cmp, instead of: %s", proto, reflect.TypeOf(e)) } if cmp.Op != expr.CmpOpEq { t.Errorf("%s - expr.Cmp should be CmpOpEq, instead of: %d", proto, cmp.Op) } if cmp.Data[0] != protoValues[idx] { t.Errorf("%s - expr.Data differs: %d<->%d", proto, cmp.Data, protoValues[idx]) } }) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/quota.go��������������������������������������������0000664�0000000�0000000�00000003210�15003540030�0023352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "fmt" "strconv" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/google/nftables/expr" ) // NewQuota returns a new quota expression. // TODO: named quotas func NewQuota(opts []*config.ExprValues) (*[]expr.Any, error) { over := false bytes := int64(0) used := int64(0) for _, opt := range opts { switch opt.Key { case NFT_QUOTA_OVER: over = true case NFT_QUOTA_UNIT_BYTES: b, err := strconv.ParseInt(opt.Value, 10, 64) if err != nil { return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) } bytes = b case NFT_QUOTA_USED: // TODO: support for other size units b, err := strconv.ParseInt(opt.Value, 10, 64) if err != nil { return nil, fmt.Errorf("invalid quota initial consumed bytes: %s", opt.Value) } used = b case NFT_QUOTA_UNIT_KB: b, err := strconv.ParseInt(opt.Value, 10, 64) if err != nil { return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) } bytes = b * 1024 case NFT_QUOTA_UNIT_MB: b, err := strconv.ParseInt(opt.Value, 10, 64) if err != nil { return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) } bytes = (b * 1024) * 1024 case NFT_QUOTA_UNIT_GB: b, err := strconv.ParseInt(opt.Value, 10, 64) if err != nil { return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) } bytes = ((b * 1024) * 1024) * 1024 default: return nil, fmt.Errorf("invalid quota key: %s", opt.Key) } } if bytes == 0 { return nil, fmt.Errorf("quota bytes cannot be 0") } return &[]expr.Any{ &expr.Quota{ Bytes: uint64(bytes), Consumed: uint64(used), Over: over, }, }, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/quota_test.go���������������������������������������0000664�0000000�0000000�00000010375�15003540030�0024423�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "testing" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables/expr" ) func TestExprQuota(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn tests := []nftest.TestsT{ { "test-quota-over-bytes-12345", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_OVER, Value: "", }, &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_BYTES, Value: "12345", }, }, 1, []interface{}{ &expr.Quota{ Bytes: uint64(12345), Consumed: 0, Over: true, }, }, false, }, { "test-quota-over-kbytes-1", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_OVER, Value: "", }, &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_KB, Value: "1", }, }, 1, []interface{}{ &expr.Quota{ Bytes: uint64(1024), Consumed: 0, Over: true, }, }, false, }, { "test-quota-over-mbytes-1", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_OVER, Value: "", }, &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_MB, Value: "1", }, }, 1, []interface{}{ &expr.Quota{ Bytes: uint64(1024 * 1024), Consumed: 0, Over: true, }, }, false, }, { "test-quota-over-gbytes-1", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_OVER, Value: "", }, &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_GB, Value: "1", }, }, 1, []interface{}{ &expr.Quota{ Bytes: uint64(1024 * 1024 * 1024), Consumed: 0, Over: true, }, }, false, }, { "test-quota-until-gbytes-1", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_GB, Value: "1", }, }, 1, []interface{}{ &expr.Quota{ Bytes: uint64(1024 * 1024 * 1024), Consumed: 0, Over: false, }, }, false, }, { "test-quota-consumed-bytes-1024", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_GB, Value: "1", }, &config.ExprValues{ Key: exprs.NFT_QUOTA_USED, Value: "1024", }, }, 1, []interface{}{ &expr.Quota{ Bytes: uint64(1024 * 1024 * 1024), Consumed: 1024, Over: false, }, }, false, }, { "test-invalid-quota-key", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: "gbyte", Value: "1", }, }, 1, []interface{}{}, true, }, { "test-invalid-quota-value", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_GB, Value: "1a", }, }, 1, []interface{}{}, true, }, { "test-invalid-quota-value", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_GB, Value: "", }, }, 1, []interface{}{}, true, }, { "test-invalid-quota-bytes-0", "", // family "", // parms []*config.ExprValues{ &config.ExprValues{ Key: exprs.NFT_QUOTA_UNIT_GB, Value: "0", }, }, 1, []interface{}{}, true, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { quotaExpr, err := exprs.NewQuota(test.Values) if err != nil && !test.ExpectedFail { t.Errorf("Error creating expr Quota: %s", quotaExpr) return } else if err != nil && test.ExpectedFail { return } r, _ := nftest.AddTestRule(t, conn, quotaExpr) if r == nil && !test.ExpectedFail { t.Error("Error adding rule with Quota expression") } if !nftest.AreExprsValid(t, &test, r) { return } if test.ExpectedFail { t.Errorf("test should have failed") } }) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/utils.go��������������������������������������������0000664�0000000�0000000�00000012620�15003540030�0023366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "strconv" "github.com/google/gopacket/layers" "golang.org/x/sys/unix" ) // GetICMPRejectCode returns the code by its name. func GetICMPRejectCode(reason string) uint8 { switch reason { case ICMP_HOST_UNREACHABLE, ICMP_ADDR_UNREACHABLE: return layers.ICMPv4CodeHost case ICMP_PROT_UNREACHABLE: return layers.ICMPv4CodeProtocol case ICMP_PORT_UNREACHABLE: return layers.ICMPv4CodePort case ICMP_ADMIN_PROHIBITED: return layers.ICMPv4CodeCommAdminProhibited case ICMP_HOST_PROHIBITED: return layers.ICMPv4CodeHostAdminProhibited case ICMP_NET_PROHIBITED: return layers.ICMPv4CodeNetAdminProhibited } return layers.ICMPv4CodeNet } // GetICMPxRejectCode returns the code by its name. func GetICMPxRejectCode(reason string) uint8 { // https://github.com/torvalds/linux/blob/master/net/netfilter/nft_reject.c#L96 // https://github.com/google/gopacket/blob/3aa782ce48d4a525acaebab344cedabfb561f870/layers/icmp4.go#L37 switch reason { case ICMP_HOST_UNREACHABLE, ICMP_NET_UNREACHABLE: return unix.NFT_REJECT_ICMP_UNREACH // results in -> net-unreachable??? case ICMP_PROT_UNREACHABLE: return unix.NFT_REJECT_ICMPX_HOST_UNREACH // results in -> prot-unreachable??? case ICMP_PORT_UNREACHABLE: return unix.NFT_REJECT_ICMPX_PORT_UNREACH // results in -> host-unreachable??? case ICMP_NO_ROUTE: return unix.NFT_REJECT_ICMPX_NO_ROUTE // results in -> net-unreachable } return unix.NFT_REJECT_ICMP_UNREACH // results in -> net-unreachable??? } // GetICMPType returns an ICMP type code func GetICMPType(icmpType string) uint8 { switch icmpType { case ICMP_ECHO_REPLY: return layers.ICMPv4TypeEchoReply case ICMP_ECHO_REQUEST: return layers.ICMPv4TypeEchoRequest case ICMP_SOURCE_QUENCH: return layers.ICMPv4TypeSourceQuench case ICMP_DEST_UNREACHABLE: return layers.ICMPv4TypeDestinationUnreachable case ICMP_ROUTER_ADVERTISEMENT: return layers.ICMPv4TypeRouterAdvertisement case ICMP_ROUTER_SOLICITATION: return layers.ICMPv4TypeRouterSolicitation case ICMP_REDIRECT: return layers.ICMPv4TypeRedirect case ICMP_TIME_EXCEEDED: return layers.ICMPv4TypeTimeExceeded case ICMP_INFO_REQUEST: return layers.ICMPv4TypeInfoRequest case ICMP_INFO_REPLY: return layers.ICMPv4TypeInfoReply case ICMP_PARAMETER_PROBLEM: return layers.ICMPv4TypeParameterProblem case ICMP_TIMESTAMP_REQUEST: return layers.ICMPv4TypeTimestampRequest case ICMP_TIMESTAMP_REPLY: return layers.ICMPv4TypeTimestampReply case ICMP_ADDRESS_MASK_REQUEST: return layers.ICMPv4TypeAddressMaskRequest case ICMP_ADDRESS_MASK_REPLY: return layers.ICMPv4TypeAddressMaskReply } return 0 } // GetICMPv6Type returns an ICMPv6 type code func GetICMPv6Type(icmpType string) uint8 { switch icmpType { case ICMP_DEST_UNREACHABLE: return layers.ICMPv6TypeDestinationUnreachable case ICMP_PACKET_TOO_BIG: return layers.ICMPv6TypePacketTooBig case ICMP_TIME_EXCEEDED: return layers.ICMPv6TypeTimeExceeded case ICMP_PARAMETER_PROBLEM: return layers.ICMPv6TypeParameterProblem case ICMP_ECHO_REQUEST: return layers.ICMPv6TypeEchoRequest case ICMP_ECHO_REPLY: return layers.ICMPv6TypeEchoReply case ICMP_ROUTER_SOLICITATION: return layers.ICMPv6TypeRouterSolicitation case ICMP_ROUTER_ADVERTISEMENT: return layers.ICMPv6TypeRouterAdvertisement case ICMP_NEIGHBOUR_SOLICITATION: return layers.ICMPv6TypeNeighborSolicitation case ICMP_NEIGHBOUR_ADVERTISEMENT: return layers.ICMPv6TypeNeighborAdvertisement case ICMP_REDIRECT: return layers.ICMPv6TypeRedirect } return 0 } // GetICMPv6RejectCode returns the code by its name. func GetICMPv6RejectCode(reason string) uint8 { switch reason { case ICMP_HOST_UNREACHABLE, ICMP_NET_UNREACHABLE, ICMP_NO_ROUTE: return layers.ICMPv6CodeNoRouteToDst case ICMP_ADDR_UNREACHABLE: return layers.ICMPv6CodeAddressUnreachable case ICMP_PORT_UNREACHABLE: return layers.ICMPv6CodePortUnreachable case ICMP_REJECT_POLICY_FAIL: return layers.ICMPv6CodeSrcAddressFailedPolicy case ICMP_REJECT_ROUTE: return layers.ICMPv6CodeRejectRouteToDst } return layers.ICMPv6CodeNoRouteToDst } // getProtocolCode will try to return the code of the given protocol. // If the protocol is not in our list, we'll use the value as decimal. // So for example IPPROTO_ENCAP (0x62) must be specified as 98. // https://pkg.go.dev/golang.org/x/sys/unix#pkg-constants func getProtocolCode(value string) (byte, error) { switch value { case NFT_PROTO_TCP: return unix.IPPROTO_TCP, nil case NFT_PROTO_UDP: return unix.IPPROTO_UDP, nil case NFT_PROTO_UDPLITE: return unix.IPPROTO_UDPLITE, nil case NFT_PROTO_SCTP: return unix.IPPROTO_SCTP, nil case NFT_PROTO_DCCP: return unix.IPPROTO_DCCP, nil case NFT_PROTO_ICMP: return unix.IPPROTO_ICMP, nil case NFT_PROTO_ICMPv6: return unix.IPPROTO_ICMPV6, nil case NFT_PROTO_AH: return unix.IPPROTO_AH, nil case NFT_PROTO_ETHERNET: return unix.IPPROTO_ETHERNET, nil case NFT_PROTO_GRE: return unix.IPPROTO_GRE, nil case NFT_PROTO_IP: return unix.IPPROTO_IP, nil case NFT_PROTO_IPIP: return unix.IPPROTO_IPIP, nil case NFT_PROTO_L2TP: return unix.IPPROTO_L2TP, nil case NFT_PROTO_COMP: return unix.IPPROTO_COMP, nil case NFT_PROTO_IGMP: return unix.IPPROTO_IGMP, nil case NFT_PROTO_ESP: return unix.IPPROTO_ESP, nil case NFT_PROTO_RAW: return unix.IPPROTO_RAW, nil case NFT_PROTO_ENCAP: return unix.IPPROTO_ENCAP, nil } prot, err := strconv.Atoi(value) if err != nil { return 0, err } return byte(prot), nil } ����������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/verdict.go������������������������������������������0000664�0000000�0000000�00000011513�15003540030�0023666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs import ( "strconv" "strings" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) // NewExprVerdict constructs a new verdict to apply on connections. func NewExprVerdict(verdict, parms string) *[]expr.Any { switch strings.ToLower(verdict) { case VERDICT_ACCEPT: return NewExprAccept() case VERDICT_DROP: return &[]expr.Any{&expr.Verdict{ Kind: expr.VerdictDrop, }} // FIXME: this verdict is not added to nftables case VERDICT_STOP: return &[]expr.Any{&expr.Verdict{ Kind: expr.VerdictStop, }} case VERDICT_REJECT: reject := NewExprReject(parms) return &[]expr.Any{reject} case VERDICT_RETURN: return &[]expr.Any{&expr.Verdict{ Kind: expr.VerdictReturn, }} case VERDICT_JUMP: return &[]expr.Any{ &expr.Verdict{ Kind: expr.VerdictKind(unix.NFT_JUMP), Chain: parms, }, } case VERDICT_QUEUE: queueNum := 0 var err error p := strings.Split(parms, " ") if len(p) == 0 { log.Warning("invalid Queue expr parameters") return nil } // TODO: allow to configure this flag if p[0] == NFT_QUEUE_NUM { queueNum, err = strconv.Atoi(p[len(p)-1]) if err != nil { log.Warning("invalid Queue num: %s", err) return nil } } return &[]expr.Any{ &expr.Queue{ Num: uint16(queueNum), Flag: expr.QueueFlagBypass, }} case VERDICT_SNAT: snat := NewExprSNAT() snat.Random, snat.FullyRandom, snat.Persistent = NewExprNATFlags(parms) snatExpr := &[]expr.Any{snat} regAddr, regProto, natParms, err := NewExprNAT(parms, VERDICT_SNAT) if err != nil { log.Warning("error adding snat verdict: %s", err) return nil } if regAddr { snat.RegAddrMin = 1 } if regProto { snat.RegProtoMin = 2 } *snatExpr = append(*natParms, *snatExpr...) return snatExpr case VERDICT_DNAT: dnat := NewExprDNAT() dnat.Random, dnat.FullyRandom, dnat.Persistent = NewExprNATFlags(parms) dnatExpr := &[]expr.Any{dnat} regAddr, regProto, natParms, err := NewExprNAT(parms, VERDICT_DNAT) if err != nil { log.Warning("error adding dnat verdict: %s", err) return nil } if regAddr { dnat.RegAddrMin = 1 } if regProto { dnat.RegProtoMin = 2 } *dnatExpr = append(*natParms, *dnatExpr...) return dnatExpr case VERDICT_MASQUERADE: m := &expr.Masq{} m.Random, m.FullyRandom, m.Persistent = NewExprNATFlags(parms) masqExpr := &[]expr.Any{m} if parms == "" { return masqExpr } // if any of the flag is set to true, toPorts must be false toPorts := !(m.Random == true || m.FullyRandom == true || m.Persistent == true) masqExpr = NewExprMasquerade(toPorts, m.Random, m.FullyRandom, m.Persistent) _, _, natParms, err := NewExprNAT(parms, VERDICT_MASQUERADE) if err != nil { log.Warning("error adding masquerade verdict: %s", err) } *masqExpr = append(*natParms, *masqExpr...) return masqExpr case VERDICT_REDIRECT: _, _, rewriteParms, err := NewExprNAT(parms, VERDICT_REDIRECT) if err != nil { log.Warning("error adding redirect verdict: %s", err) return nil } redirExpr := NewExprRedirect() *redirExpr = append(*rewriteParms, *redirExpr...) return redirExpr case VERDICT_TPROXY: _, _, rewriteParms, err := NewExprNAT(parms, VERDICT_TPROXY) if err != nil { log.Warning("error adding tproxy verdict: %s", err) return nil } tproxyExpr := &[]expr.Any{} *tproxyExpr = append(*tproxyExpr, *rewriteParms...) tVerdict := NewExprTproxy() *tproxyExpr = append(*tproxyExpr, *tVerdict...) *tproxyExpr = append(*tproxyExpr, *NewExprAccept()...) return tproxyExpr } // target can be empty, "ct set mark" or "log" for example return &[]expr.Any{} } // NewExprAccept creates the accept verdict. func NewExprAccept() *[]expr.Any { return &[]expr.Any{&expr.Verdict{ Kind: expr.VerdictAccept, }} } // NewExprReject creates new Reject expression // icmpx, to reject the IPv4 and IPv6 traffic, icmp for ipv4, icmpv6 for ... // Ex.: "Target": "reject", "TargetParameters": "with tcp reset" // https://wiki.nftables.org/wiki-nftables/index.php/Rejecting_traffic func NewExprReject(parms string) *expr.Reject { reject := &expr.Reject{} reject.Code = unix.NFT_REJECT_ICMP_UNREACH reject.Type = unix.NFT_REJECT_ICMP_UNREACH parmList := strings.Split(parms, " ") length := len(parmList) if length <= 1 { return reject } what := parmList[1] how := parmList[length-1] switch what { case NFT_PROTO_TCP: reject.Type = unix.NFT_REJECT_TCP_RST reject.Code = unix.NFT_REJECT_TCP_RST case NFT_PROTO_ICMP: reject.Type = unix.NFT_REJECT_ICMP_UNREACH reject.Code = GetICMPRejectCode(how) return reject case NFT_PROTO_ICMPX: // icmp and icmpv6 reject.Type = unix.NFT_REJECT_ICMPX_UNREACH reject.Code = GetICMPxRejectCode(how) return reject case NFT_PROTO_ICMPv6: reject.Type = 1 reject.Code = GetICMPv6RejectCode(how) default: } return reject } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/exprs/verdict_test.go�������������������������������������0000664�0000000�0000000�00000021263�15003540030�0024730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package exprs_test import ( "fmt" "reflect" "testing" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) type verdictTestsT struct { name string verdict string parms string expectedExpr string expectedKind expr.VerdictKind } func TestExprVerdict(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn // we must create a custom chain before using JUMP verdict. tbl, _ := nftest.Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) nftest.Fw.Conn.AddChain(&nftables.Chain{ Name: "custom-chain", Table: tbl, }) nftest.Fw.Commit() verdictTests := []verdictTestsT{ {"test-accept", exprs.VERDICT_ACCEPT, "", "*expr.Verdict", expr.VerdictAccept}, {"test-AcCept", "AcCePt", "", "*expr.Verdict", expr.VerdictAccept}, {"test-ACCEPT", "ACCEPT", "", "*expr.Verdict", expr.VerdictAccept}, {"test-drop", exprs.VERDICT_DROP, "", "*expr.Verdict", expr.VerdictDrop}, //{"test-stop", exprs.VERDICT_STOP, "", "*expr.Verdict", expr.VerdictStop}, {"test-return", exprs.VERDICT_RETURN, "", "*expr.Verdict", expr.VerdictReturn}, {"test-jump", exprs.VERDICT_JUMP, "custom-chain", "*expr.Verdict", expr.VerdictJump}, // empty verdict must be valid at this level. // it can be used with "log" or "ct set mark" {"test-empty-verdict", "", "", "*expr.Verdict", expr.VerdictAccept}, } for _, test := range verdictTests { t.Run(test.name, func(t *testing.T) { verdExpr := exprs.NewExprVerdict(test.verdict, test.parms) r, _ := nftest.AddTestRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule with verdict expression %s", test.verdict) return } if test.name == "test-empty-verdict" { return } e := r.Exprs[0] if reflect.TypeOf(e).String() != test.expectedExpr { t.Errorf("first expression should be *expr.Verdict, instead of: %s", reflect.TypeOf(e)) return } verd, ok := e.(*expr.Verdict) if !ok { t.Errorf("invalid verdict: %T", e) return } if verd.Kind != test.expectedKind { t.Errorf("invalid verdict kind: %+v, expected: %+v", verd.Kind, test.expectedKind) return } }) } } func TestExprVerdictReject(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn type rejectTests struct { name string parms string what string family string parmType byte parmCode byte } tests := []rejectTests{ { "test-reject-tcp-RST", "with tcp reset", exprs.NFT_PROTO_TCP, exprs.NFT_FAMILY_INET, unix.NFT_REJECT_TCP_RST, unix.NFT_REJECT_TCP_RST, }, { "test-reject-icmp-host-unreachable", fmt.Sprint("with icmp ", exprs.ICMP_HOST_UNREACHABLE), exprs.NFT_FAMILY_IP, exprs.NFT_PROTO_ICMP, unix.NFT_REJECT_ICMP_UNREACH, exprs.GetICMPRejectCode(exprs.ICMP_HOST_UNREACHABLE), }, { "test-reject-icmp-addr-unreachable", fmt.Sprint("with icmp ", exprs.ICMP_ADDR_UNREACHABLE), exprs.NFT_FAMILY_IP, exprs.NFT_PROTO_ICMP, unix.NFT_REJECT_ICMP_UNREACH, exprs.GetICMPRejectCode(exprs.ICMP_ADDR_UNREACHABLE), }, { "test-reject-icmp-prot-unreachable", fmt.Sprint("with icmp ", exprs.ICMP_PROT_UNREACHABLE), exprs.NFT_FAMILY_IP, exprs.NFT_PROTO_ICMP, unix.NFT_REJECT_ICMP_UNREACH, exprs.GetICMPRejectCode(exprs.ICMP_PROT_UNREACHABLE), }, { "test-reject-icmp-port-unreachable", fmt.Sprint("with icmp ", exprs.ICMP_PORT_UNREACHABLE), exprs.NFT_FAMILY_IP, exprs.NFT_PROTO_ICMP, unix.NFT_REJECT_ICMP_UNREACH, exprs.GetICMPRejectCode(exprs.ICMP_PORT_UNREACHABLE), }, { "test-reject-icmp-admin-prohibited", fmt.Sprint("with icmp ", exprs.ICMP_ADMIN_PROHIBITED), exprs.NFT_FAMILY_IP, exprs.NFT_PROTO_ICMP, unix.NFT_REJECT_ICMP_UNREACH, exprs.GetICMPRejectCode(exprs.ICMP_ADMIN_PROHIBITED), }, { "test-reject-icmp-host-prohibited", fmt.Sprint("with icmp ", exprs.ICMP_HOST_PROHIBITED), exprs.NFT_FAMILY_IP, exprs.NFT_PROTO_ICMP, unix.NFT_REJECT_ICMP_UNREACH, exprs.GetICMPRejectCode(exprs.ICMP_HOST_PROHIBITED), }, { "test-reject-icmp-net-prohibited", fmt.Sprint("with icmp ", exprs.ICMP_NET_PROHIBITED), exprs.NFT_FAMILY_IP, exprs.NFT_PROTO_ICMP, unix.NFT_REJECT_ICMP_UNREACH, exprs.GetICMPRejectCode(exprs.ICMP_NET_PROHIBITED), }, // icmpx { "test-reject-icmpx-net-unreachable", fmt.Sprint("with icmpx ", exprs.ICMP_NET_UNREACHABLE), exprs.NFT_FAMILY_INET, exprs.NFT_PROTO_ICMPX, unix.NFT_REJECT_ICMPX_UNREACH, exprs.GetICMPxRejectCode(exprs.ICMP_NET_UNREACHABLE), }, { "test-reject-icmpx-host-unreachable", fmt.Sprint("with icmpx ", exprs.ICMP_HOST_UNREACHABLE), exprs.NFT_FAMILY_INET, exprs.NFT_PROTO_ICMPX, unix.NFT_REJECT_ICMPX_UNREACH, exprs.GetICMPxRejectCode(exprs.ICMP_HOST_UNREACHABLE), }, { "test-reject-icmpx-prot-unreachable", fmt.Sprint("with icmpx ", exprs.ICMP_PROT_UNREACHABLE), exprs.NFT_FAMILY_INET, exprs.NFT_PROTO_ICMPX, unix.NFT_REJECT_ICMPX_UNREACH, exprs.GetICMPxRejectCode(exprs.ICMP_PROT_UNREACHABLE), }, { "test-reject-icmpx-port-unreachable", fmt.Sprint("with icmpx ", exprs.ICMP_PORT_UNREACHABLE), exprs.NFT_FAMILY_INET, exprs.NFT_PROTO_ICMPX, unix.NFT_REJECT_ICMPX_UNREACH, exprs.GetICMPxRejectCode(exprs.ICMP_PORT_UNREACHABLE), }, { "test-reject-icmpx-no-route", fmt.Sprint("with icmpx ", exprs.ICMP_NO_ROUTE), exprs.NFT_FAMILY_INET, exprs.NFT_PROTO_ICMPX, unix.NFT_REJECT_ICMPX_UNREACH, exprs.GetICMPxRejectCode(exprs.ICMP_NO_ROUTE), }, // icmpv6 { "test-reject-icmpv6-net-unreachable", fmt.Sprint("with icmpv6 ", exprs.ICMP_NET_UNREACHABLE), exprs.NFT_FAMILY_IP6, exprs.NFT_PROTO_ICMPv6, 1, exprs.GetICMPv6RejectCode(exprs.ICMP_NET_UNREACHABLE), }, { "test-reject-icmpv6-addr-unreachable", fmt.Sprint("with icmpv6 ", exprs.ICMP_ADDR_UNREACHABLE), exprs.NFT_FAMILY_IP6, exprs.NFT_PROTO_ICMPv6, 1, exprs.GetICMPv6RejectCode(exprs.ICMP_ADDR_UNREACHABLE), }, { "test-reject-icmpv6-host-unreachable", fmt.Sprint("with icmpv6 ", exprs.ICMP_HOST_UNREACHABLE), exprs.NFT_FAMILY_IP6, exprs.NFT_PROTO_ICMPv6, 1, exprs.GetICMPv6RejectCode(exprs.ICMP_HOST_UNREACHABLE), }, { "test-reject-icmpv6-port-unreachable", fmt.Sprint("with icmpv6 ", exprs.ICMP_PORT_UNREACHABLE), exprs.NFT_FAMILY_IP6, exprs.NFT_PROTO_ICMPv6, 1, exprs.GetICMPv6RejectCode(exprs.ICMP_PORT_UNREACHABLE), }, { "test-reject-icmpv6-no-route", fmt.Sprint("with icmpv6 ", exprs.ICMP_NO_ROUTE), exprs.NFT_FAMILY_IP6, exprs.NFT_PROTO_ICMPv6, 1, exprs.GetICMPv6RejectCode(exprs.ICMP_NO_ROUTE), }, { "test-reject-icmpv6-reject-policy-fail", fmt.Sprint("with icmpv6 ", exprs.ICMP_REJECT_POLICY_FAIL), exprs.NFT_FAMILY_IP6, exprs.NFT_PROTO_ICMPv6, 1, exprs.GetICMPv6RejectCode(exprs.ICMP_REJECT_POLICY_FAIL), }, { "test-reject-icmpv6-reject-route", fmt.Sprint("with icmpv6 ", exprs.ICMP_REJECT_ROUTE), exprs.NFT_FAMILY_IP6, exprs.NFT_PROTO_ICMPv6, 1, exprs.GetICMPv6RejectCode(exprs.ICMP_REJECT_ROUTE), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { verdExpr := exprs.NewExprVerdict(exprs.VERDICT_REJECT, test.parms) r, _ := nftest.AddTestRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule with reject verdict %s", "") return } e := r.Exprs[0] if reflect.TypeOf(e).String() != "*expr.Reject" { t.Errorf("first expression should be *expr.Verdict, instead of: %s", reflect.TypeOf(e)) return } verd, ok := e.(*expr.Reject) if !ok { t.Errorf("invalid verdict: %T", e) return } //fmt.Printf("reject verd: %+v\n", verd) if verd.Code != uint8(test.parmCode) { t.Errorf("invalid reject verdict code: %d, expected: %d", verd.Code, test.parmCode) } }) } } func TestExprVerdictQueue(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn verdExpr := exprs.NewExprVerdict(exprs.VERDICT_QUEUE, "num 1") r, _ := nftest.AddTestRule(t, conn, verdExpr) if r == nil { t.Errorf("Error adding rule with Queue verdict") return } e := r.Exprs[0] if reflect.TypeOf(e).String() != "*expr.Queue" { t.Errorf("first expression should be *expr.Queue, instead of: %s", reflect.TypeOf(e)) return } verd, ok := e.(*expr.Queue) if !ok { t.Errorf("invalid verdict: %T", e) return } if verd.Num != 1 { t.Errorf("invalid queue verdict Num: %d", verd.Num) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/monitor.go������������������������������������������������0000664�0000000�0000000�00000004345�15003540030�0022561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "time" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" ) // AreRulesLoaded checks if the firewall rules for intercept traffic are loaded. func (n *Nft) AreRulesLoaded() bool { n.Lock() defer n.Unlock() nRules := 0 chains, err := n.Conn.ListChains() if err != nil { log.Warning("[nftables] error listing nftables chains: %s", err) return false } for _, c := range chains { rules, err := n.Conn.GetRule(c.Table, c) if err != nil { log.Warning("[nftables] Error listing rules: %s", err) continue } for rdx, r := range rules { if string(r.UserData) == InterceptionRuleKey { if c.Table.Name == exprs.NFT_CHAIN_FILTER && c.Name == exprs.NFT_HOOK_INPUT && rdx != 0 { log.Warning("nftables DNS rule not in 1st position (%d)", rdx) return false } nRules++ if c.Table.Name == exprs.NFT_CHAIN_MANGLE && rdx < len(rules)-2 { log.Warning("nfables queue rule is not the latest of the list (%d/%d), reloading", rdx, len(rules)) return false } } } } // we expect to have exactly 3 rules (2 queue and 1 dns). If there're less or more, then we // need to reload them. if nRules != 3 { log.Warning("nfables filter rules not loaded: %d", nRules) return false } return true } // ReloadConfCallback gets called after the configuration changes. func (n *Nft) ReloadConfCallback() { log.Important("reloadConfCallback changed, reloading") n.DeleteSystemRules(!common.ForcedDelRules, !common.RestoreChains, log.GetLogLevel() == log.DEBUG) n.AddSystemRules(common.ReloadRules, !common.BackupChains) } // ReloadRulesCallback gets called when the interception rules are not present. func (n *Nft) ReloadRulesCallback() { log.Important("nftables firewall rules changed, reloading") n.DisableInterception(log.GetLogLevel() == log.DEBUG) time.Sleep(time.Millisecond * 500) n.EnableInterception() } // PreloadConfCallback gets called before the fw configuration is loaded func (n *Nft) PreloadConfCallback() { log.Info("nftables config changed, reloading") n.DeleteSystemRules(!common.ForcedDelRules, common.RestoreChains, log.GetLogLevel() == log.DEBUG) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/monitor_test.go�������������������������������������������0000664�0000000�0000000�00000005535�15003540030�0023622�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables_test import ( "testing" "time" "github.com/evilsocket/opensnitch/daemon/firewall/common" nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables" ) // mimic EnableInterception() but without NewRulesChecker() func addInterceptionRules(nft *nftb.Nft, t *testing.T) { if err := nft.AddInterceptionTables(); err != nil { t.Errorf("Error while adding interception tables: %s", err) return } if err := nft.AddInterceptionChains(); err != nil { t.Errorf("Error while adding interception chains: %s", err) return } if err, _ := nft.QueueDNSResponses(common.EnableRule, common.EnableRule); err != nil { t.Errorf("Error while running DNS nftables rule: %s", err) } if err, _ := nft.QueueConnections(common.EnableRule, common.EnableRule); err != nil { t.Errorf("Error while running conntrack nftables rule: %s", err) } } func _testMonitorReload(t *testing.T, conn *nftables.Conn, nft *nftb.Nft) { tblfilter := nft.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) if tblfilter == nil || tblfilter.Name != exprs.NFT_CHAIN_FILTER { t.Error("table filter-inet not in the list") } chnFilterInput := nftest.Fw.GetChain(exprs.NFT_HOOK_INPUT, tblfilter, exprs.NFT_FAMILY_INET) if chnFilterInput == nil { t.Error("chain input-filter-inet not in the list") } rules, _ := conn.GetRules(tblfilter, chnFilterInput) if len(rules) == 0 { t.Error("DNS interception rule not added") } conn.FlushChain(chnFilterInput) nftest.Fw.Commit() // the rules checker checks the rules every 10s reloaded := false for i := 0; i < 15; i++ { if r, _ := getRule(t, conn, tblfilter.Name, exprs.NFT_HOOK_INPUT, nftb.InterceptionRuleKey, 0); r != nil { reloaded = true break } time.Sleep(time.Second) } if !reloaded { t.Error("rules under input-filter-inet not reloaded after 10s") } } func TestAreRulesLoaded(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn addInterceptionRules(nftest.Fw, t) if !nftest.Fw.AreRulesLoaded() { t.Error("interception rules not loaded, and they should") } nftest.Fw.DelInterceptionRules() if nftest.Fw.AreRulesLoaded() { t.Error("interception rules are loaded, and the shouldn't") } } func TestMonitorReload(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn nftest.Fw.EnableInterception() // test that rules are reloaded after being deleted, but also // that the monitor is not stopped after the first reload. _testMonitorReload(t, conn, nftest.Fw) _testMonitorReload(t, conn, nftest.Fw) _testMonitorReload(t, conn, nftest.Fw) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/nftables.go�����������������������������������������������0000664�0000000�0000000�00000012342�15003540030�0022664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "bytes" "encoding/json" "strings" "sync" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/iptables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/ui/protocol" "github.com/golang/protobuf/jsonpb" "github.com/google/nftables" ) // Action is the modifier we apply to a rule. type Action string // Actions we apply to the firewall. const ( fwKey = "opensnitch-key" InterceptionRuleKey = fwKey + "-interception" SystemRuleKey = fwKey + "-system" Name = "nftables" ) var ( filterTable = &nftables.Table{ Family: nftables.TableFamilyINet, Name: exprs.NFT_CHAIN_FILTER, } mangleTable = &nftables.Table{ Family: nftables.TableFamilyINet, Name: exprs.NFT_CHAIN_FILTER, } ) // Nft holds the fields of our nftables firewall type Nft struct { Conn *nftables.Conn chains iptables.SystemChains common.Common config.Config sync.Mutex } // NewNft creates a new nftables object func NewNft() *nftables.Conn { return &nftables.Conn{} } // Fw initializes a new nftables object func Fw() (*Nft, error) { n := &Nft{ chains: iptables.SystemChains{ Rules: make(map[string]*iptables.SystemRule), }, } return n, nil } // Name returns the name of the firewall func (n *Nft) Name() string { return Name } // Init inserts the firewall rules and starts monitoring for firewall // changes. func (n *Nft) Init(qNum *int) { if n.IsRunning() { return } n.ErrChan = make(chan string, 100) InitMapsStore() n.SetQueueNum(qNum) n.Conn = NewNft() // In order to clean up any existing firewall rule before start, // we need to load the fw configuration first to know what rules // were configured. n.NewSystemFwConfig(n.PreloadConfCallback, n.ReloadConfCallback) n.LoadDiskConfiguration(!common.ReloadConf) // start from a clean state // The daemon may have exited unexpectedly, leaving residual fw rules, so we // need to clean them up to avoid duplicated rules. n.DelInterceptionRules() n.AddSystemRules(!common.ReloadRules, common.BackupChains) n.EnableInterception() n.Running = true } // Stop deletes the firewall rules, allowing network traffic. func (n *Nft) Stop() { if n.IsRunning() == false { return } n.StopConfigWatcher() n.StopCheckingRules() n.CleanRules(log.GetLogLevel() == log.DEBUG) n.Lock() n.Running = false n.Unlock() } // EnableInterception adds firewall rules to intercept connections func (n *Nft) EnableInterception() { if err := n.AddInterceptionTables(); err != nil { log.Error("Error while adding interception tables: %s", err) return } if err := n.AddInterceptionChains(); err != nil { log.Error("Error while adding interception chains: %s", err) return } if err, _ := n.QueueDNSResponses(common.EnableRule, common.EnableRule); err != nil { log.Error("Error while running DNS nftables rule: %s", err) } if err, _ := n.QueueConnections(common.EnableRule, common.EnableRule); err != nil { log.Error("Error while running conntrack nftables rule: %s", err) } // start monitoring firewall rules to intercept network traffic. n.NewRulesChecker(n.AreRulesLoaded, n.ReloadRulesCallback) } // DisableInterception removes firewall rules to intercept outbound connections. func (n *Nft) DisableInterception(logErrors bool) { n.StopCheckingRules() n.DelInterceptionRules() } // CleanRules deletes the rules we added. func (n *Nft) CleanRules(logErrors bool) { n.DisableInterception(logErrors) n.DeleteSystemRules(common.ForcedDelRules, common.RestoreChains, logErrors) } // Commit applies the queued changes, creating new objects (tables, chains, etc). // You add rules, chains or tables, and after calling to Flush() they're added to the system. // NOTE: it's very important not to call Flush() without queued tasks. func (n *Nft) Commit() bool { if err := n.Conn.Flush(); err != nil { log.Warning("%s error applying changes: %s", logTag, err) return false } return true } // Serialize converts the configuration from json to protobuf func (n *Nft) Serialize() (*protocol.SysFirewall, error) { sysfw := &protocol.SysFirewall{} jun := jsonpb.Unmarshaler{ AllowUnknownFields: true, } rawConfig, err := json.Marshal(&n.SysConfig) if err != nil { log.Error("nftables.Serialize() struct to string error: %s", err) return nil, err } // string to proto if err := jun.Unmarshal(strings.NewReader(string(rawConfig)), sysfw); err != nil { log.Error("nftables.Serialize() string to protobuf error: %s", err) return nil, err } return sysfw, nil } // Deserialize converts a protocolbuffer structure to byte array. func (n *Nft) Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) { jun := jsonpb.Marshaler{ OrigName: true, EmitDefaults: true, Indent: " ", } // NOTE: '<' and '>' characters are encoded to unicode (\u003c). // This has no effect on adding rules to nftables. // Users can still write "<" if they want to, rules are added ok. var b bytes.Buffer if err := jun.Marshal(&b, sysfw); err != nil { log.Error("nfables.Deserialize() error 2: %s", err) return nil, err } return b.Bytes(), nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/nftest/���������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022040�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/nftest/nftest.go������������������������������������������0000664�0000000�0000000�00000003354�15003540030�0023677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftest import ( "os" "runtime" "testing" nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" "github.com/google/nftables" "github.com/vishvananda/netns" ) var ( conn *nftables.Conn newNS netns.NsHandle // Fw represents the nftables Fw object. Fw, _ = nftb.Fw() ) func init() { nftb.InitMapsStore() } // SkipIfNotPrivileged will skip the test from where it's invoked, // to skip the test if we don't have root privileges. // This may occur when executing the tests on restricted environments, // such as containers, chroots, etc. func SkipIfNotPrivileged(t *testing.T) { if os.Getenv("PRIVILEGED_TESTS") == "" { t.Skip("Set PRIVILEGED_TESTS to 1 to launch these tests, and launch them as root, or as a user allowed to create new namespaces.") } } // OpenSystemConn opens a new connection with the kernel in a new namespace. // https://github.com/google/nftables/blob/8f2d395e1089dea4966c483fbeae7e336917c095/internal/nftest/system_conn.go#L15 func OpenSystemConn(t *testing.T) (*nftables.Conn, netns.NsHandle) { t.Helper() // We lock the goroutine into the current thread, as namespace operations // such as those invoked by `netns.New()` are thread-local. This is undone // in nftest.CleanupSystemConn(). runtime.LockOSThread() ns, err := netns.New() if err != nil { t.Fatalf("netns.New() failed: %v", err) } t.Log("OpenSystemConn() with NS:", ns) c, err := nftables.New(nftables.WithNetNSFd(int(ns))) if err != nil { t.Fatalf("nftables.New() failed: %v", err) } return c, ns } // CleanupSystemConn closes the given namespace. func CleanupSystemConn(t *testing.T, newNS netns.NsHandle) { defer runtime.UnlockOSThread() if err := newNS.Close(); err != nil { t.Fatalf("newNS.Close() failed: %v", err) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/nftest/test_utils.go��������������������������������������0000664�0000000�0000000�00000015306�15003540030�0024573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftest import ( "bytes" "reflect" "testing" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/google/nftables" "github.com/google/nftables/expr" ) // TestsT defines the fields of a test. type TestsT struct { Name string Family string Parms string Values []*config.ExprValues ExpectedExprsNum int ExpectedExprs []interface{} ExpectedFail bool } // AreExprsValid checks if the expressions defined in the given rule are valid // according to the expected expressions defined in the tests. func AreExprsValid(t *testing.T, test *TestsT, rule *nftables.Rule) bool { total := len(rule.Exprs) if total != test.ExpectedExprsNum { t.Errorf("expected %d expressions, found %d", test.ExpectedExprsNum, total) return false } for idx, e := range rule.Exprs { if reflect.TypeOf(e).String() != reflect.TypeOf(test.ExpectedExprs[idx]).String() { t.Errorf("first expression should be %s, instead of: %s", reflect.TypeOf(test.ExpectedExprs[idx]), reflect.TypeOf(e)) return false } switch e.(type) { case *expr.Meta: lExpr, ok := e.(*expr.Meta) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Meta) if !ok || !okExpected { t.Errorf("invalid Meta expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.Key != lExpect.Key || lExpr.Register != lExpect.Register { t.Errorf("invalid Meta.Key,\ngot: %+v\nexpected: %+v\n", lExpr.Key, lExpect.Key) } if lExpr.SourceRegister != lExpect.SourceRegister { t.Errorf("invalid Meta.SourceRegister,\ngot: %+v\nexpected: %+v\n", lExpr.SourceRegister, lExpect.SourceRegister) } if lExpr.Register != lExpect.Register { t.Errorf("invalid Meta.Register,\ngot: %+v\nexpected: %+v\n", lExpr.SourceRegister, lExpect.SourceRegister) } case *expr.Immediate: lExpr, ok := e.(*expr.Immediate) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Immediate) if !ok || !okExpected { t.Errorf("invalid Immediate expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if !bytes.Equal(lExpr.Data, lExpect.Data) && !test.ExpectedFail { t.Errorf("invalid Immediate.Data,\ngot: %+v,\nexpected: %+v", lExpr.Data, lExpect.Data) return false } case *expr.TProxy: lExpr, ok := e.(*expr.TProxy) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.TProxy) if !ok || !okExpected { t.Errorf("invalid TProxy expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.Family != lExpect.Family || lExpr.TableFamily != lExpect.TableFamily || lExpr.RegPort != lExpect.RegPort { t.Errorf("invalid TProxy expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.Redir: lExpr, ok := e.(*expr.Redir) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Redir) if !ok || !okExpected { t.Errorf("invalid Redir expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.RegisterProtoMin != lExpect.RegisterProtoMin { t.Errorf("invalid Redir expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.Masq: lExpr, ok := e.(*expr.Masq) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Masq) if !ok || !okExpected { t.Errorf("invalid Masq expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.ToPorts != lExpect.ToPorts || lExpr.Random != lExpect.Random || lExpr.FullyRandom != lExpect.FullyRandom || lExpr.Persistent != lExpect.Persistent { t.Errorf("invalid Masq expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.NAT: lExpr, ok := e.(*expr.NAT) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.NAT) if !ok || !okExpected { t.Errorf("invalid NAT expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.RegProtoMin != lExpect.RegProtoMin || lExpr.RegAddrMin != lExpect.RegAddrMin || lExpr.Random != lExpect.Random || lExpr.FullyRandom != lExpect.FullyRandom || lExpr.Persistent != lExpect.Persistent { t.Errorf("invalid NAT expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.Quota: lExpr, ok := e.(*expr.Quota) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Quota) if !ok || !okExpected { t.Errorf("invalid Quota expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.Bytes != lExpect.Bytes || lExpr.Over != lExpect.Over || lExpr.Consumed != lExpect.Consumed { t.Errorf("invalid Quota.Data,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.Ct: lExpr, ok := e.(*expr.Ct) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Ct) if !ok || !okExpected { t.Errorf("invalid Ct expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.Key != lExpect.Key || lExpr.Register != lExpect.Register || lExpr.SourceRegister != lExpect.SourceRegister { t.Errorf("invalid Ct parms,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.Bitwise: lExpr, ok := e.(*expr.Bitwise) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Bitwise) if !ok || !okExpected { t.Errorf("invalid Bitwise expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if lExpr.Len != lExpect.Len || !bytes.Equal(lExpr.Mask, lExpect.Mask) || !bytes.Equal(lExpr.Xor, lExpect.Xor) || lExpr.DestRegister != lExpect.DestRegister || lExpr.SourceRegister != lExpect.SourceRegister { t.Errorf("invalid Bitwise parms,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.Log: lExpr, ok := e.(*expr.Log) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Log) if !ok || !okExpected { t.Errorf("invalid Log expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if !bytes.Equal(lExpr.Data, lExpect.Data) && !test.ExpectedFail { t.Errorf("invalid Log.Data,\ngot: %+v,\nexpected: %+v", lExpr.Data, lExpect.Data) return false } if lExpr.Key != lExpect.Key || lExpr.Level != lExpect.Level || lExpr.Group != lExpect.Group || lExpr.Snaplen != lExpect.Snaplen || lExpr.QThreshold != lExpect.QThreshold { t.Errorf("invalid Log fields,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } case *expr.Cmp: lExpr, ok := e.(*expr.Cmp) lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Cmp) if !ok || !okExpected { t.Errorf("invalid Cmp expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) return false } if !bytes.Equal(lExpr.Data, lExpect.Data) && !test.ExpectedFail { t.Errorf("invalid Cmp.Data,\ngot: %+v,\nexpected: %+v", lExpr.Data, lExpect.Data) return false } } } return true } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/nftest/utils.go�������������������������������������������0000664�0000000�0000000�00000005403�15003540030�0023531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftest import ( "testing" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/google/nftables" "github.com/google/nftables/expr" ) // AddTestRule adds a generic table, chain and rule with the given expression. func AddTestRule(t *testing.T, conn *nftables.Conn, exp *[]expr.Any) (*nftables.Rule, *nftables.Chain) { _, err := Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) if err != nil { t.Errorf("pre step add_table() yyy-inet failed: %s", err) return nil, nil } chn := Fw.AddChain( exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, nftables.ChainPolicyAccept) if chn == nil { t.Error("pre step add_chain() input-yyy-inet failed") return nil, nil } //nft.Commit() r, err := Fw.AddRule( exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, 0, "key-yyy", exp) if err != nil { t.Errorf("Error adding rule: %s", err) return nil, nil } t.Logf("Rule: %+v", r) return r, chn } // AddTestSNATRule adds a generic table, chain and rule with the given expression. func AddTestSNATRule(t *testing.T, conn *nftables.Conn, exp *[]expr.Any) (*nftables.Rule, *nftables.Chain) { _, err := Fw.AddTable("uuu", exprs.NFT_FAMILY_INET) if err != nil { t.Errorf("pre step add_table() uuu-inet failed: %s", err) return nil, nil } chn := Fw.AddChain( exprs.NFT_HOOK_POSTROUTING, "uuu", exprs.NFT_FAMILY_INET, nftables.ChainPriorityNATSource, nftables.ChainTypeNAT, nftables.ChainHookPostrouting, nftables.ChainPolicyAccept) if chn == nil { t.Error("pre step add_chain() input-uuu-inet failed") return nil, nil } //nft.Commit() r, err := Fw.AddRule( exprs.NFT_HOOK_POSTROUTING, "uuu", exprs.NFT_FAMILY_INET, 0, "key-uuu", exp) if err != nil { t.Errorf("Error adding rule: %s", err) return nil, nil } t.Logf("Rule: %+v", r) return r, chn } // AddTestDNATRule adds a generic table, chain and rule with the given expression. func AddTestDNATRule(t *testing.T, conn *nftables.Conn, exp *[]expr.Any) (*nftables.Rule, *nftables.Chain) { _, err := Fw.AddTable("iii", exprs.NFT_FAMILY_INET) if err != nil { t.Errorf("pre step add_table() iii-inet failed: %s", err) return nil, nil } chn := Fw.AddChain( exprs.NFT_HOOK_PREROUTING, "iii", exprs.NFT_FAMILY_INET, nftables.ChainPriorityNATDest, nftables.ChainTypeNAT, nftables.ChainHookPrerouting, nftables.ChainPolicyAccept) if chn == nil { t.Error("pre step add_chain() input-iii-inet failed") return nil, nil } //nft.Commit() r, err := Fw.AddRule( exprs.NFT_HOOK_PREROUTING, "iii", exprs.NFT_FAMILY_INET, 0, "key-iii", exp) if err != nil { t.Errorf("Error adding rule: %s", err) return nil, nil } t.Logf("Rule: %+v", r) return r, chn } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/parser.go�������������������������������������������������0000664�0000000�0000000�00000014160�15003540030�0022362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" "github.com/google/nftables/expr" ) // nftables rules are composed of expressions, for example: // tcp dport 443 ip daddr 192.168.1.1 // \-----------/ \------------------/ // with these format: // keyword1<SPACE>keyword2<SPACE>value... // // here we parse the expression, and based on keyword1, we build the rule with the given options. // // If the rule has multiple values (tcp dport 80,443,8080), no spaces are allowed, // and the separator is a ",", instead of the format { 80, 443, 8080 } // // In order to debug invalid expressions, or how to build new ones, use the following command: // # nft --debug netlink add rule filter output mark set 1 // ip filter output // [ immediate reg 1 0x00000001 ] // [ meta set mark with reg 1 ] // // Debugging added rules: // nft --debug netlink list ruleset // // https://wiki.archlinux.org/title/Nftables#Expressions // https://wiki.nftables.org/wiki-nftables/index.php/Building_rules_through_expressions func (n *Nft) parseExpression(table, chain, family string, expression *config.Expressions) *[]expr.Any { var exprList []expr.Any cmpOp := exprs.NewOperator(expression.Statement.Op) switch expression.Statement.Name { case exprs.NFT_CT: exprCt := n.buildConntrackRule(expression.Statement.Values, &cmpOp) if exprCt == nil { log.Warning("%s Ct statement error", logTag) return nil } exprList = append(exprList, *exprCt...) case exprs.NFT_META: metaExpr, err := exprs.NewExprMeta(expression.Statement.Values, &cmpOp) if err != nil { log.Warning("%s meta statement error: %s", logTag, err) return nil } for _, exprValue := range expression.Statement.Values { switch exprValue.Key { case exprs.NFT_META_L4PROTO: l4rule, err := n.buildL4ProtoRule(table, family, exprValue.Value, &cmpOp) if err != nil { log.Warning("%s meta.l4proto statement error: %s", logTag, err) return nil } *metaExpr = append(*metaExpr, *l4rule...) case exprs.NFT_DPORT, exprs.NFT_SPORT: exprPDir, err := exprs.NewExprPortDirection(exprValue.Key) if err != nil { log.Warning("%s ports statement error: %s", logTag, err) return nil } *metaExpr = append(*metaExpr, []expr.Any{exprPDir}...) portsRule, err := n.buildPortsRule(table, family, exprValue.Value, &cmpOp) if err != nil { log.Warning("%s meta.l4proto.ports statement error: %s", logTag, err) return nil } *metaExpr = append(*metaExpr, *portsRule...) } } return metaExpr case exprs.NFT_ETHER: etherExpr, err := exprs.NewExprEther(expression.Statement.Values) if err != nil { log.Warning("%s ether statement error: %s", logTag, err) return nil } return etherExpr // TODO: support iif, oif case exprs.NFT_IIFNAME, exprs.NFT_OIFNAME: isOut := expression.Statement.Name == exprs.NFT_OIFNAME iface := expression.Statement.Values[0].Key if iface == "" { log.Warning("%s network interface statement error: %s", logTag, expression.Statement.Name) return nil } exprList = append(exprList, *exprs.NewExprIface(iface, isOut, cmpOp)...) case exprs.NFT_FAMILY_IP, exprs.NFT_FAMILY_IP6: exprIP, err := exprs.NewExprIP(family, expression.Statement.Values, cmpOp) if err != nil { log.Warning("%s addr statement error: %s", logTag, err) return nil } exprList = append(exprList, *exprIP...) case exprs.NFT_PROTO_ICMP, exprs.NFT_PROTO_ICMPv6: exprICMP := n.buildICMPRule(table, family, expression.Statement.Name, expression.Statement.Values) if exprICMP == nil { log.Warning("%s icmp statement error", logTag) return nil } exprList = append(exprList, *exprICMP...) case exprs.NFT_LOG: exprLog, err := exprs.NewExprLog(expression.Statement) if err != nil { log.Warning("%s log statement error", logTag) return nil } exprList = append(exprList, *exprLog...) case exprs.NFT_LIMIT: exprLimit, err := exprs.NewExprLimit(expression.Statement) if err != nil { log.Warning("%s %s", logTag, err) return nil } exprList = append(exprList, *exprLimit...) case exprs.NFT_PROTO_UDP, exprs.NFT_PROTO_TCP, exprs.NFT_PROTO_UDPLITE, exprs.NFT_PROTO_SCTP, exprs.NFT_PROTO_DCCP: exprProto, err := exprs.NewExprProtocol(expression.Statement.Name) if err != nil { log.Warning("%s proto statement error: %s", logTag, err) return nil } exprList = append(exprList, *exprProto...) for _, exprValue := range expression.Statement.Values { switch exprValue.Key { case exprs.NFT_DPORT, exprs.NFT_SPORT: exprPDir, err := exprs.NewExprPortDirection(exprValue.Key) if err != nil { log.Warning("%s ports statement error: %s", logTag, err) return nil } exprList = append(exprList, []expr.Any{exprPDir}...) portsRule, err := n.buildPortsRule(table, family, exprValue.Value, &cmpOp) if err != nil { log.Warning("%s proto.ports statement error: %s", logTag, err) return nil } exprList = append(exprList, *portsRule...) } } case exprs.NFT_QUOTA: exprQuota, err := exprs.NewQuota(expression.Statement.Values) if err != nil { log.Warning("%s quota statement error: %s", logTag, err) return nil } exprList = append(exprList, *exprQuota...) case exprs.NFT_NOTRACK: exprList = append(exprList, *exprs.NewNoTrack()...) case exprs.NFT_COUNTER: defaultCounterName := "opensnitch" counterObj := &nftables.CounterObj{ Table: &nftables.Table{Name: table, Family: nftables.TableFamilyIPv4}, Name: defaultCounterName, Bytes: 0, Packets: 0, } for _, counterOption := range expression.Statement.Values { switch counterOption.Key { case exprs.NFT_COUNTER_NAME: defaultCounterName = counterOption.Value counterObj.Name = defaultCounterName case exprs.NFT_COUNTER_BYTES: // TODO: allow to set initial bytes/packets? counterObj.Bytes = 1 case exprs.NFT_COUNTER_PACKETS: counterObj.Packets = 1 } } n.Conn.AddObj(counterObj) exprList = append(exprList, *exprs.NewExprCounter(defaultCounterName)...) } return &exprList } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/rule_helpers.go�������������������������������������������0000664�0000000�0000000�00000014073�15003540030�0023562�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "fmt" "strings" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" "github.com/google/nftables/expr" ) // rules examples: https://github.com/google/nftables/blob/master/nftables_test.go func (n *Nft) buildICMPRule(table, family string, icmpProtoVersion string, icmpOptions []*config.ExprValues) *[]expr.Any { tbl := n.GetTable(table, family) if tbl == nil { return nil } offset := uint32(0) icmpType := uint8(0) setType := nftables.SetDatatype{} switch icmpProtoVersion { case exprs.NFT_PROTO_ICMP: setType = nftables.TypeICMPType case exprs.NFT_PROTO_ICMPv6: setType = nftables.TypeICMP6Type default: return nil } exprICMP, _ := exprs.NewExprProtocol(icmpProtoVersion) ICMPrule := []expr.Any{} ICMPrule = append(ICMPrule, *exprICMP...) ICMPtemp := []expr.Any{} setElements := []nftables.SetElement{} for _, icmp := range icmpOptions { switch icmp.Key { case exprs.NFT_ICMP_TYPE: icmpTypeList := strings.Split(icmp.Value, ",") for _, icmpTypeStr := range icmpTypeList { if exprs.NFT_PROTO_ICMPv6 == icmpProtoVersion { icmpType = exprs.GetICMPv6Type(icmpTypeStr) } else { icmpType = exprs.GetICMPType(icmpTypeStr) } exprCmp := &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{icmpType}, } ICMPtemp = append(ICMPtemp, []expr.Any{exprCmp}...) // fill setElements. If there're more than 1 icmp type we'll use it later setElements = append(setElements, []nftables.SetElement{ { Key: []byte{icmpType}, }, }...) } case exprs.NFT_ICMP_CODE: // TODO offset = 1 } } ICMPrule = append(ICMPrule, []expr.Any{ &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: offset, // 0 type, 1 code Len: 1, }, }...) if len(setElements) == 1 { ICMPrule = append(ICMPrule, ICMPtemp...) } else { set := &nftables.Set{ Anonymous: true, Constant: true, Table: tbl, KeyType: setType, } if err := n.Conn.AddSet(set, setElements); err != nil { log.Warning("%s AddSet() error: %s", logTag, err) return nil } sysSets = append(sysSets, []*nftables.Set{set}...) ICMPrule = append(ICMPrule, []expr.Any{ &expr.Lookup{ SourceRegister: 1, SetName: set.Name, SetID: set.ID, }}...) } return &ICMPrule } func (n *Nft) buildConntrackRule(ctOptions []*config.ExprValues, cmpOp *expr.CmpOp) *[]expr.Any { exprList := []expr.Any{} setMark := false for _, ctOption := range ctOptions { switch ctOption.Key { // we expect to have multiple "state" keys: // { "state": "established", "state": "related" } case exprs.NFT_CT_STATE: ctExprState, err := exprs.NewExprCtState(ctOptions) if err != nil { log.Warning("%s ct set state error: %s", logTag, err) return nil } exprList = append(exprList, *ctExprState...) exprList = append(exprList, &expr.Cmp{Op: expr.CmpOpNeq, Register: 1, Data: []byte{0, 0, 0, 0}}, ) // we only need to iterate once here goto Exit case exprs.NFT_CT_SET_MARK: setMark = true case exprs.NFT_CT_MARK: ctExprMark, err := exprs.NewExprCtMark(setMark, ctOption.Value, cmpOp) if err != nil { log.Warning("%s ct mark error: %s", logTag, err) return nil } exprList = append(exprList, *ctExprMark...) goto Exit default: log.Warning("%s invalid conntrack option: %s", logTag, ctOption) return nil } } Exit: return &exprList } // buildL4ProtoRule helper builds a new protocol rule to match ports and protocols. // // nft --debug=netlink add rule filter input meta l4proto { tcp, udp } th dport 53 // __set%d filter 3 size 2 // __set%d filter 0 // element 00000006 : 0 [end] element 00000011 : 0 [end] // ip filter input // [ meta load l4proto => reg 1 ] // [ lookup reg 1 set __set%d ] // [ payload load 2b @ transport header + 2 => reg 1 ] // [ cmp eq reg 1 0x00003500 ] func (n *Nft) buildL4ProtoRule(table, family, l4prots string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { tbl := n.GetTable(table, family) if tbl == nil { return nil, fmt.Errorf("Invalid table (%s, %s)", table, family) } exprList := []expr.Any{} if strings.Index(l4prots, ",") != -1 { set := &nftables.Set{ Anonymous: true, Constant: true, Table: tbl, KeyType: nftables.TypeInetProto, } protoSet := exprs.NewExprProtoSet(l4prots) if err := n.Conn.AddSet(set, *protoSet); err != nil { log.Warning("%s protoSet, AddSet() error: %s", logTag, err) return nil, err } exprList = append(exprList, &expr.Lookup{ SourceRegister: 1, SetName: set.Name, SetID: set.ID, }) } else { exprProto := exprs.NewExprL4Proto(l4prots, cmpOp) exprList = append(exprList, *exprProto...) } return &exprList, nil } func (n *Nft) buildPortsRule(table, family, ports string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { tbl := n.GetTable(table, family) if tbl == nil { return nil, fmt.Errorf("Invalid table (%s, %s)", table, family) } exprList := []expr.Any{} if strings.Index(ports, ",") != -1 { set := &nftables.Set{ Anonymous: true, Constant: true, Table: tbl, KeyType: nftables.TypeInetService, } setElements := exprs.NewExprPortSet(ports) if err := n.Conn.AddSet(set, *setElements); err != nil { log.Warning("%s portSet, AddSet() error: %s", logTag, err) return nil, err } exprList = append(exprList, &expr.Lookup{ SourceRegister: 1, SetName: set.Name, SetID: set.ID, }) sysSets = append(sysSets, []*nftables.Set{set}...) } else if strings.Index(ports, "-") != -1 { portRange, err := exprs.NewExprPortRange(ports, cmpOp) if err != nil { log.Warning("%s invalid portRange: %s, %s", logTag, ports, err) return nil, err } exprList = append(exprList, *portRange...) } else { exprPort, err := exprs.NewExprPort(ports, cmpOp) if err != nil { return nil, err } exprList = append(exprList, *exprPort...) } return &exprList, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/rules.go��������������������������������������������������0000664�0000000�0000000�00000017170�15003540030�0022224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "fmt" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" "golang.org/x/sys/unix" ) // QueueDNSResponses redirects DNS responses to us, in order to keep a cache // of resolved domains. // This rule must be added in top of the system rules, otherwise it may get bypassed. // nft insert rule ip filter input udp sport 53 queue num 0 bypass func (n *Nft) QueueDNSResponses(enable bool, logError bool) (error, error) { if n.Conn == nil { return nil, nil } families := []string{exprs.NFT_FAMILY_INET} for _, fam := range families { table := n.GetTable(exprs.NFT_CHAIN_FILTER, fam) chain := GetChain(exprs.NFT_HOOK_INPUT, table) if table == nil { log.Error("QueueDNSResponses() Error getting table: %s-filter", fam) continue } if chain == nil { log.Error("QueueDNSResponses() Error getting chain: %s-%d", table.Name, table.Family) continue } // nft list ruleset -a n.Conn.InsertRule(&nftables.Rule{ Position: 0, Table: table, Chain: chain, Exprs: []expr.Any{ &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_UDP}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 0, Len: 2, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: binaryutil.BigEndian.PutUint16(uint16(53)), }, &expr.Queue{ Num: n.QueueNum, Flag: expr.QueueFlagBypass, }, }, // rule key, to allow get it later by key UserData: []byte(InterceptionRuleKey), }) } // apply changes if !n.Commit() { return fmt.Errorf("Error adding DNS interception rules"), nil } return nil, nil } // QueueConnections inserts the firewall rule which redirects connections to us. // Connections are queued until the user denies/accept them, or reaches a timeout. // This rule must be added at the end of all the other rules, that way we can add // rules above this one to exclude a service/app from being intercepted. // nft insert rule ip mangle OUTPUT ct state new queue num 0 bypass func (n *Nft) QueueConnections(enable bool, logError bool) (error, error) { if n.Conn == nil { return nil, fmt.Errorf("nftables QueueConnections: netlink connection not active") } table := n.GetTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) if table == nil { return nil, fmt.Errorf("QueueConnections() Error getting table mangle-inet") } chain := GetChain(exprs.NFT_HOOK_OUTPUT, table) if chain == nil { return nil, fmt.Errorf("QueueConnections() Error getting outputChain: output-%s", table.Name) } n.Conn.AddRule(&nftables.Rule{ Position: 0, Table: table, Chain: chain, Exprs: []expr.Any{ &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpNeq, Register: 1, Data: []byte{unix.IPPROTO_TCP}, }, &expr.Ct{Register: 1, SourceRegister: false, Key: expr.CtKeySTATE}, &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitNEW | expr.CtStateBitRELATED), Xor: binaryutil.NativeEndian.PutUint32(0), }, &expr.Cmp{Op: expr.CmpOpNeq, Register: 1, Data: []byte{0, 0, 0, 0}}, &expr.Queue{ Num: n.QueueNum, Flag: expr.QueueFlagBypass, }, }, // rule key, to allow get it later by key UserData: []byte(InterceptionRuleKey), }) /* nft --debug=netlink add rule inet mangle output tcp flags '& (fin|syn|rst|ack) == syn' queue bypass num 0 [ meta load l4proto => reg 1 ] [ cmp eq reg 1 0x00000006 ] [ payload load 1b @ transport header + 13 => reg 1 ] [ bitwise reg 1 = ( reg 1 & 0x00000002 ) ^ 0x00000000 ] [ cmp neq reg 1 0x00000000 ] [ queue num 0 bypass ] Intercept packets *only* with the SYN flag set. Using 'ct state NEW' causes to intercept packets with other flags set, which sometimes means that we receive outbound connections not in the expected order: 443:1.1.1.1 -> 192.168.123:12345 (bits ACK, ACK+PSH or SYN+ACK set) */ n.Conn.AddRule(&nftables.Rule{ Position: 0, Table: table, Chain: chain, Exprs: []expr.Any{ &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_TCP}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 13, Len: 1, }, &expr.Bitwise{ DestRegister: 1, SourceRegister: 1, Len: 1, Mask: []byte{0x17}, Xor: []byte{0x00}, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x02}, }, &expr.Queue{ Num: n.QueueNum, Flag: expr.QueueFlagBypass, }, }, // rule key, to allow get it later by key UserData: []byte(InterceptionRuleKey), }) // apply changes if !n.Commit() { return fmt.Errorf("Error adding interception rule "), nil } return nil, nil } // InsertRule inserts a rule at the top of rules list. func (n *Nft) InsertRule(chain, table, family string, position uint64, exprs *[]expr.Any) error { tbl := n.GetTable(table, family) if tbl == nil { return fmt.Errorf("%s getting table: %s, %s", logTag, table, family) } chainKey := getChainKey(chain, tbl) chn, chok := sysChains.Load(chainKey) if !chok { return fmt.Errorf("%s getting table: %s, %s", logTag, table, family) } rule := &nftables.Rule{ Position: position, Table: tbl, Chain: chn.(*nftables.Chain), Exprs: *exprs, UserData: []byte(SystemRuleKey), } n.Conn.InsertRule(rule) if !n.Commit() { return fmt.Errorf("rule not added") } return nil } // AddRule adds a rule to the system. func (n *Nft) AddRule(chain, table, family string, position uint64, key string, exprs *[]expr.Any) (*nftables.Rule, error) { tbl := n.GetTable(table, family) if tbl == nil { return nil, fmt.Errorf("getting %s table: %s, %s", logTag, table, family) } chainKey := getChainKey(chain, tbl) chn, chok := sysChains.Load(chainKey) if !chok { return nil, fmt.Errorf("getting table: %s, %s", table, family) } rule := &nftables.Rule{ Position: position, Table: tbl, Chain: chn.(*nftables.Chain), Exprs: *exprs, UserData: []byte(key), } n.Conn.AddRule(rule) if !n.Commit() { return nil, fmt.Errorf("adding %s rule", logTag) } return rule, nil } func (n *Nft) delRulesByKey(key string) error { chains, err := n.Conn.ListChains() if err != nil { return fmt.Errorf("error listing nftables chains (%s): %s", key, err) } for _, c := range chains { rules, err := n.Conn.GetRule(c.Table, c) if err != nil { log.Warning("Error listing rules (%s): %s", key, err) continue } delRules := 0 for _, r := range rules { if string(r.UserData) != key { continue } // just passing the r object doesn't work. if err := n.Conn.DelRule(&nftables.Rule{ Table: c.Table, Chain: c, Handle: r.Handle, }); err != nil { log.Warning("[nftables] error deleting rule (%s): %s", key, err) continue } delRules++ } if delRules > 0 { if !n.Commit() { log.Warning("%s error deleting rules: %s", logTag, err) } } if len(rules) == 0 || len(rules) == delRules { _, chfound := sysChains.Load(getChainKey(c.Name, c.Table)) if chfound { n.DelChain(c) } } } return nil } // DelInterceptionRules deletes our interception rules, by key. func (n *Nft) DelInterceptionRules() { n.delRulesByKey(InterceptionRuleKey) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/rules_test.go���������������������������������������������0000664�0000000�0000000�00000014074�15003540030�0023263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables_test import ( "testing" nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables" ) func getRulesList(t *testing.T, conn *nftables.Conn, family, tblName, chnName string) ([]*nftables.Rule, int) { chains, err := conn.ListChains() if err != nil { return nil, -1 } for rdx, c := range chains { if c.Table.Family == nftb.GetFamilyCode(family) && c.Table.Name == tblName && c.Name == chnName { rules, err := conn.GetRule(c.Table, c) if err != nil { return nil, -1 } return rules, rdx } } return nil, -1 } func getRule(t *testing.T, conn *nftables.Conn, tblName, chnName, key string, ruleHandle uint64) (*nftables.Rule, int) { chains, err := conn.ListChains() if err != nil { return nil, -1 } for _, c := range chains { rules, err := conn.GetRule(c.Table, c) if err != nil { continue } for rdx, r := range rules { //t.Logf("Table: %s<->%s, Chain: %s<->%s, Rule Handle: %d<->%d, UserData: %s<->%s", c.Table.Name, tblName, c.Name, chnName, r.Handle, ruleHandle, string(r.UserData), key) if c.Table.Name == tblName && c.Name == chnName { if ruleHandle > 0 && r.Handle == ruleHandle { return r, rdx } if key != "" && string(r.UserData) == key { return r, rdx } } } } return nil, -1 } func TestAddRule(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn r, chn := nftest.AddTestRule(t, conn, exprs.NewNoTrack()) /* _, err := nft.AddTable("yyy", exprs.NFT_FAMILY_INET) if err != nil { t.Error("pre step add_table() yyy-inet failed") } chn := nft.AddChain( exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, nftables.ChainPolicyAccept) if chn == nil { t.Error("pre step add_chain() input-yyy-inet failed") } r, err := nft.addRule( exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, 0, "key-yyy", exprs.NewNoTrack()) if err != nil { t.Errorf("Error adding rule: %s", err) } */ rules, err := conn.GetRules(chn.Table, chn) if err != nil || len(rules) != 1 { t.Errorf("Rule not added, total: %d", len(rules)) } t.Log(r.Handle) } func TestInsertRule(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn _, err := nftest.Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) if err != nil { t.Error("pre step add_table() yyy-inet failed") } chn := nftest.Fw.AddChain( exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, nftables.ChainPolicyAccept) if chn == nil { t.Error("pre step add_chain() input-yyy-inet failed") } err = nftest.Fw.InsertRule( exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, 0, exprs.NewNoTrack()) if err != nil { t.Errorf("Error inserting rule: %s", err) } rules, err := conn.GetRules(chn.Table, chn) if err != nil || len(rules) != 1 { t.Errorf("Rule not inserted, total: %d", len(rules)) } } func TestQueueConnections(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn _, err := nftest.Fw.AddTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) if err != nil { t.Error("pre step add_table() mangle-inet failed") } chn := nftest.Fw.AddChain( exprs.NFT_HOOK_OUTPUT, exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, nftables.ChainPolicyAccept) if chn == nil { t.Error("pre step add_chain() output-mangle-inet failed") } if err1, err2 := nftest.Fw.QueueConnections(true, true); err1 != nil && err2 != nil { t.Errorf("rule to queue connections not added: %s, %s", err1, err2) } r, _ := getRule(t, conn, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, nftb.InterceptionRuleKey, 0) if r == nil { t.Error("rule to queue connections not in the list") } if string(r.UserData) != nftb.InterceptionRuleKey { t.Errorf("invalid UserData: %s", string(r.UserData)) } } func TestQueueDNSResponses(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn _, err := nftest.Fw.AddTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) if err != nil { t.Error("pre step add_table() filter-inet failed") } chn := nftest.Fw.AddChain( exprs.NFT_HOOK_INPUT, exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, nftables.ChainPolicyAccept) if chn == nil { t.Error("pre step add_chain() input-filter-inet failed") } if err1, err2 := nftest.Fw.QueueDNSResponses(true, true); err1 != nil && err2 != nil { t.Errorf("rule to queue DNS responses not added: %s, %s", err1, err2) } r, _ := getRule(t, conn, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, nftb.InterceptionRuleKey, 0) if r == nil { t.Error("rule to queue DNS responses not in the list") } if string(r.UserData) != nftb.InterceptionRuleKey { t.Errorf("invalid UserData: %s", string(r.UserData)) } // nftables.DelRule() does not accept rule handles == 0 // https://github.com/google/nftables/blob/8f2d395e1089dea4966c483fbeae7e336917c095/rule.go#L200 // sometimes when adding this rule in new namespaces it's added with rule.Handle == 0, so it fails deleting the rule, thus failing the test. // can it happen on "prod" environments? /*if err1, err2 := nft.QueueDNSResponses(false, true); err1 != nil && err2 != nil { t.Errorf("rule to queue DNS responses not deleted: %s, %s", err1, err2) } r, _ = getRule(t, conn, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, nftb.InterceptionRuleKey, 0) if r != nil { t.Error("rule to queue DNS responses should have been deleted") }*/ } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/system.go�������������������������������������������������0000664�0000000�0000000�00000011524�15003540030�0022413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "fmt" "strings" "sync" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/iptables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" "github.com/google/nftables/expr" "github.com/google/uuid" ) // store of tables added to the system type sysTablesT struct { tables map[string]*nftables.Table sync.RWMutex } func (t *sysTablesT) Add(name string, tbl *nftables.Table) { t.Lock() defer t.Unlock() t.tables[name] = tbl } func (t *sysTablesT) Get(name string) *nftables.Table { t.RLock() defer t.RUnlock() return t.tables[name] } func (t *sysTablesT) List() map[string]*nftables.Table { t.RLock() defer t.RUnlock() return t.tables } func (t *sysTablesT) Del(name string) { t.Lock() defer t.Unlock() delete(t.tables, name) } var ( logTag = "nftables:" sysTables *sysTablesT sysChains *sync.Map origSysChains map[string]*nftables.Chain sysSets []*nftables.Set ) // InitMapsStore initializes internal stores of chains and maps. func InitMapsStore() { sysTables = &sysTablesT{ tables: make(map[string]*nftables.Table), } sysChains = &sync.Map{} origSysChains = make(map[string]*nftables.Chain) } // CreateSystemRule create the custom firewall chains and adds them to system. // nft insert rule ip opensnitch-filter opensnitch-input udp dport 1153 func (n *Nft) CreateSystemRule(chain *config.FwChain, logErrors bool) bool { if chain.IsInvalid() { log.Warning("%s CreateSystemRule(), Chain's field Name and Family cannot be empty", logTag) return false } tableName := chain.Table n.AddTable(chain.Table, chain.Family) // regular chains doesn't have a hook, nor a type if chain.Hook == "" && chain.Type == "" { n.addRegularChain(chain.Name, tableName, chain.Family) return n.Commit() } chainPolicy := nftables.ChainPolicyAccept if iptables.Action(strings.ToLower(chain.Policy)) == exprs.VERDICT_DROP { chainPolicy = nftables.ChainPolicyDrop } chainHook := GetHook(chain.Hook) chainPrio, chainType := GetChainPriority(chain.Family, chain.Type, chain.Hook) if chainPrio == nil { log.Warning("%s Invalid system firewall combination: %s, %s", logTag, chain.Type, chain.Hook) return false } if ret := n.AddChain(chain.Name, chain.Table, chain.Family, chainPrio, chainType, chainHook, chainPolicy); ret == nil { log.Warning("%s error adding chain: %s, table: %s", logTag, chain.Name, chain.Table) return false } return n.Commit() } // AddSystemRules creates the system firewall from configuration. func (n *Nft) AddSystemRules(reload, backupExistingChains bool) { n.SysConfig.RLock() defer n.SysConfig.RUnlock() if n.SysConfig.Enabled == false { log.Important("[nftables] AddSystemRules() fw disabled") return } if backupExistingChains { n.backupExistingChains() } for _, fwCfg := range n.SysConfig.SystemRules { for _, chain := range fwCfg.Chains { if !n.CreateSystemRule(chain, true) { log.Info("createSystem failed: %s %s", chain.Name, chain.Table) continue } for i := len(chain.Rules) - 1; i >= 0; i-- { if chain.Rules[i].UUID == "" { uuid := uuid.New() chain.Rules[i].UUID = uuid.String() } if chain.Rules[i].Enabled { if err4, _ := n.AddSystemRule(chain.Rules[i], chain); err4 != nil { n.SendError(fmt.Sprintf("%s (%s)", err4, chain.Rules[i].UUID)) } } } } } } // DeleteSystemRules deletes the system rules. // If force is false and the rule has not been previously added, // it won't try to delete the tables and chains. Otherwise it'll try to delete them. func (n *Nft) DeleteSystemRules(force, restoreExistingChains, logErrors bool) { n.Lock() defer n.Unlock() if err := n.delRulesByKey(SystemRuleKey); err != nil { log.Warning("error deleting interception rules: %s", err) } if restoreExistingChains { n.restoreBackupChains() } if force { n.DelSystemTables() } } // AddSystemRule inserts a new rule. func (n *Nft) AddSystemRule(rule *config.FwRule, chain *config.FwChain) (err4, err6 error) { n.Lock() defer n.Unlock() exprList := []expr.Any{} for _, expression := range rule.Expressions { exprsOfRule := n.parseExpression(chain.Table, chain.Name, chain.Family, expression) if exprsOfRule == nil { return fmt.Errorf("%s invalid rule parameters: %v", rule.UUID, expression), nil } exprList = append(exprList, *exprsOfRule...) } if len(exprList) > 0 { exprVerdict := exprs.NewExprVerdict(rule.Target, rule.TargetParameters) if exprVerdict == nil { return fmt.Errorf("%s invalid verdict %s %s", rule.UUID, rule.Target, rule.TargetParameters), nil } exprList = append(exprList, *exprVerdict...) if err := n.InsertRule(chain.Name, chain.Table, chain.Family, rule.Position, &exprList); err != nil { return err, nil } } return nil, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/system_test.go��������������������������������������������0000664�0000000�0000000�00000011031�15003540030�0023443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables_test import ( "testing" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" ) type sysChainsListT struct { family string table string chain string expectedRules int } func TestAddSystemRules(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback) if err != nil { t.Logf("Error creating fw config: %s", err) } cfg.SetFile("./testdata/test-sysfw-conf.json") if err := cfg.LoadDiskConfiguration(false); err != nil { t.Errorf("Error loading config from disk: %s", err) } nftest.Fw.AddSystemRules(false, false) rules, _ := getRulesList(t, conn, exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT) // 3 rules in total, 1 disabled. if len(rules) != 1 { t.Errorf("test-load-conf.json mangle-output should contain only 3 rules, no -> %d", len(rules)) for _, r := range rules { t.Logf("%+v", r) } } rules, _ = getRulesList(t, conn, exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT) // 3 rules in total, 1 disabled. if len(rules) != 3 { t.Errorf("test-load-conf.json mangle-output should contain only 3 rules, no -> %d", len(rules)) for _, r := range rules { t.Log(r) } } rules, _ = getRulesList(t, conn, exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_FORWARD) // 3 rules in total, 1 disabled. if len(rules) != 1 { t.Errorf("test-load-conf.json mangle-output should contain only 3 rules, no -> %d", len(rules)) for _, r := range rules { t.Log(r) } } } func TestFwConfDisabled(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback) if err != nil { t.Logf("Error creating fw config: %s", err) } cfg.SetFile("./testdata/test-sysfw-conf.json") if err := cfg.LoadDiskConfiguration(false); err != nil { t.Errorf("Error loading config from disk: %s", err) } nftest.Fw.AddSystemRules(false, false) tests := []sysChainsListT{ { exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, 3, }, { exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_FORWARD, 1, }, { exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, 1, }, } for _, tt := range tests { rules, _ := getRulesList(t, conn, tt.family, tt.table, tt.chain) if len(rules) != 0 { t.Logf("%d rules found, there should be 0", len(rules)) } } } func TestDeleteSystemRules(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback) if err != nil { t.Logf("Error creating fw config: %s", err) } cfg.SetFile("./testdata/test-sysfw-conf.json") if err := cfg.LoadDiskConfiguration(false); err != nil { t.Errorf("Error loading config from disk: %s", err) } nftest.Fw.AddSystemRules(false, false) tests := []sysChainsListT{ { exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, 3, }, { exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_FORWARD, 1, }, { exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, 1, }, } for _, tt := range tests { rules, _ := getRulesList(t, conn, tt.family, tt.table, tt.chain) if len(rules) != tt.expectedRules { t.Errorf("%d rules found, there should be %d", len(rules), tt.expectedRules) } } t.Run("test-delete-system-rules", func(t *testing.T) { nftest.Fw.DeleteSystemRules(false, false, true) for _, tt := range tests { rules, _ := getRulesList(t, conn, tt.family, tt.table, tt.chain) if len(rules) != 0 { t.Errorf("%d rules found, there should be 0", len(rules)) } tbl := nftest.Fw.GetTable(tt.table, tt.family) if tbl == nil { t.Errorf("table %s-%s should exist", tt.table, tt.family) } /*chn := nft.getChain(tt.chain, tbl, tt.family) if chn == nil { if chains, err := conn.ListChains(); err == nil { for _, c := range chains { } } t.Errorf("chain %s-%s-%s should exist", tt.family, tt.table, tt.chain) }*/ } }) t.Run("test-delete-system-rules+chains", func(t *testing.T) { }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/tables.go�������������������������������������������������0000664�0000000�0000000�00000004431�15003540030�0022340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "fmt" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" ) // AddTable adds a new table to nftables. func (n *Nft) AddTable(name, family string) (*nftables.Table, error) { famCode := GetFamilyCode(family) tbl := &nftables.Table{ Family: famCode, Name: name, } n.Conn.AddTable(tbl) if !n.Commit() { return nil, fmt.Errorf("%s error adding system firewall table: %s, family: %s (%d)", logTag, name, family, famCode) } key := getTableKey(name, family) sysTables.Add(key, tbl) return tbl, nil } // GetTable retrieves an already added table to the system. func (n *Nft) GetTable(name, family string) *nftables.Table { return sysTables.Get(getTableKey(name, family)) } func getTableKey(name string, family interface{}) string { return fmt.Sprint(name, "-", family) } // AddInterceptionTables adds the needed tables to intercept traffic. func (n *Nft) AddInterceptionTables() error { if _, err := n.AddTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET); err != nil { return err } if _, err := n.AddTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET); err != nil { return err } return nil } // Contrary to iptables, in nftables there're no predefined rules. // Convention is though to use the iptables names by default. // We need at least: mangle and filter tables, inet family (IPv4 and IPv6). func (n *Nft) addSystemTables() { n.AddTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) n.AddTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) } // return the number of rules that we didn't add. func (n *Nft) nonSystemRules(tbl *nftables.Table) int { chains, err := n.Conn.ListChains() if err != nil { return -1 } t := 0 for _, c := range chains { if tbl.Name != c.Table.Name && tbl.Family != c.Table.Family { continue } rules, err := n.Conn.GetRule(c.Table, c) if err != nil { return -1 } t += len(rules) } return t } // DelSystemTables deletes tables created from fw configuration. func (n *Nft) DelSystemTables() { for k, tbl := range sysTables.List() { if n.nonSystemRules(tbl) != 0 { continue } n.Conn.DelTable(tbl) if !n.Commit() { log.Warning("error deleting system table: %s", k) continue } sysTables.Del(k) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/tables_test.go��������������������������������������������0000664�0000000�0000000�00000006466�15003540030�0023411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables_test import ( "testing" nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" "github.com/google/nftables" ) func tableExists(t *testing.T, conn *nftables.Conn, origtbl *nftables.Table, family string) bool { tables, err := conn.ListTablesOfFamily( nftb.GetFamilyCode(family), ) if err != nil { return false } found := false for _, tbl := range tables { if origtbl != nil && tbl.Name == origtbl.Name { found = true break } } return found } func TestAddTable(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn t.Run("inet family", func(t *testing.T) { tblxxx, err := nftest.Fw.AddTable("xxx", exprs.NFT_FAMILY_INET) if err != nil { t.Error("table xxx-inet not added:", err) } if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_INET) == false { t.Error("table xxx-inet not in the list") } nftest.Fw.DelSystemTables() if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_INET) { t.Error("table xxx-inet still exists") } }) t.Run("ip family", func(t *testing.T) { tblxxx, err := nftest.Fw.AddTable("xxx", exprs.NFT_FAMILY_IP) if err != nil { t.Error("table xxx-ip not added:", err) } if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP) == false { t.Error("table xxx-ip not in the list") } nftest.Fw.DelSystemTables() if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP) { t.Errorf("table xxx-ip still exists:") // %+v", sysTables) } }) t.Run("ip6 family", func(t *testing.T) { tblxxx, err := nftest.Fw.AddTable("xxx", exprs.NFT_FAMILY_IP6) if err != nil { t.Error("table xxx-ip6 not added:", err) } if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP6) == false { t.Error("table xxx-ip6 not in the list") } nftest.Fw.DelSystemTables() if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP6) { t.Errorf("table xxx-ip6 still exists:") // %+v", sysTables) } }) } // TestAddInterceptionTables checks if the needed tables have been created. // We use 2: mangle-inet for intercepting outbound connections, and filter-inet for DNS responses interception func TestAddInterceptionTables(t *testing.T) { nftest.SkipIfNotPrivileged(t) conn, newNS := nftest.OpenSystemConn(t) defer nftest.CleanupSystemConn(t, newNS) nftest.Fw.Conn = conn if err := nftest.Fw.AddInterceptionTables(); err != nil { t.Errorf("addInterceptionTables() error: %s", err) } t.Run("mangle-inet", func(t *testing.T) { tblmangle := nftest.Fw.GetTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) if tblmangle == nil { t.Error("interception table mangle-inet not in the list") } if tableExists(t, nftest.Fw.Conn, tblmangle, exprs.NFT_FAMILY_INET) == false { t.Error("table mangle-inet not in the list") } }) t.Run("filter-inet", func(t *testing.T) { tblfilter := nftest.Fw.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) if tblfilter == nil { t.Error("interception table filter-inet not in the list") } if tableExists(t, nftest.Fw.Conn, tblfilter, exprs.NFT_FAMILY_INET) == false { t.Error("table filter-inet not in the list") } }) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/testdata/�������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022346�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/testdata/test-sysfw-conf.json�����������������������������0000664�0000000�0000000�00000010343�15003540030�0026315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "Enabled": true, "Version": 1, "SystemRules": [ { "Chains": [ { "Name": "input", "Table": "filter", "Family": "inet", "Priority": "", "Type": "filter", "Hook": "input", "Policy": "accept", "Rules": [ { "Enabled": true, "Position": "0", "Description": "Allow SSH server connections when input policy is DROP", "Parameters": "", "Expressions": [ { "Statement": { "Op": "", "Name": "tcp", "Values": [ { "Key": "dport", "Value": "22" } ] } } ], "Target": "accept", "TargetParameters": "" } ] }, { "Name": "output", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "output", "Policy": "accept", "Rules": [ { "Enabled": true, "Position": "0", "Description": "Allow ICMP", "Expressions": [ { "Statement": { "Op": "", "Name": "icmp", "Values": [ { "Key": "type", "Value": "echo-request" }, { "Key": "type", "Value": "echo-reply" } ] } } ], "Target": "accept", "TargetParameters": "" }, { "Enabled": true, "Position": "0", "Description": "Allow ICMPv6", "Expressions": [ { "Statement": { "Op": "", "Name": "icmpv6", "Values": [ { "Key": "type", "Value": "echo-request" }, { "Key": "type", "Value": "echo-reply" } ] } } ], "Target": "accept", "TargetParameters": "" }, { "Enabled": true, "Position": "0", "Description": "Exclude WireGuard VPN from being intercepted", "Parameters": "", "Expressions": [ { "Statement": { "Op": "", "Name": "udp", "Values": [ { "Key": "dport", "Value": "51820" } ] } } ], "Target": "accept", "TargetParameters": "" } ] }, { "Name": "forward", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "forward", "Policy": "accept", "Rules": [ { "UUID": "7d7394e1-100d-4b87-a90a-cd68c46edb0b", "Enabled": true, "Position": "0", "Description": "Intercept forwarded connections (docker, etc)", "Expressions": [ { "Statement": { "Op": "", "Name": "ct", "Values": [ { "Key": "state", "Value": "new" } ] } } ], "Target": "queue", "TargetParameters": "num 0" } ] } ] } ] } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/utils.go��������������������������������������������������0000664�0000000�0000000�00000013632�15003540030�0022231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "strings" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" ) func GetFamilyCode(family string) nftables.TableFamily { famCode := nftables.TableFamilyINet switch family { // [filter]: prerouting forward input output postrouting // [nat]: prerouting, input output postrouting // [route]: output case exprs.NFT_FAMILY_IP6: famCode = nftables.TableFamilyIPv6 case exprs.NFT_FAMILY_IP: famCode = nftables.TableFamilyIPv4 case exprs.NFT_FAMILY_BRIDGE: // [filter]: prerouting forward input output postrouting famCode = nftables.TableFamilyBridge case exprs.NFT_FAMILY_ARP: // [filter]: input output famCode = nftables.TableFamilyARP case exprs.NFT_FAMILY_NETDEV: // [filter]: egress, ingress famCode = nftables.TableFamilyNetdev } return famCode } func GetHook(chain string) *nftables.ChainHook { hook := nftables.ChainHookOutput // https://github.com/google/nftables/blob/master/chain.go#L33 switch strings.ToLower(chain) { case exprs.NFT_HOOK_INPUT: hook = nftables.ChainHookInput case exprs.NFT_HOOK_PREROUTING: hook = nftables.ChainHookPrerouting case exprs.NFT_HOOK_POSTROUTING: hook = nftables.ChainHookPostrouting case exprs.NFT_HOOK_FORWARD: hook = nftables.ChainHookForward case exprs.NFT_HOOK_INGRESS: hook = nftables.ChainHookIngress } return hook } // GetChainPriority gets the corresponding priority for the given chain, based // on the following configuration matrix: // https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook // https://github.com/google/nftables/blob/master/chain.go#L48 // man nft (table 6.) func GetChainPriority(family, cType, hook string) (*nftables.ChainPriority, nftables.ChainType) { // types: route, nat, filter chainType := nftables.ChainTypeFilter // priorities: raw, conntrack, mangle, natdest, filter, security chainPrio := nftables.ChainPriorityFilter family = strings.ToLower(family) cType = strings.ToLower(cType) hook = strings.ToLower(hook) // constraints // https://www.netfilter.org/projects/nftables/manpage.html#lbAQ if (cType == exprs.NFT_CHAIN_NATDEST || cType == exprs.NFT_CHAIN_NATSOURCE) && hook == exprs.NFT_HOOK_FORWARD { log.Warning("[nftables] invalid nat combination of tables and hooks. chain: %s, hook: %s", cType, hook) return nil, chainType } if family == exprs.NFT_FAMILY_NETDEV && (cType != exprs.NFT_CHAIN_FILTER || hook != exprs.NFT_HOOK_INGRESS) { log.Warning("[nftables] invalid netdev combination of tables and hooks. chain: %s, hook: %s", cType, hook) return nil, chainType } if family == exprs.NFT_FAMILY_ARP && (cType != exprs.NFT_CHAIN_FILTER || (hook != exprs.NFT_HOOK_OUTPUT && hook != exprs.NFT_HOOK_INPUT)) { log.Warning("[nftables] invalid arp combination of tables and hooks. chain: %s, hook: %s", cType, hook) return nil, chainType } if family == exprs.NFT_FAMILY_BRIDGE && (cType != exprs.NFT_CHAIN_FILTER || (hook == exprs.NFT_HOOK_EGRESS || hook == exprs.NFT_HOOK_INGRESS)) { log.Warning("[nftables] invalid bridge combination of tables and hooks. chain: %s, hook: %s", cType, hook) return nil, chainType } // Standard priority names, family and hook compatibility matrix // https://www.netfilter.org/projects/nftables/manpage.html#lbAQ switch cType { case exprs.NFT_CHAIN_FILTER: if family == exprs.NFT_FAMILY_BRIDGE { // bridge all filter -200 NF_BR_PRI_FILTER_BRIDGED chainPrio = nftables.ChainPriorityConntrack switch hook { case exprs.NFT_HOOK_PREROUTING: // -300 chainPrio = nftables.ChainPriorityRaw case exprs.NFT_HOOK_OUTPUT: // -100 chainPrio = nftables.ChainPriorityNATSource case exprs.NFT_HOOK_POSTROUTING: // 300 chainPrio = nftables.ChainPriorityConntrackHelper } } case exprs.NFT_CHAIN_MANGLE: // hooks: all // XXX: check hook input? chainPrio = nftables.ChainPriorityMangle // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types // (...) equivalent semantics to the mangle table but only for the output hook (for other hooks use type filter instead). // Despite of what is said on the wiki, mangle chains must be of filter type, // otherwise on some kernels (4.19.x) table MANGLE hook OUTPUT chain is not created chainType = nftables.ChainTypeFilter case exprs.NFT_CHAIN_RAW: // hook: all chainPrio = nftables.ChainPriorityRaw case exprs.NFT_CHAIN_CONNTRACK: chainPrio, chainType = GetConntrackPriority(hook) case exprs.NFT_CHAIN_NATDEST: // hook: prerouting chainPrio = nftables.ChainPriorityNATDest switch hook { case exprs.NFT_HOOK_OUTPUT: chainPrio = nftables.ChainPriorityNATSource } chainType = nftables.ChainTypeNAT case exprs.NFT_CHAIN_NATSOURCE: // hook: postrouting chainPrio = nftables.ChainPriorityNATSource chainType = nftables.ChainTypeNAT case exprs.NFT_CHAIN_SECURITY: // hook: all chainPrio = nftables.ChainPrioritySecurity case exprs.NFT_CHAIN_SELINUX: // hook: all if hook != exprs.NFT_HOOK_POSTROUTING { chainPrio = nftables.ChainPrioritySELinuxLast } else { chainPrio = nftables.ChainPrioritySELinuxFirst } } return chainPrio, chainType } // https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook func GetConntrackPriority(hook string) (*nftables.ChainPriority, nftables.ChainType) { chainType := nftables.ChainTypeFilter chainPrio := nftables.ChainPriorityConntrack switch hook { case exprs.NFT_HOOK_PREROUTING: chainPrio = nftables.ChainPriorityConntrack // ChainTypeNAT not allowed here case exprs.NFT_HOOK_OUTPUT: chainPrio = nftables.ChainPriorityNATSource // 100 - ChainPriorityConntrack case exprs.NFT_HOOK_POSTROUTING: chainPrio = nftables.ChainPriorityConntrackHelper chainType = nftables.ChainTypeNAT case exprs.NFT_HOOK_INPUT: // can also be hook == NFT_HOOK_POSTROUTING chainPrio = nftables.ChainPriorityConntrackConfirm } return chainPrio, chainType } ������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/firewall/nftables/utils_test.go���������������������������������������������0000664�0000000�0000000�00000016753�15003540030�0023277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables_test import ( "testing" nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" "github.com/google/nftables" ) type chainPrioT struct { test string errorReason string family string chain string hook string checkEqual bool chainPrio *nftables.ChainPriority chainType nftables.ChainType } // TestGetConntrackPriority test basic Conntrack chains priority configurations. // https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook func TestGetConntrackPriority(t *testing.T) { t.Run("hook-prerouting", func(t *testing.T) { cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_PREROUTING) if cprio != nftables.ChainPriorityConntrack && ctype != nftables.ChainTypeFilter { t.Errorf("invalid conntrack priority or type for hook PREROUTING: %+v, %+v", cprio, ctype) } }) t.Run("hook-output", func(t *testing.T) { cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_OUTPUT) if cprio != nftables.ChainPriorityNATSource && ctype != nftables.ChainTypeFilter { t.Errorf("invalid conntrack priority or type for hook OUTPUT: %+v, %+v", cprio, ctype) } }) t.Run("hook-postrouting", func(t *testing.T) { cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_POSTROUTING) if cprio != nftables.ChainPriorityConntrackHelper && ctype != nftables.ChainTypeNAT { t.Errorf("invalid conntrack priority or type for hook POSTROUTING: %+v, %+v", cprio, ctype) } }) t.Run("hook-input", func(t *testing.T) { cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_INPUT) if cprio != nftables.ChainPriorityConntrackConfirm && ctype != nftables.ChainTypeFilter { t.Errorf("invalid conntrack priority or type for hook INPUT: %+v, %+v", cprio, ctype) } }) } // https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook // https://github.com/google/nftables/blob/master/chain.go#L48 // man nft (table 6.) func TestGetChainPriority(t *testing.T) { matrixTests := []chainPrioT{ // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types // (...) equivalent semantics to the mangle table but only for the output hook (for other hooks use type filter instead). // Despite of what is said on the wiki, mangle chains must be of filter type, // otherwise on some kernels (4.19.x) table MANGLE hook OUTPUT chain is not created { "inet-mangle-output", "invalid MANGLE chain priority or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, true, nftables.ChainPriorityMangle, nftables.ChainTypeFilter, }, { "inet-natdest-output", "invalid NATDest-output chain priority or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_OUTPUT, true, nftables.ChainPriorityNATSource, nftables.ChainTypeNAT, }, { "inet-natdest-prerouting", "invalid NATDest-prerouting chain priority or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_PREROUTING, true, nftables.ChainPriorityNATDest, nftables.ChainTypeNAT, }, { "inet-natsource-postrouting", "invalid NATSource-postrouting chain priority or type: %+v-%+v, %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_POSTROUTING, true, nftables.ChainPriorityNATSource, nftables.ChainTypeNAT, }, // constraints // https://www.netfilter.org/projects/nftables/manpage.html#lbAQ { "inet-natdest-forward", "invalid natdest-forward chain: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_FORWARD, true, nil, nftables.ChainTypeFilter, }, { "inet-natsource-forward", "invalid natsource-forward chain: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, true, nil, nftables.ChainTypeFilter, }, { "netdev-filter-ingress", "invalid netdev chain prio or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_NETDEV, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INGRESS, true, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, }, { "arp-filter-input", "invalid arp chain prio or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_ARP, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, true, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, }, { "bridge-filter-prerouting", "invalid bridge-prerouting chain prio or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_BRIDGE, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_PREROUTING, true, nftables.ChainPriorityRaw, nftables.ChainTypeFilter, }, { "bridge-filter-output", "invalid bridge-output chain prio or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_BRIDGE, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_OUTPUT, true, nftables.ChainPriorityNATSource, nftables.ChainTypeFilter, }, { "bridge-filter-postrouting", "invalid bridge-postrouting chain prio or type: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_BRIDGE, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_POSTROUTING, true, nftables.ChainPriorityConntrackHelper, nftables.ChainTypeFilter, }, } for _, testChainPrio := range matrixTests { t.Run(testChainPrio.test, func(t *testing.T) { chainPrio, chainType := nftb.GetChainPriority(testChainPrio.family, testChainPrio.chain, testChainPrio.hook) if testChainPrio.checkEqual { if chainPrio != testChainPrio.chainPrio && chainType != testChainPrio.chainType { t.Errorf(testChainPrio.errorReason, chainPrio, chainType, testChainPrio.chainPrio, testChainPrio.chainType) } } else { if chainPrio == testChainPrio.chainPrio && chainType == testChainPrio.chainType { t.Errorf(testChainPrio.errorReason, chainPrio, chainType, testChainPrio.chainPrio, testChainPrio.chainType) } } }) } } func TestInvalidChainPriority(t *testing.T) { matrixTests := []chainPrioT{ { "inet-natdest-forward", "natdest-forward chain should be invalid: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_FORWARD, true, nil, nftables.ChainTypeFilter, }, { "inet-natsource-forward", "natsource-forward chain should be invalid: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, true, nil, nftables.ChainTypeFilter, }, { "netdev-natsource-forward", "netdev chain should be invalid: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_NETDEV, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, true, nil, nftables.ChainTypeFilter, }, { "arp-natsource-forward", "arp chain should be invalid: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_ARP, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, true, nil, nftables.ChainTypeFilter, }, { "bridge-natsource-forward", "bridge chain should be invalid: %+v-%+v <-> %v-%v", exprs.NFT_FAMILY_ARP, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, true, nil, nftables.ChainTypeFilter, }, } for _, testChainPrio := range matrixTests { t.Run(testChainPrio.test, func(t *testing.T) { chainPrio, chainType := nftb.GetChainPriority(testChainPrio.family, testChainPrio.chain, testChainPrio.hook) if testChainPrio.checkEqual { if chainPrio != testChainPrio.chainPrio && chainType != testChainPrio.chainType { } } else { if chainPrio == testChainPrio.chainPrio && chainType == testChainPrio.chainType { t.Errorf(testChainPrio.errorReason, chainPrio, chainType, testChainPrio.chainPrio, testChainPrio.chainType) } } }) } } ���������������������opensnitch-1.6.9/daemon/firewall/rules.go�����������������������������������������������������������0000664�0000000�0000000�00000007615�15003540030�0020431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package firewall import ( "fmt" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/firewall/iptables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // Firewall is the interface that all firewalls (iptables, nftables) must implement. type Firewall interface { Init(*int) Stop() Name() string IsRunning() bool SetQueueNum(num *int) SaveConfiguration(rawConfig string) error EnableInterception() DisableInterception(bool) QueueDNSResponses(bool, bool) (error, error) QueueConnections(bool, bool) (error, error) CleanRules(bool) AddSystemRules(bool, bool) DeleteSystemRules(bool, bool, bool) Serialize() (*protocol.SysFirewall, error) Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) ErrorsChan() <-chan string ErrChanEmpty() bool } var ( fw Firewall queueNum = 0 ) // Init initializes the firewall and loads firewall rules. // We'll try to use the firewall configured in the configuration (iptables/nftables). // If iptables is not installed, we can add nftables rules directly to the kernel, // without relying on any binaries. func Init(fwType string, qNum *int) (err error) { if fwType == iptables.Name { fw, err = iptables.Fw() if err != nil { log.Warning("iptables not available: %s", err) } } if fwType == nftables.Name || err != nil { fw, err = nftables.Fw() if err != nil { log.Warning("nftables not available: %s", err) } } if err != nil { return fmt.Errorf("firewall error: %s, not iptables nor nftables are available or are usable. Please, report it on github", err) } if fw == nil { return fmt.Errorf("Firewall not initialized") } fw.Stop() fw.Init(qNum) queueNum = *qNum log.Info("Using %s firewall", fw.Name()) return } // IsRunning returns if the firewall is running or not. func IsRunning() bool { return fw != nil && fw.IsRunning() } // ErrorsChan returns the channel where the errors are sent to. func ErrorsChan() <-chan string { return fw.ErrorsChan() } // ErrChanEmpty checks if the errors channel is empty. func ErrChanEmpty() bool { return fw.ErrChanEmpty() } // CleanRules deletes the rules we added. func CleanRules(logErrors bool) { if fw == nil { return } fw.CleanRules(logErrors) } // ChangeFw stops current firewall and initializes a new one. func ChangeFw(fwtype string) (err error) { Stop() err = Init(fwtype, &queueNum) return } // Reload deletes existing firewall rules and readds them. func Reload() { fw.Stop() fw.Init(&queueNum) } // ReloadSystemRules deletes existing rules, and add them again func ReloadSystemRules() { fw.DeleteSystemRules(!common.ForcedDelRules, common.RestoreChains, true) fw.AddSystemRules(common.ReloadRules, common.BackupChains) } // EnableInterception removes the rules to intercept outbound connections. func EnableInterception() error { if fw == nil { return fmt.Errorf("firewall not initialized when trying to enable interception, report please") } fw.EnableInterception() return nil } // DisableInterception removes the rules to intercept outbound connections. func DisableInterception() error { if fw == nil { return fmt.Errorf("firewall not initialized when trying to disable interception, report please") } fw.DisableInterception(true) return nil } // Stop deletes the firewall rules, allowing network traffic. func Stop() { if fw == nil { return } fw.Stop() } // SaveConfiguration saves configuration string to disk func SaveConfiguration(rawConfig []byte) error { return fw.SaveConfiguration(string(rawConfig)) } // Serialize transforms firewall json configuration to protobuf func Serialize() (*protocol.SysFirewall, error) { return fw.Serialize() } // Deserialize transforms firewall json configuration to protobuf func Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) { return fw.Deserialize(sysfw) } �������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/go.mod����������������������������������������������������������������������0000664�0000000�0000000�00000002311�15003540030�0016235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������module github.com/evilsocket/opensnitch/daemon go 1.17 require ( github.com/fsnotify/fsnotify v1.4.7 github.com/golang/protobuf v1.5.0 github.com/google/gopacket v1.1.14 github.com/google/nftables v0.1.0 github.com/google/uuid v1.3.0 github.com/iovisor/gobpf v0.2.0 github.com/varlink/go v0.4.0 github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452 github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae golang.org/x/net v0.0.0-20211209124913-491a49abca63 golang.org/x/sys v0.0.0-20211205182925-97ca703d548d google.golang.org/grpc v1.32.0 ) require ( github.com/BurntSushi/toml v0.4.1 // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect github.com/mdlayher/netlink v1.4.2 // indirect github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect golang.org/x/mod v0.5.1 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect google.golang.org/protobuf v1.26.0 // indirect honnef.co/go/tools v0.2.2 // indirect ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/go.sum����������������������������������������������������������������������0000664�0000000�0000000�00000046372�15003540030�0016301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gopacket v1.1.14 h1:1+TEhSu8Mh154ZBVjyd1Nt2Bb7cnyOeE3GQyb1WGLqI= github.com/google/gopacket v1.1.14/go.mod h1:UCLx9mCmAwsVbn6qQl1WIEt2SO7Nd2fD0th1TBAsqBw= github.com/google/nftables v0.1.0 h1:T6lS4qudrMufcNIZ8wSRrL+iuwhsKxpN+zFLxhUWOqk= github.com/google/nftables v0.1.0/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/iovisor/gobpf v0.2.0 h1:34xkQxft+35GagXBk3n23eqhm0v7q0ejeVirb8sqEOQ= github.com/iovisor/gobpf v0.2.0/go.mod h1:WSY9Jj5RhdgC3ci1QaacvbFdQ8cbrEjrpiZbLHLt2s4= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo= github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ= github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60 h1:tHdB+hQRHU10CfcK0furo6rSNgZ38JT8uPh70c/pFD8= github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE= github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0= github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= github.com/mdlayher/netlink v1.4.2 h1:3sbnJWe/LETovA7yRZIX3f9McVOWV3OySH6iIBxiFfI= github.com/mdlayher/netlink v1.4.2/go.mod h1:13VaingaArGUTUxFLf/iEovKxXji32JAtF858jZYEug= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g= github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY= github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/varlink/go v0.4.0 h1:+/BQoUO9eJK/+MTSHwFcJch7TMsb6N6Dqp6g0qaXXRo= github.com/varlink/go v0.4.0/go.mod h1:DKg9Y2ctoNkesREGAEak58l+jOC6JU2aqZvUYs5DynU= github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452 h1:xe1bLd/sNkKVWdZuAb2+4JeMQMYyQ7Av38iRrE1lhm8= github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk= honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015713�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/formats/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017366�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/formats/csv.go����������������������������������������������������������0000664�0000000�0000000�00000001706�15003540030�0020514�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package formats import ( "fmt" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // CSV name of the output format, used in json configs const CSV = "csv" // Csv object type Csv struct { } // NewCSV returns a new CSV transformer object. func NewCSV() *Csv { return &Csv{} } // Transform takes input arguments and formats them to CSV. func (c *Csv) Transform(args ...interface{}) (out string) { p := args[0] values := p.([]interface{}) for _, val := range values { switch val.(type) { case *protocol.Connection: con := val.(*protocol.Connection) out = fmt.Sprint(out, con.SrcIp, ",", con.SrcPort, ",", con.DstIp, ",", con.DstHost, ",", con.DstPort, ",", con.Protocol, ",", con.ProcessId, ",", con.UserId, ",", //con.ProcessComm, ",", con.ProcessPath, ",", con.ProcessArgs, ",", con.ProcessCwd, ",", ) default: out = fmt.Sprint(out, val, ",") } } out = out[:len(out)-1] return } ����������������������������������������������������������opensnitch-1.6.9/daemon/log/formats/formats.go������������������������������������������������������0000664�0000000�0000000�00000000476�15003540030�0021377�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package formats // LoggerFormat is the common interface that every format must meet. // Transform expects an arbitrary number of arguments and types, and // it must transform them to a string. // Arguments can be of type Connection, string, int, etc. type LoggerFormat interface { Transform(...interface{}) string } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/formats/json.go���������������������������������������������������������0000664�0000000�0000000�00000003013�15003540030�0020663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package formats import ( "encoding/json" "fmt" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // JSON name of the output format, used in our json config const JSON = "json" // events types const ( EvConnection = iota EvExec ) // JSONEventFormat object to be sent to the remote service. // TODO: Expand as needed: ebpf events, etc. type JSONEventFormat struct { Event interface{} `json:"Event"` Rule string `json:"Rule"` Action string `json:"Action"` Type uint8 `json:"Type"` } // NewJSON returns a new Json format, to send events as json. // The json is the protobuffer in json format. func NewJSON() *JSONEventFormat { return &JSONEventFormat{} } // Transform takes input arguments and formats them to JSON format. func (j *JSONEventFormat) Transform(args ...interface{}) (out string) { p := args[0] jObj := &JSONEventFormat{} values := p.([]interface{}) for n, val := range values { switch val.(type) { // TODO: // case *protocol.Rule: // case *protocol.Process: // case *protocol.Alerts: case *protocol.Connection: // XXX: All fields of the Connection object are sent, is this what we want? // or should we send an anonymous json? jObj.Event = val.(*protocol.Connection) jObj.Type = EvConnection case string: // action // rule name if n == 1 { jObj.Action = val.(string) } else if n == 2 { jObj.Rule = val.(string) } } } rawCfg, err := json.Marshal(&jObj) if err != nil { return } out = fmt.Sprint(string(rawCfg), "\n\n") return } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/formats/rfc3164.go������������������������������������������������������0000664�0000000�0000000�00000003025�15003540030�0021005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package formats import ( "fmt" "log/syslog" "os" "time" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // RFC3164 name of the output format, used in our json config const RFC3164 = "rfc3164" // Rfc3164 object type Rfc3164 struct { seq int } // NewRfc3164 returns a new Rfc3164 object, that transforms a message to // RFC3164 format. func NewRfc3164() *Rfc3164 { return &Rfc3164{} } // Transform takes input arguments and formats them to RFC3164 format. func (r *Rfc3164) Transform(args ...interface{}) (out string) { hostname := "" tag := "" arg1 := args[0] // we can do this better. Think. if len(args) > 1 { hostname = args[1].(string) tag = args[2].(string) } values := arg1.([]interface{}) for n, val := range values { switch val.(type) { case *protocol.Connection: con := val.(*protocol.Connection) out = fmt.Sprint(out, " SRC=\"", con.SrcIp, "\"", " SPT=\"", con.SrcPort, "\"", " DST=\"", con.DstIp, "\"", " DSTHOST=\"", con.DstHost, "\"", " DPT=\"", con.DstPort, "\"", " PROTO=\"", con.Protocol, "\"", " PID=\"", con.ProcessId, "\"", " UID=\"", con.UserId, "\"", //" COMM=", con.ProcessComm, "\"", " PATH=\"", con.ProcessPath, "\"", " CMDLINE=\"", con.ProcessArgs, "\"", " CWD=\"", con.ProcessCwd, "\"", ) default: out = fmt.Sprint(out, " ARG", n, "=\"", val, "\"") } } out = fmt.Sprintf("<%d>%s %s %s[%d]: [%s]\n", syslog.LOG_NOTICE|syslog.LOG_DAEMON, time.Now().Format(time.RFC3339), hostname, tag, os.Getpid(), out[1:]) return } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/formats/rfc5424.go������������������������������������������������������0000664�0000000�0000000�00000003044�15003540030�0021007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package formats import ( "fmt" "log/syslog" "os" "time" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // RFC5424 name of the output format, used in our json config const RFC5424 = "rfc5424" // Rfc5424 object type Rfc5424 struct { seq int } // NewRfc5424 returns a new Rfc5424 object, that transforms a message to // RFC5424 format (sort of). func NewRfc5424() *Rfc5424 { return &Rfc5424{} } // Transform takes input arguments and formats them to RFC5424 format. func (r *Rfc5424) Transform(args ...interface{}) (out string) { hostname := "" tag := "" arg1 := args[0] if len(args) > 1 { arg2 := args[1] arg3 := args[2] hostname = arg2.(string) tag = arg3.(string) } values := arg1.([]interface{}) for n, val := range values { switch val.(type) { case *protocol.Connection: con := val.(*protocol.Connection) out = fmt.Sprint(out, " SRC=\"", con.SrcIp, "\"", " SPT=\"", con.SrcPort, "\"", " DST=\"", con.DstIp, "\"", " DSTHOST=\"", con.DstHost, "\"", " DPT=\"", con.DstPort, "\"", " PROTO=\"", con.Protocol, "\"", " PID=\"", con.ProcessId, "\"", " UID=\"", con.UserId, "\"", //" COMM=", con.ProcessComm, "\"", " PATH=\"", con.ProcessPath, "\"", " CMDLINE=\"", con.ProcessArgs, "\"", " CWD=\"", con.ProcessCwd, "\"", ) default: out = fmt.Sprint(out, " ARG", n, "=\"", val, "\"") } } out = fmt.Sprintf("<%d>1 %s %s %s %d TCPOUT - [%s]\n", syslog.LOG_NOTICE|syslog.LOG_DAEMON, time.Now().Format(time.RFC3339), hostname, tag, os.Getpid(), out[1:]) return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/log.go������������������������������������������������������������������0000664�0000000�0000000�00000011406�15003540030�0017025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package log import ( "fmt" "os" "strings" "sync" "time" ) type Handler func(format string, args ...interface{}) // https://misc.flogisoft.com/bash/tip_colors_and_formatting const ( BOLD = "\033[1m" DIM = "\033[2m" RED = "\033[31m" GREEN = "\033[32m" BLUE = "\033[34m" YELLOW = "\033[33m" FG_BLACK = "\033[30m" FG_WHITE = "\033[97m" BG_DGRAY = "\033[100m" BG_RED = "\033[41m" BG_GREEN = "\033[42m" BG_YELLOW = "\033[43m" BG_LBLUE = "\033[104m" RESET = "\033[0m" ) // log level constants const ( DEBUG = iota INFO IMPORTANT WARNING ERROR FATAL ) // var ( WithColors = true Output = os.Stdout StdoutFile = "/dev/stdout" DateFormat = "2006-01-02 15:04:05" MinLevel = INFO LogUTC = true LogMicro = false mutex = &sync.RWMutex{} labels = map[int]string{ DEBUG: "DBG", INFO: "INF", IMPORTANT: "IMP", WARNING: "WAR", ERROR: "ERR", FATAL: "!!!", } colors = map[int]string{ DEBUG: DIM + FG_BLACK + BG_DGRAY, INFO: FG_WHITE + BG_GREEN, IMPORTANT: FG_WHITE + BG_LBLUE, WARNING: FG_WHITE + BG_YELLOW, ERROR: FG_WHITE + BG_RED, FATAL: FG_WHITE + BG_RED + BOLD, } ) // Wrap wraps a text with effects func Wrap(s, effect string) string { if WithColors == true { s = effect + s + RESET } return s } // Dim dims a text func Dim(s string) string { return Wrap(s, DIM) } // Bold bolds a text func Bold(s string) string { return Wrap(s, BOLD) } // Red reds the text func Red(s string) string { return Wrap(s, RED) } // Green greens the text func Green(s string) string { return Wrap(s, GREEN) } // Blue blues the text func Blue(s string) string { return Wrap(s, BLUE) } // Yellow yellows the text func Yellow(s string) string { return Wrap(s, YELLOW) } // Raw prints out a text without colors func Raw(format string, args ...interface{}) { mutex.RLock() defer mutex.RUnlock() fmt.Fprintf(Output, format, args...) } // SetLogLevel sets the log level func SetLogLevel(newLevel int) { mutex.Lock() defer mutex.Unlock() MinLevel = newLevel } // GetLogLevel returns the current log level configured. func GetLogLevel() int { mutex.RLock() defer mutex.RUnlock() return MinLevel } // SetLogUTC configures UTC timestamps func SetLogUTC(newLogUTC bool) { mutex.Lock() defer mutex.Unlock() LogUTC = newLogUTC } // GetLogUTC returns the current config. func GetLogUTC() bool { mutex.RLock() defer mutex.RUnlock() return LogUTC } // SetLogMicro configures microsecond timestamps func SetLogMicro(newLogMicro bool) { mutex.Lock() defer mutex.Unlock() LogMicro = newLogMicro } // GetLogMicro returns the current config. func GetLogMicro() bool { mutex.Lock() defer mutex.Unlock() return LogMicro } // Log prints out a text with the given color and format func Log(level int, format string, args ...interface{}) { mutex.Lock() defer mutex.Unlock() if level >= MinLevel { label := labels[level] color := colors[level] datefmt := DateFormat if LogMicro == true { datefmt = DateFormat + ".000000" } when := time.Now().UTC().Format(datefmt) if LogUTC == false { when = time.Now().Local().Format(datefmt) } what := fmt.Sprintf(format, args...) if strings.HasSuffix(what, "\n") == false { what += "\n" } l := Dim("[%s]") r := Wrap(" %s ", color) + " %s" fmt.Fprintf(Output, l+" "+r, when, label, what) } } func setDefaultLogOutput() { mutex.Lock() Output = os.Stdout mutex.Unlock() } // OpenFile opens a file to print out the logs func OpenFile(logFile string) (err error) { if logFile == StdoutFile { setDefaultLogOutput() return } if Output, err = os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil { Error("Error opening log: %s %s", logFile, err) //fallback to stdout setDefaultLogOutput() } Important("Start writing logs to %s", logFile) return err } // Close closes the current output file descriptor func Close() { if Output != os.Stdout { Output.Close() } } // Debug is the log level for debugging purposes func Debug(format string, args ...interface{}) { Log(DEBUG, format, args...) } // Info is the log level for informative messages func Info(format string, args ...interface{}) { Log(INFO, format, args...) } // Important is the log level for things that must pay attention func Important(format string, args ...interface{}) { Log(IMPORTANT, format, args...) } // Warning is the log level for non-critical errors func Warning(format string, args ...interface{}) { Log(WARNING, format, args...) } // Error is the log level for errors that should be corrected func Error(format string, args ...interface{}) { Log(ERROR, format, args...) } // Fatal is the log level for errors that must be corrected before continue func Fatal(format string, args ...interface{}) { Log(FATAL, format, args...) os.Exit(1) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/loggers/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017355�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/loggers/logger.go�������������������������������������������������������0000664�0000000�0000000�00000004343�15003540030�0021167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggers import "fmt" const logTag = "opensnitch" // Logger is the common interface that every logger must met. // Serves as a generic holder of different types of loggers. type Logger interface { Transform(...interface{}) string Write(string) } // LoggerConfig holds the configuration of a logger type LoggerConfig struct { // Name of the logger: syslog, elastic, ... Name string // Format: rfc5424, csv, json, ... Format string // Protocol: udp, tcp Protocol string // Server: 127.0.0.1:514 Server string // WriteTimeout: WriteTimeout string // Tag: opensnitchd, mytag, ... Tag string // Workers: number of workers Workers int } // LoggerManager represents the LoggerManager. type LoggerManager struct { loggers map[string]Logger msgs chan []interface{} count int } // NewLoggerManager instantiates all the configured loggers. func NewLoggerManager() *LoggerManager { lm := &LoggerManager{ loggers: make(map[string]Logger), } return lm } // Load loggers configuration and initialize them. func (l *LoggerManager) Load(configs []LoggerConfig, workers int) { for _, cfg := range configs { switch cfg.Name { case LOGGER_REMOTE: if lgr, err := NewRemote(&cfg); err == nil { l.count++ l.loggers[fmt.Sprint(lgr.Name, lgr.cfg.Server, lgr.cfg.Protocol)] = lgr workers += cfg.Workers } case LOGGER_REMOTE_SYSLOG: if lgr, err := NewRemoteSyslog(&cfg); err == nil { l.count++ l.loggers[fmt.Sprint(lgr.Name, lgr.cfg.Server, lgr.cfg.Protocol)] = lgr workers += cfg.Workers } case LOGGER_SYSLOG: if lgr, err := NewSyslog(&cfg); err == nil { l.count++ l.loggers[lgr.Name] = lgr workers += cfg.Workers } } } if workers == 0 { workers = 4 } l.msgs = make(chan []interface{}, workers) for i := 0; i < workers; i++ { go newWorker(i, l) } } func (l *LoggerManager) write(args ...interface{}) { for _, logger := range l.loggers { logger.Write(logger.Transform(args...)) } } func newWorker(id int, l *LoggerManager) { for { for msg := range l.msgs { l.write(msg) } } } // Log sends data to the loggers. func (l *LoggerManager) Log(args ...interface{}) { if l.count > 0 { go func(args ...interface{}) { argv := args l.msgs <- argv }(args...) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/loggers/remote.go�������������������������������������������������������0000664�0000000�0000000�00000010454�15003540030�0021203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggers import ( "fmt" "log/syslog" "net" "os" "strings" "sync" "sync/atomic" "time" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/formats" ) const ( LOGGER_REMOTE = "remote" ) // Remote defines the logger that writes events to a generic remote server. // It can write to the local or a remote daemon, UDP or TCP. // It supports writing events in RFC5424, RFC3164, CSV and JSON formats. type Remote struct { Name string Tag string Hostname string Writer *syslog.Writer logFormat formats.LoggerFormat cfg *LoggerConfig netConn net.Conn Timeout time.Duration errors uint32 maxErrors uint32 status uint32 mu *sync.RWMutex } // NewRemote returns a new object that manipulates and prints outbound connections // to a remote syslog server, with the given format (RFC5424 by default) func NewRemote(cfg *LoggerConfig) (*Remote, error) { var err error log.Info("NewRemote logger: %v", cfg) sys := &Remote{ mu: &sync.RWMutex{}, } sys.Name = LOGGER_REMOTE sys.cfg = cfg // list of allowed formats for this logger sys.logFormat = formats.NewRfc5424() if cfg.Format == formats.RFC3164 { sys.logFormat = formats.NewRfc3164() } else if cfg.Format == formats.JSON { sys.logFormat = formats.NewJSON() } else if cfg.Format == formats.CSV { sys.logFormat = formats.NewCSV() } sys.Tag = logTag if cfg.Tag != "" { sys.Tag = cfg.Tag } sys.Hostname, err = os.Hostname() if err != nil { sys.Hostname = "localhost" } if cfg.WriteTimeout == "" { cfg.WriteTimeout = writeTimeout } sys.Timeout = (time.Second * 15) if err = sys.Open(); err != nil { log.Error("Error loading logger: %s", err) return nil, err } log.Info("[%s] initialized: %v", sys.Name, cfg) return sys, err } // Open opens a new connection with a server or with the daemon. func (s *Remote) Open() (err error) { atomic.StoreUint32(&s.errors, 0) if s.cfg.Server == "" { return fmt.Errorf("[%s] Server address must not be empty", s.Name) } s.mu.Lock() s.netConn, err = s.Dial(s.cfg.Protocol, s.cfg.Server, s.Timeout*5) s.mu.Unlock() if err == nil { atomic.StoreUint32(&s.status, CONNECTED) } return err } // Dial opens a new connection with a remote server. func (s *Remote) Dial(proto, addr string, connTimeout time.Duration) (netConn net.Conn, err error) { switch proto { case "udp", "tcp": netConn, err = net.DialTimeout(proto, addr, connTimeout) if err != nil { return nil, err } default: return nil, fmt.Errorf("[%s] Network protocol %s not supported", s.Name, proto) } return netConn, nil } // Close closes the writer object func (s *Remote) Close() (err error) { s.mu.RLock() if s.netConn != nil { err = s.netConn.Close() //s.netConn.conn = nil } s.mu.RUnlock() atomic.StoreUint32(&s.status, DISCONNECTED) return } // ReOpen tries to reestablish the connection with the writer func (s *Remote) ReOpen() { if atomic.LoadUint32(&s.status) == CONNECTING { return } atomic.StoreUint32(&s.status, CONNECTING) if err := s.Close(); err != nil { log.Debug("[%s] error closing Close(): %s", s.Name, err) } if err := s.Open(); err != nil { log.Debug("[%s] ReOpen() error: %s", s.Name, err) } else { log.Debug("[%s] ReOpen() ok", s.Name) } } // Transform transforms data for proper ingestion. func (s *Remote) Transform(args ...interface{}) (out string) { if s.logFormat != nil { args = append(args, s.Hostname) args = append(args, s.Tag) out = s.logFormat.Transform(args...) } return } func (s *Remote) Write(msg string) { deadline := time.Now().Add(s.Timeout) // BUG: it's fairly common to have write timeouts via udp/tcp. // Reopening the connection with the server helps to resume sending events to the server, // and have a continuous stream of events. Otherwise it'd stop working. // I haven't figured out yet why these write errors ocurr. s.mu.Lock() s.netConn.SetWriteDeadline(deadline) _, err := s.netConn.Write([]byte(msg)) s.mu.Unlock() if err == nil { return } log.Debug("[%s] %s write error: %v", s.Name, s.cfg.Protocol, err.(net.Error)) atomic.AddUint32(&s.errors, 1) if atomic.LoadUint32(&s.errors) > maxAllowedErrors { s.ReOpen() return } } func (s *Remote) formatLine(msg string) string { nl := "" if !strings.HasSuffix(msg, "\n") { nl = "\n" } return fmt.Sprintf("%s%s", msg, nl) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/loggers/remote_syslog.go������������������������������������������������0000664�0000000�0000000�00000010437�15003540030�0022604�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggers import ( "fmt" "net" "os" "sync" "sync/atomic" "time" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/formats" ) const ( LOGGER_REMOTE_SYSLOG = "remote_syslog" writeTimeout = "1s" // restart syslog connection after these amount of errors maxAllowedErrors = 10 ) // connection status const ( DISCONNECTED = iota CONNECTED CONNECTING ) // RemoteSyslog defines the logger that writes traces to the syslog. // It can write to the local or a remote daemon. type RemoteSyslog struct { Syslog Hostname string netConn net.Conn Timeout time.Duration errors uint32 status uint32 mu *sync.RWMutex } // NewRemoteSyslog returns a new object that manipulates and prints outbound connections // to a remote syslog server, with the given format (RFC5424 by default) func NewRemoteSyslog(cfg *LoggerConfig) (*RemoteSyslog, error) { var err error log.Info("NewSyslog logger: %v", cfg) sys := &RemoteSyslog{ mu: &sync.RWMutex{}, } sys.Name = LOGGER_REMOTE_SYSLOG sys.cfg = cfg // list of allowed formats for this logger sys.logFormat = formats.NewRfc5424() if cfg.Format == formats.RFC3164 { sys.logFormat = formats.NewRfc3164() } else if cfg.Format == formats.CSV { sys.logFormat = formats.NewCSV() } sys.Tag = logTag if cfg.Tag != "" { sys.Tag = cfg.Tag } sys.Hostname, err = os.Hostname() if err != nil { sys.Hostname = "localhost" } if cfg.WriteTimeout == "" { cfg.WriteTimeout = writeTimeout } sys.Timeout, _ = time.ParseDuration(cfg.WriteTimeout) if err = sys.Open(); err != nil { log.Error("Error loading logger: %s", err) return nil, err } log.Info("[%s] initialized: %v", sys.Name, cfg) return sys, err } // Open opens a new connection with a server or with the daemon. func (s *RemoteSyslog) Open() (err error) { atomic.StoreUint32(&s.errors, 0) if s.cfg.Server == "" { return fmt.Errorf("[%s] Server address must not be empty", s.Name) } s.mu.Lock() s.netConn, err = s.Dial(s.cfg.Protocol, s.cfg.Server, s.Timeout*5) s.mu.Unlock() if err == nil { atomic.StoreUint32(&s.status, CONNECTED) } return err } // Dial opens a new connection with a syslog server. func (s *RemoteSyslog) Dial(proto, addr string, connTimeout time.Duration) (netConn net.Conn, err error) { switch proto { case "udp", "tcp": netConn, err = net.DialTimeout(proto, addr, connTimeout) if err != nil { return nil, err } default: return nil, fmt.Errorf("[%s] Network protocol %s not supported", s.Name, proto) } return netConn, nil } // Close closes the writer object func (s *RemoteSyslog) Close() (err error) { s.mu.RLock() defer s.mu.RUnlock() if s.netConn != nil { err = s.netConn.Close() //s.netConn.conn = nil } atomic.StoreUint32(&s.status, DISCONNECTED) return } // ReOpen tries to reestablish the connection with the writer func (s *RemoteSyslog) ReOpen() { if atomic.LoadUint32(&s.status) == CONNECTING { return } atomic.StoreUint32(&s.status, CONNECTING) if err := s.Close(); err != nil { log.Debug("[%s] error closing Close(): %s", s.Name, err) } if err := s.Open(); err != nil { log.Debug("[%s] ReOpen() error: %s", s.Name, err) return } } // Transform transforms data for proper ingestion. func (s *RemoteSyslog) Transform(args ...interface{}) (out string) { if s.logFormat != nil { args = append(args, s.Hostname) args = append(args, s.Tag) out = s.logFormat.Transform(args...) } return } func (s *RemoteSyslog) Write(msg string) { deadline := time.Now().Add(s.Timeout) // BUG: it's fairly common to have write timeouts via udp/tcp. // Reopening the connection with the server helps to resume sending events to syslog, // and have a continuous stream of events. Otherwise it'd stop working. // I haven't figured out yet why these write errors ocurr. s.mu.RLock() s.netConn.SetWriteDeadline(deadline) _, err := s.netConn.Write([]byte(msg)) s.mu.RUnlock() if err != nil { log.Debug("[%s] %s write error: %v", s.Name, s.cfg.Protocol, err.(net.Error)) atomic.AddUint32(&s.errors, 1) if atomic.LoadUint32(&s.errors) > maxAllowedErrors { s.ReOpen() return } } } // https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/log/syslog/syslog.go;l=286;drc=0a1a092c4b56a1d4033372fbd07924dad8cbb50b func (s *RemoteSyslog) formatLine(msg string) string { return msg } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/log/loggers/syslog.go�������������������������������������������������������0000664�0000000�0000000�00000003307�15003540030�0021227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggers import ( "log/syslog" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/formats" ) const ( LOGGER_SYSLOG = "syslog" ) // Syslog defines the logger that writes traces to the syslog. // It can write to the local or a remote daemon. type Syslog struct { Name string Writer *syslog.Writer Tag string logFormat formats.LoggerFormat cfg *LoggerConfig } // NewSyslog returns a new object that manipulates and prints outbound connections // to syslog (local or remote), with the given format (RFC5424 by default) func NewSyslog(cfg *LoggerConfig) (*Syslog, error) { var err error log.Info("NewSyslog logger: %v", cfg) sys := &Syslog{ Name: LOGGER_SYSLOG, cfg: cfg, } sys.logFormat = formats.NewRfc5424() if cfg.Format == formats.CSV { sys.logFormat = formats.NewCSV() } sys.Tag = logTag if cfg.Tag != "" { sys.Tag = cfg.Tag } if err = sys.Open(); err != nil { log.Error("Error loading logger: %s", err) return nil, err } log.Info("[%s logger] initialized: %v", sys.Name, cfg) return sys, err } // Open opens a new connection with a server or with the daemon. func (s *Syslog) Open() error { var err error s.Writer, err = syslog.New(syslog.LOG_NOTICE|syslog.LOG_DAEMON, logTag) return err } // Close closes the writer object func (s *Syslog) Close() error { return s.Writer.Close() } // Transform transforms data for proper ingestion. func (s *Syslog) Transform(args ...interface{}) (out string) { if s.logFormat != nil { out = s.logFormat.Transform(args...) } return } func (s *Syslog) Write(msg string) { if err := s.Writer.Notice(msg); err != nil { log.Error("[%s] write error: %s", s.Name, err) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/main.go���������������������������������������������������������������������0000664�0000000�0000000�00000043742�15003540030�0016417�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2018 Simone Margaritelli // 2021 themighty1 // 2022 calesanz // 2019-2022 Gustavo Iñiguez Goia // // This file is part of OpenSnitch. // // OpenSnitch 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. // // OpenSnitch 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 for more details. // // You should have received a copy of the GNU General Public License // along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. */ package main import ( "bytes" "context" "flag" "fmt" "io/ioutil" golog "log" "net" "os" "os/signal" "runtime" "runtime/pprof" "syscall" "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/dns" "github.com/evilsocket/opensnitch/daemon/dns/systemd" "github.com/evilsocket/opensnitch/daemon/firewall" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/loggers" "github.com/evilsocket/opensnitch/daemon/netfilter" "github.com/evilsocket/opensnitch/daemon/netlink" "github.com/evilsocket/opensnitch/daemon/procmon/ebpf" "github.com/evilsocket/opensnitch/daemon/procmon/monitor" "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/statistics" "github.com/evilsocket/opensnitch/daemon/ui" "github.com/evilsocket/opensnitch/daemon/ui/config" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) var ( showVersion = false checkRequirements = false procmonMethod = "" logFile = "" logUTC = true logMicro = false rulesPath = "/etc/opensnitchd/rules/" configFile = "/etc/opensnitchd/default-config.json" ebpfModPath = "" // /usr/lib/opensnitchd/ebpf noLiveReload = false queueNum = 0 repeatQueueNum int //will be set later to queueNum + 1 workers = 16 debug = false warning = false important = false errorlog = false uiSocket = "" uiClient = (*ui.Client)(nil) cpuProfile = "" memProfile = "" ctx = (context.Context)(nil) cancel = (context.CancelFunc)(nil) err = (error)(nil) rules = (*rule.Loader)(nil) stats = (*statistics.Statistics)(nil) queue = (*netfilter.Queue)(nil) repeatPktChan = (<-chan netfilter.Packet)(nil) pktChan = (<-chan netfilter.Packet)(nil) wrkChan = (chan netfilter.Packet)(nil) sigChan = (chan os.Signal)(nil) exitChan = (chan bool)(nil) loggerMgr *loggers.LoggerManager resolvMonitor *systemd.ResolvedMonitor ) func init() { flag.BoolVar(&showVersion, "version", debug, "Show daemon version of this executable and exit.") flag.BoolVar(&checkRequirements, "check-requirements", debug, "Check system requirements for incompatibilities.") flag.StringVar(&procmonMethod, "process-monitor-method", procmonMethod, "How to search for processes path. Options: ftrace, audit (experimental), ebpf (experimental), proc (default)") flag.StringVar(&uiSocket, "ui-socket", uiSocket, "Path the UI gRPC service listener (https://github.com/grpc/grpc/blob/master/doc/naming.md).") flag.IntVar(&queueNum, "queue-num", queueNum, "Netfilter queue number.") flag.IntVar(&workers, "workers", workers, "Number of concurrent workers.") flag.BoolVar(&noLiveReload, "no-live-reload", debug, "Disable rules live reloading.") flag.StringVar(&rulesPath, "rules-path", rulesPath, "Path to load JSON rules from.") flag.StringVar(&configFile, "config-file", configFile, "Path to the daemon configuration file.") //flag.StringVar(&ebpfModPath, "ebpf-modules-path", ebpfModPath, "Path to the directory with the eBPF modules.") flag.StringVar(&logFile, "log-file", logFile, "Write logs to this file instead of the standard output.") flag.BoolVar(&logUTC, "log-utc", logUTC, "Write logs output with UTC timezone (enabled by default).") flag.BoolVar(&logMicro, "log-micro", logMicro, "Write logs output with microsecond timestamp (disabled by default).") flag.BoolVar(&debug, "debug", debug, "Enable debug level logs.") flag.BoolVar(&warning, "warning", warning, "Enable warning level logs.") flag.BoolVar(&important, "important", important, "Enable important level logs.") flag.BoolVar(&errorlog, "error", errorlog, "Enable error level logs.") flag.StringVar(&cpuProfile, "cpu-profile", cpuProfile, "Write CPU profile to this file.") flag.StringVar(&memProfile, "mem-profile", memProfile, "Write memory profile to this file.") } // Load configuration file from disk, by default from /etc/opensnitchd/default-config.json, // or from the path specified by configFile. // This configuration will be loaded again by uiClient(), in order to monitor it for changes. func loadDiskConfiguration() (*config.Config, error) { if configFile == "" { return nil, fmt.Errorf("Configuration file cannot be empty") } raw, err := config.Load(configFile) if err != nil || len(raw) == 0 { return nil, fmt.Errorf("Error loading configuration %s: %s", configFile, err) } clientConfig, err := config.Parse(raw) if err != nil { return nil, fmt.Errorf("Error parsing configuration %s: %s", configFile, err) } log.Info("Loading configuration file %s ...", configFile) return &clientConfig, nil } func overwriteLogging() bool { return debug || warning || important || errorlog || logFile != "" || logMicro } func setupLogging() { golog.SetOutput(ioutil.Discard) if debug { log.SetLogLevel(log.DEBUG) } else if warning { log.SetLogLevel(log.WARNING) } else if important { log.SetLogLevel(log.IMPORTANT) } else if errorlog { log.SetLogLevel(log.ERROR) } else { log.SetLogLevel(log.INFO) } log.SetLogUTC(logUTC) log.SetLogMicro(logMicro) var logFileToUse string if logFile == "" { logFileToUse = log.StdoutFile } else { logFileToUse = logFile } log.Close() if err := log.OpenFile(logFileToUse); err != nil { log.Error("Error opening user defined log: %s %s", logFileToUse, err) } } func setupProfiling() { if cpuProfile != "" { if f, err := os.Create(cpuProfile); err != nil { log.Fatal("%s", err) } else if err := pprof.StartCPUProfile(f); err != nil { log.Fatal("%s", err) } } } func setupSignals() { sigChan = make(chan os.Signal, 1) exitChan = make(chan bool, workers+1) signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) go func() { sig := <-sigChan log.Raw("\n") log.Important("Got signal: %v", sig) cancel() time.AfterFunc(10*time.Second, func() { log.Error("[REVIEW] closing due to timeout") os.Exit(0) }) }() } func worker(id int) { log.Debug("Worker #%d started.", id) for true { select { case <-ctx.Done(): goto Exit default: pkt, ok := <-wrkChan if !ok { log.Debug("worker channel closed %d", id) goto Exit } onPacket(pkt) } } Exit: log.Debug("worker #%d exit", id) } func setupWorkers() { log.Debug("Starting %d workers ...", workers) // setup the workers wrkChan = make(chan netfilter.Packet) for i := 0; i < workers; i++ { go worker(i) } } // Listen to events sent from other modules func listenToEvents() { for i := 0; i < 5; i++ { go func(uiClient *ui.Client) { for evt := range ebpf.Events() { // for loop vars are per-loop, not per-item evt := evt uiClient.PostAlert( protocol.Alert_WARNING, protocol.Alert_KERNEL_EVENT, protocol.Alert_SHOW_ALERT, protocol.Alert_MEDIUM, evt) } }(uiClient) } } func initSystemdResolvedMonitor() { resolvMonitor, err := systemd.NewResolvedMonitor() if err != nil { log.Debug("[DNS] Unable to use systemd-resolved monitor: %s", err) return } _, err = resolvMonitor.Connect() if err != nil { log.Debug("[DNS] Connecting to systemd-resolved: %s", err) return } err = resolvMonitor.Subscribe() if err != nil { log.Debug("[DNS] Subscribing to systemd-resolved DNS events: %s", err) return } go func() { for { select { case exit := <-resolvMonitor.Exit(): if exit == nil { log.Info("[DNS] systemd-resolved monitor stopped") return } log.Debug("[DNS] systemd-resolved monitor disconnected. Reconnecting...") case response := <-resolvMonitor.GetDNSResponses(): if response.State != systemd.SuccessState { log.Debug("[DNS] systemd-resolved monitor response error: %v", response) continue } /*for i, q := range response.Question { log.Debug("%d SYSTEMD RESPONSE Q: %s", i, q.Name) }*/ for i, a := range response.Answer { if a.RR.Key.Type != systemd.DNSTypeA && a.RR.Key.Type != systemd.DNSTypeAAAA && a.RR.Key.Type != systemd.DNSTypeCNAME { log.Debug("systemd-resolved, excluding answer: %#v", a) continue } domain := a.RR.Key.Name ip := net.IP(a.RR.Address) log.Debug("%d systemd-resolved monitor response: %s -> %s", i, domain, ip) if a.RR.Key.Type == systemd.DNSTypeCNAME { log.Debug("systemd-resolved CNAME >> %s -> %s", a.RR.Name, domain) dns.Track(a.RR.Name, domain) } else { dns.Track(ip.String(), domain) } } } } }() } func doCleanup(queue, repeatQueue *netfilter.Queue) { log.Info("Cleaning up ...") firewall.Stop() monitor.End() uiClient.Close() queue.Close() repeatQueue.Close() if resolvMonitor != nil { resolvMonitor.Close() } if cpuProfile != "" { pprof.StopCPUProfile() } if memProfile != "" { f, err := os.Create(memProfile) if err != nil { fmt.Printf("Could not create memory profile: %s\n", err) return } defer f.Close() runtime.GC() // get up-to-date statistics if err := pprof.WriteHeapProfile(f); err != nil { fmt.Printf("Could not write memory profile: %s\n", err) } } } func onPacket(packet netfilter.Packet) { // DNS response, just parse, track and accept. if dns.TrackAnswers(packet.Packet) == true { packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark) stats.OnDNSResponse() return } // Parse the connection state con := conman.Parse(packet, uiClient.InterceptUnknown()) if con == nil { applyDefaultAction(&packet, nil) return } // accept our own connections if con.Process.ID == os.Getpid() { packet.SetVerdict(netfilter.NF_ACCEPT) return } // search a match in preloaded rules r := acceptOrDeny(&packet, con) if r != nil && r.Nolog { return } // XXX: if a connection is not intercepted due to InterceptUnknown == false, // it's not sent to the server, which leads to miss information. stats.OnConnectionEvent(con, r, r == nil) } func applyDefaultAction(packet *netfilter.Packet, con *conman.Connection) { if uiClient.DefaultAction() == rule.Allow { packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark) return } if uiClient.DefaultAction() == rule.Reject && con != nil { netlink.KillSocket(con.Protocol, con.SrcIP, con.SrcPort, con.DstIP, con.DstPort) } packet.SetVerdict(netfilter.NF_DROP) } func acceptOrDeny(packet *netfilter.Packet, con *conman.Connection) *rule.Rule { r := rules.FindFirstMatch(con) if r == nil { // no rule matched // Note that as soon as we set a verdict on a packet, the next packet in the netfilter queue // will begin to be processed even if this function hasn't yet returned // send a request to the UI client if // 1) connected and running and 2) we are not already asking if uiClient.Connected() == false || uiClient.GetIsAsking() == true { applyDefaultAction(packet, con) log.Debug("UI is not running or busy, connected: %v, running: %v", uiClient.Connected(), uiClient.GetIsAsking()) return nil } uiClient.SetIsAsking(true) defer uiClient.SetIsAsking(false) // In order not to block packet processing, we send our packet to a different netfilter queue // and then immediately pull it back out of that queue packet.SetRequeueVerdict(uint16(repeatQueueNum)) var o bool var pkt netfilter.Packet // don't wait for the packet longer than 1 sec select { case pkt, o = <-repeatPktChan: if !o { log.Debug("error while receiving packet from repeatPktChan") return nil } case <-time.After(1 * time.Second): log.Debug("timed out while receiving packet from repeatPktChan") return nil } //check if the pulled out packet is the same we put in if res := bytes.Compare(packet.Packet.Data(), pkt.Packet.Data()); res != 0 { log.Error("The packet which was requeued has changed abruptly. This should never happen. Please report this incident to the Opensnitch developers. %v %v ", packet, pkt) return nil } packet = &pkt // Update the hostname again. // This is required due to a race between the ebpf dns hook and the actual first packet beeing sent if con.DstHost == "" { con.DstHost = dns.HostOr(con.DstIP, con.DstHost) } r = uiClient.Ask(con) if r == nil { log.Error("Invalid rule received, applying default action") applyDefaultAction(packet, con) return nil } ok := false pers := "" action := string(r.Action) if r.Action == rule.Allow { action = log.Green(action) } else { action = log.Red(action) } // check if and how the rule needs to be saved if r.Duration == rule.Always { pers = "Saved" // add to the loaded rules and persist on disk if err := rules.Add(r, true); err != nil { log.Error("Error while saving rule: %s", err) } else { ok = true } } else { pers = "Added" // add to the rules but do not save to disk if err := rules.Add(r, false); err != nil { log.Error("Error while adding rule: %s", err) } else { ok = true } } if ok { log.Important("%s new rule: %s if %s", pers, action, r.Operator.String()) } } if packet == nil { log.Debug("Packet nil after processing rules") return r } if r.Enabled == false { applyDefaultAction(packet, con) ruleName := log.Green(r.Name) log.Info("DISABLED (%s) %s %s -> %s:%d (%s)", uiClient.DefaultAction(), log.Bold(log.Green("✔")), log.Bold(con.Process.Path), log.Bold(con.To()), con.DstPort, ruleName) } else if r.Action == rule.Allow { packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark) ruleName := log.Green(r.Name) if r.Operator.Operand == rule.OpTrue { ruleName = log.Dim(r.Name) } log.Debug("%s %s -> %d:%s => %s:%d, mark: %x (%s)", log.Bold(log.Green("✔")), log.Bold(con.Process.Path), con.SrcPort, log.Bold(con.SrcIP.String()), log.Bold(con.To()), con.DstPort, packet.Mark, ruleName) } else { if r.Action == rule.Reject { netlink.KillSocket(con.Protocol, con.SrcIP, con.SrcPort, con.DstIP, con.DstPort) } packet.SetVerdict(netfilter.NF_DROP) log.Debug("%s %s -> %d:%s => %s:%d, mark: %x (%s)", log.Bold(log.Red("✘")), log.Bold(con.Process.Path), con.SrcPort, log.Bold(con.SrcIP.String()), log.Bold(con.To()), con.DstPort, packet.Mark, log.Red(r.Name)) } return r } func main() { ctx, cancel = context.WithCancel(context.Background()) defer cancel() flag.Parse() if showVersion { fmt.Println(core.Version) os.Exit(0) } if checkRequirements { core.CheckSysRequirements() os.Exit(0) } setupLogging() setupProfiling() log.Important("Starting %s v%s", core.Name, core.Version) cfg, err := loadDiskConfiguration() if err != nil { log.Fatal("%s", err) } if err == nil && cfg.Rules.Path != "" { rulesPath = cfg.Rules.Path } if rulesPath == "" { log.Fatal("rules path cannot be empty") } rulesPath, err := core.ExpandPath(rulesPath) if err != nil { log.Fatal("Error accessing rules path (does it exist?): %s", err) } setupSignals() log.Info("Loading rules from %s ...", rulesPath) rules, err = rule.NewLoader(!noLiveReload) if err != nil { log.Fatal("%s", err) } else if err = rules.Load(rulesPath); err != nil { log.Fatal("%s", err) } stats = statistics.New(rules) loggerMgr = loggers.NewLoggerManager() uiClient = ui.NewClient(uiSocket, configFile, stats, rules, loggerMgr) // prepare the queue setupWorkers() queue, err := netfilter.NewQueue(uint16(queueNum)) if err != nil { msg := fmt.Sprintf("Error creating queue #%d: %s", queueNum, err) uiClient.SendWarningAlert(msg) log.Warning("Is opensnitchd already running?") log.Fatal(msg) } pktChan = queue.Packets() repeatQueueNum = queueNum + 1 repeatQueue, rqerr := netfilter.NewQueue(uint16(repeatQueueNum)) if rqerr != nil { msg := fmt.Sprintf("Error creating repeat queue #%d: %s", repeatQueueNum, rqerr) uiClient.SendErrorAlert(msg) log.Warning("Is opensnitchd already running?") log.Warning(msg) } repeatPktChan = repeatQueue.Packets() // queue is ready, run firewall rules and start intercepting connections if err = firewall.Init(uiClient.GetFirewallType(), &queueNum); err != nil { log.Warning("%s", err) uiClient.SendWarningAlert(err) } uiClient.Connect() listenToEvents() if overwriteLogging() { setupLogging() } // overwrite monitor method from configuration if the user has passed // the option via command line. if procmonMethod != "" { if err := monitor.ReconfigureMonitorMethod(procmonMethod, cfg.Ebpf.ModulesPath); err != nil { msg := fmt.Sprintf("Unable to set process monitor method via parameter: %v", err) uiClient.SendWarningAlert(msg) log.Warning(msg) } } go func(uiClient *ui.Client, ebpfPath string) { if err := dns.ListenerEbpf(ebpfPath); err != nil { msg := fmt.Sprintf("EBPF-DNS: Unable to attach ebpf listener: %s", err) log.Warning(msg) // don't display an alert, since this module is not critical uiClient.PostAlert( protocol.Alert_ERROR, protocol.Alert_GENERIC, protocol.Alert_SAVE_TO_DB, protocol.Alert_MEDIUM, msg) } }(uiClient, cfg.Ebpf.ModulesPath) initSystemdResolvedMonitor() log.Info("Running on netfilter queue #%d ...", queueNum) for { select { case <-ctx.Done(): goto Exit case pkt, ok := <-pktChan: if !ok { goto Exit } wrkChan <- pkt } } Exit: close(wrkChan) doCleanup(queue, repeatQueue) os.Exit(0) } ������������������������������opensnitch-1.6.9/daemon/netfilter/������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017126�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netfilter/packet.go���������������������������������������������������������0000664�0000000�0000000�00000003021�15003540030�0020720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netfilter import "C" import ( "github.com/google/gopacket" ) // packet consts const ( IPv4 = 4 ) // Verdict holds the action to perform on a packet (NF_DROP, NF_ACCEPT, etc) type Verdict C.uint // VerdictContainer struct type VerdictContainer struct { Mark uint32 Verdict Verdict Packet []byte } // Packet holds the data of a network packet type Packet struct { Packet gopacket.Packet verdictChannel chan VerdictContainer IfaceInIdx int IfaceOutIdx int Mark uint32 UID uint32 NetworkProtocol uint8 } // SetVerdict emits a veredict on a packet func (p *Packet) SetVerdict(v Verdict) { p.verdictChannel <- VerdictContainer{Verdict: v, Packet: nil, Mark: 0} } // SetVerdictAndMark emits a veredict on a packet and marks it in order to not // analyze it again. func (p *Packet) SetVerdictAndMark(v Verdict, mark uint32) { p.verdictChannel <- VerdictContainer{Verdict: v, Packet: nil, Mark: mark} } // SetRequeueVerdict apply a verdict on a requeued packet func (p *Packet) SetRequeueVerdict(newQueueID uint16) { v := uint(NF_QUEUE) q := (uint(newQueueID) << 16) v = v | q p.verdictChannel <- VerdictContainer{Verdict: Verdict(v), Packet: nil, Mark: p.Mark} } // SetVerdictWithPacket apply a verdict, but with a new packet func (p *Packet) SetVerdictWithPacket(v Verdict, packet []byte) { p.verdictChannel <- VerdictContainer{Verdict: v, Packet: packet, Mark: 0} } // IsIPv4 returns if the packet is IPv4 func (p *Packet) IsIPv4() bool { return p.NetworkProtocol == IPv4 } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netfilter/queue.c�����������������������������������������������������������0000664�0000000�0000000�00000000024�15003540030�0020412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "queue.h" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netfilter/queue.go����������������������������������������������������������0000664�0000000�0000000�00000015232�15003540030�0020604�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netfilter /* #cgo pkg-config: libnetfilter_queue #cgo CFLAGS: -I/usr/include #cgo LDFLAGS: -L/usr/lib64/ -ldl #include "queue.h" */ import "C" import ( "fmt" "os" "sync" "syscall" "time" "unsafe" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/gopacket" "github.com/google/gopacket/layers" "golang.org/x/sys/unix" ) const ( AF_INET = 2 AF_INET6 = 10 NF_DROP Verdict = 0 NF_ACCEPT Verdict = 1 NF_STOLEN Verdict = 2 NF_QUEUE Verdict = 3 NF_REPEAT Verdict = 4 NF_STOP Verdict = 5 NF_DEFAULT_QUEUE_SIZE uint32 = 4096 NF_DEFAULT_PACKET_SIZE uint32 = 4096 ) var ( queueIndex = make(map[uint32]*chan Packet, 0) queueIndexLock = sync.RWMutex{} gopacketDecodeOptions = gopacket.DecodeOptions{Lazy: true, NoCopy: true} ) // VerdictContainerC is the struct that contains the mark, action, length and // payload of a packet. // It's defined in queue.h, and filled on go_callback() type VerdictContainerC C.verdictContainer // Queue holds the information of a netfilter queue. // The handles of the connection to the kernel and the created queue. // A channel where the intercepted packets will be received. // The ID of the queue. type Queue struct { h *C.struct_nfq_handle qh *C.struct_nfq_q_handle packets chan Packet fd C.int idx uint32 } // NewQueue opens a new netfilter queue to receive packets marked with a mark. func NewQueue(queueID uint16) (q *Queue, err error) { q = &Queue{ idx: uint32(time.Now().UnixNano()), packets: make(chan Packet), } if err = q.create(queueID); err != nil { return nil, err } else if err = q.setup(); err != nil { return nil, err } go q.run() return q, nil } func (q *Queue) create(queueID uint16) (err error) { var ret C.int if q.h, err = C.nfq_open(); err != nil { return fmt.Errorf("Error opening Queue handle: %v", err) } else if ret, err = C.nfq_unbind_pf(q.h, AF_INET); err != nil || ret < 0 { errmsg := fmt.Errorf("Error %d unbinding existing q handler from AF_INET protocol family: %v", ret, err) if syscall.Errno(ret) == unix.EINVAL { errmsg = fmt.Errorf("%s\nRestarting your computer may help to solve this error (see issues: #323 and #912 for more information)", errmsg) } return errmsg } else if ret, err = C.nfq_unbind_pf(q.h, AF_INET6); err != nil || ret < 0 { return fmt.Errorf("Error (%d) unbinding existing q handler from AF_INET6 protocol family: %v", ret, err) } else if ret, err := C.nfq_bind_pf(q.h, AF_INET); err != nil || ret < 0 { return fmt.Errorf("Error (%d) binding to AF_INET protocol family: %v", ret, err) } else if ret, err := C.nfq_bind_pf(q.h, AF_INET6); err != nil || ret < 0 { return fmt.Errorf("Error (%d) binding to AF_INET6 protocol family: %v", ret, err) } else if q.qh, err = C.CreateQueue(q.h, C.uint16_t(queueID), C.uint32_t(q.idx)); err != nil || q.qh == nil { q.destroy() return fmt.Errorf("Error binding to queue: %v", err) } queueIndexLock.Lock() queueIndex[q.idx] = &q.packets queueIndexLock.Unlock() return nil } func (q *Queue) setup() (err error) { var ret C.int queueSize := C.uint32_t(NF_DEFAULT_QUEUE_SIZE) bufferSize := C.uint(NF_DEFAULT_PACKET_SIZE) totSize := C.uint(NF_DEFAULT_QUEUE_SIZE * NF_DEFAULT_PACKET_SIZE) if ret, err = C.nfq_set_queue_maxlen(q.qh, queueSize); err != nil || ret < 0 { q.destroy() return fmt.Errorf("Unable to set max packets in queue: %v", err) } else if C.nfq_set_mode(q.qh, C.uint8_t(2), bufferSize) < 0 { q.destroy() return fmt.Errorf("Unable to set packets copy mode: %v", err) } else if q.fd, err = C.nfq_fd(q.h); err != nil { q.destroy() return fmt.Errorf("Unable to get queue file-descriptor. %v", err) } else if C.nfnl_rcvbufsiz(C.nfq_nfnlh(q.h), totSize) < 0 { q.destroy() return fmt.Errorf("Unable to increase netfilter buffer space size") } return nil } func (q *Queue) run() { if errno := C.Run(q.h, q.fd); errno != 0 { fmt.Fprintf(os.Stderr, "Terminating, unable to receive packet due to errno=%d", errno) } } // Close ensures that nfqueue resources are freed and closed. // C.stop_reading_packets() stops the reading packets loop, which causes // go-subroutine run() to exit. // After exit, listening queue is destroyed and closed. // If for some reason any of the steps stucks while closing it, we'll exit by timeout. func (q *Queue) Close() { C.stop_reading_packets() q.destroy() queueIndexLock.Lock() delete(queueIndex, q.idx) queueIndexLock.Unlock() close(q.packets) } func (q *Queue) destroy() { // we'll try to exit cleanly, but sometimes nfqueue gets stuck time.AfterFunc(5*time.Second, func() { log.Warning("queue (%d) stuck, closing by timeout", q.idx) if q != nil { C.close(q.fd) q.closeNfq() } os.Exit(0) }) if q.qh != nil { if ret := C.nfq_destroy_queue(q.qh); ret != 0 { log.Warning("Queue.destroy() idx=%d, nfq_destroy_queue() not closed: %d", q.idx, ret) } } q.closeNfq() } func (q *Queue) closeNfq() { if q.h != nil { if ret := C.nfq_close(q.h); ret != 0 { log.Warning("Queue.destroy() idx=%d, nfq_close() not closed: %d", q.idx, ret) } } } // Packets return the list of enqueued packets. func (q *Queue) Packets() <-chan Packet { return q.packets } // FYI: the export keyword is mandatory to specify that go_callback is defined elsewhere //export go_callback func go_callback(queueID C.int, data *C.uchar, length C.int, mark C.uint, idx uint32, vc *VerdictContainerC, uid, devIn, devOut uint32) { (*vc).verdict = C.uint(NF_ACCEPT) (*vc).data = nil (*vc).mark_set = 0 (*vc).length = 0 queueIndexLock.RLock() queueChannel, found := queueIndex[idx] queueIndexLock.RUnlock() if !found { fmt.Fprintf(os.Stderr, "Unexpected queue idx %d\n", idx) return } xdata := C.GoBytes(unsafe.Pointer(data), length) p := Packet{ verdictChannel: make(chan VerdictContainer), Mark: uint32(mark), UID: uid, NetworkProtocol: xdata[0] >> 4, // first 4 bits is the version IfaceInIdx: int(devIn), IfaceOutIdx: int(devOut), } var packet gopacket.Packet if p.IsIPv4() { packet = gopacket.NewPacket(xdata, layers.LayerTypeIPv4, gopacketDecodeOptions) } else { packet = gopacket.NewPacket(xdata, layers.LayerTypeIPv6, gopacketDecodeOptions) } p.Packet = packet select { case *queueChannel <- p: select { case v := <-p.verdictChannel: if v.Packet == nil { (*vc).verdict = C.uint(v.Verdict) } else { (*vc).verdict = C.uint(v.Verdict) (*vc).data = (*C.uchar)(unsafe.Pointer(&v.Packet[0])) (*vc).length = C.uint(len(v.Packet)) } if v.Mark != 0 { (*vc).mark_set = C.uint(1) (*vc).mark = C.uint(v.Mark) } } case <-time.After(1 * time.Millisecond): fmt.Fprintf(os.Stderr, "Timed out while sending packet to queue channel %d\n", idx) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netfilter/queue.h�����������������������������������������������������������0000664�0000000�0000000�00000006320�15003540030�0020424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef _NETFILTER_QUEUE_H #define _NETFILTER_QUEUE_H #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <errno.h> #include <math.h> #include <unistd.h> #include <dlfcn.h> #include <netinet/in.h> #include <linux/types.h> #include <linux/socket.h> #include <linux/netfilter.h> #include <libnetfilter_queue/libnetfilter_queue.h> typedef struct { unsigned int verdict; unsigned int mark; unsigned int mark_set; unsigned int length; unsigned char *data; } verdictContainer; static void *get_uid = NULL; extern void go_callback(int id, unsigned char* data, int len, unsigned int mark, uint32_t idx, verdictContainer *vc, uint32_t uid, uint32_t in_dev, uint32_t out_dev); static uint8_t stop = 0; static inline void configure_uid_if_available(struct nfq_q_handle *qh){ void *hndl = dlopen("libnetfilter_queue.so.1", RTLD_LAZY); if (!hndl) { hndl = dlopen("libnetfilter_queue.so", RTLD_LAZY); if (!hndl){ printf("WARNING: libnetfilter_queue not available\n"); return; } } if ((get_uid = dlsym(hndl, "nfq_get_uid")) == NULL){ printf("WARNING: nfq_get_uid not available\n"); return; } printf("OK: libnetfiler_queue supports nfq_get_uid\n"); #ifdef NFQA_CFG_F_UID_GID if (qh != NULL && nfq_set_queue_flags(qh, NFQA_CFG_F_UID_GID, NFQA_CFG_F_UID_GID)){ printf("WARNING: UID not available on this kernel/libnetfilter_queue\n"); } #endif } static int nf_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *arg){ if (stop) { return -1; } uint32_t id = -1, idx = 0, mark = 0; struct nfqnl_msg_packet_hdr *ph = NULL; unsigned char *buffer = NULL; int size = 0; verdictContainer vc = {0}; uint32_t uid = 0xffffffff; uint32_t in_dev=0, out_dev=0; in_dev = nfq_get_indev(nfa); out_dev = nfq_get_outdev(nfa); mark = nfq_get_nfmark(nfa); ph = nfq_get_msg_packet_hdr(nfa); id = ntohl(ph->packet_id); size = nfq_get_payload(nfa, &buffer); idx = (uint32_t)((uintptr_t)arg); #ifdef NFQA_CFG_F_UID_GID if (get_uid) nfq_get_uid(nfa, &uid); #endif go_callback(id, buffer, size, mark, idx, &vc, uid, in_dev, out_dev); if( vc.mark_set == 1 ) { return nfq_set_verdict2(qh, id, vc.verdict, vc.mark, vc.length, vc.data); } return nfq_set_verdict2(qh, id, vc.verdict, vc.mark, vc.length, vc.data); } static inline struct nfq_q_handle* CreateQueue(struct nfq_handle *h, uint16_t queue, uint32_t idx) { struct nfq_q_handle* qh = nfq_create_queue(h, queue, &nf_callback, (void*)((uintptr_t)idx)); if (qh == NULL){ printf("ERROR: nfq_create_queue() queue not created\n"); } else { configure_uid_if_available(qh); } return qh; } static inline void stop_reading_packets() { stop = 1; } static inline int Run(struct nfq_handle *h, int fd) { char buf[4096] __attribute__ ((aligned)); int rcvd, opt = 1; setsockopt(fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(int)); while ((rcvd = recv(fd, buf, sizeof(buf), 0)) >= 0) { if (stop == 1) { return errno; } nfq_handle_packet(h, buf, rcvd); } return errno; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netlink/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016576�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netlink/ifaces.go�����������������������������������������������������������0000664�0000000�0000000�00000002322�15003540030�0020356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netlink import ( "net" "github.com/evilsocket/opensnitch/daemon/log" "github.com/vishvananda/netlink" ) // https://cs.opensource.google/go/go/+/refs/tags/go1.20.6:src/net/ip.go;l=133 // TODO: remove when upgrading go version. func isPrivate(ip net.IP) bool { if ip4 := ip.To4(); ip4 != nil { return ip4[0] == 10 || (ip4[0] == 172 && ip4[1]&0xf0 == 16) || (ip4[0] == 192 && ip4[1] == 168) } return len(ip) == 16 && ip[0]&0xfe == 0xfc } // GetLocalAddrs returns the list of local IPs func GetLocalAddrs() map[string]netlink.Addr { localAddresses := make(map[string]netlink.Addr) addr, err := netlink.AddrList(nil, netlink.FAMILY_ALL) if err != nil { log.Error("eBPF error looking up this machine's addresses via netlink: %v", err) return nil } for _, a := range addr { log.Debug("local addr: %+v\n", a) localAddresses[a.IP.String()] = a } return localAddresses } // AddrUpdateToAddr translates AddrUpdate struct to Addr. func AddrUpdateToAddr(addr *netlink.AddrUpdate) netlink.Addr { return netlink.Addr{ IPNet: &addr.LinkAddress, LinkIndex: addr.LinkIndex, Flags: addr.Flags, Scope: addr.Scope, PreferedLft: addr.PreferedLft, ValidLft: addr.ValidLft, } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netlink/socket.go�����������������������������������������������������������0000664�0000000�0000000�00000020250�15003540030�0020414�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netlink import ( "fmt" "net" "strconv" "syscall" "github.com/evilsocket/opensnitch/daemon/log" "github.com/vishvananda/netlink" "golang.org/x/sys/unix" ) // GetSocketInfo asks the kernel via netlink for a given connection. // If the connection is found, we return the uid and the possible // associated inodes. // If the outgoing connection is not found but there're entries with the source // port and same protocol, add all the inodes to the list. // // Some examples: // outgoing connection as seen by netfilter || connection details dumped from kernel // // 47344:192.168.1.106 -> 151.101.65.140:443 || in kernel: 47344:192.168.1.106 -> 151.101.65.140:443 // 8612:192.168.1.5 -> 192.168.1.255:8612 || in kernel: 8612:192.168.1.105 -> 0.0.0.0:0 // 123:192.168.1.5 -> 217.144.138.234:123 || in kernel: 123:0.0.0.0 -> 0.0.0.0:0 // 45015:127.0.0.1 -> 239.255.255.250:1900 || in kernel: 45015:127.0.0.1 -> 0.0.0.0:0 // 50416:fe80::9fc2:ddcf:df22:aa50 -> fe80::1:53 || in kernel: 50416:254.128.0.0 -> 254.128.0.0:53 // 51413:192.168.1.106 -> 103.224.182.250:1337 || in kernel: 51413:0.0.0.0 -> 0.0.0.0:0 func GetSocketInfo(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) (uid int, inodes []int) { uid = -1 family := uint8(syscall.AF_INET) ipproto := uint8(syscall.IPPROTO_TCP) protoLen := len(proto) if proto[protoLen-1:protoLen] == "6" { family = syscall.AF_INET6 } if proto[:3] == "udp" { ipproto = syscall.IPPROTO_UDP if protoLen >= 7 && proto[:7] == "udplite" { ipproto = syscall.IPPROTO_UDPLITE } } if protoLen >= 4 && proto[:4] == "sctp" { ipproto = syscall.IPPROTO_SCTP } if protoLen >= 4 && proto[:4] == "icmp" { ipproto = syscall.IPPROTO_RAW } if sockList, err := SocketGet(family, ipproto, uint16(srcPort), uint16(dstPort), srcIP, dstIP); err == nil { for n, sock := range sockList { if sock.UID != 0xffffffff { uid = int(sock.UID) } log.Debug("[%d/%d] outgoing connection uid: %d, %d:%v -> %v:%d || netlink response: %d:%v -> %v:%d inode: %d - loopback: %v multicast: %v unspecified: %v linklocalunicast: %v ifaceLocalMulticast: %v GlobalUni: %v ", n, len(sockList), int(sock.UID), srcPort, srcIP, dstIP, dstPort, sock.ID.SourcePort, sock.ID.Source, sock.ID.Destination, sock.ID.DestinationPort, sock.INode, sock.ID.Destination.IsLoopback(), sock.ID.Destination.IsMulticast(), sock.ID.Destination.IsUnspecified(), sock.ID.Destination.IsLinkLocalUnicast(), sock.ID.Destination.IsLinkLocalMulticast(), sock.ID.Destination.IsGlobalUnicast(), ) if sock.ID.SourcePort == uint16(srcPort) && sock.ID.Source.Equal(srcIP) && (sock.ID.DestinationPort == uint16(dstPort)) && ((sock.ID.Destination.IsGlobalUnicast() || sock.ID.Destination.IsLoopback()) && sock.ID.Destination.Equal(dstIP)) { inodes = append([]int{int(sock.INode)}, inodes...) continue } log.Debug("GetSocketInfo() invalid: %d:%v -> %v:%d", sock.ID.SourcePort, sock.ID.Source, sock.ID.Destination, sock.ID.DestinationPort) } // handle special cases (see function description): ntp queries (123), broadcasts, incomming connections. if len(inodes) == 0 && len(sockList) > 0 { for n, sock := range sockList { if sockList[n].ID.Destination.Equal(net.IPv4zero) || sockList[n].ID.Destination.Equal(net.IPv6zero) { inodes = append([]int{int(sock.INode)}, inodes...) log.Debug("netlink socket not found, adding entry: %d:%v -> %v:%d || %d:%v -> %v:%d inode: %d state: %s", srcPort, srcIP, dstIP, dstPort, sockList[n].ID.SourcePort, sockList[n].ID.Source, sockList[n].ID.Destination, sockList[n].ID.DestinationPort, sockList[n].INode, TCPStatesMap[sock.State]) } else if sock.ID.SourcePort == uint16(srcPort) && sock.ID.Source.Equal(srcIP) && (sock.ID.DestinationPort == uint16(dstPort)) { inodes = append([]int{int(sock.INode)}, inodes...) continue } else { log.Debug("netlink socket not found, EXCLUDING entry: %d:%v -> %v:%d || %d:%v -> %v:%d inode: %d state: %s", srcPort, srcIP, dstIP, dstPort, sockList[n].ID.SourcePort, sockList[n].ID.Source, sockList[n].ID.Destination, sockList[n].ID.DestinationPort, sockList[n].INode, TCPStatesMap[sock.State]) } } } } else { log.Debug("netlink socket error: %v - %d:%v -> %v:%d", err, srcPort, srcIP, dstIP, dstPort) } return uid, inodes } // GetSocketInfoByInode dumps the kernel sockets table and searches the given // inode on it. func GetSocketInfoByInode(inodeStr string) (*Socket, error) { inode, err := strconv.ParseUint(inodeStr, 10, 32) if err != nil { return nil, err } type inetStruct struct{ family, proto uint8 } socketTypes := []inetStruct{ {syscall.AF_INET, syscall.IPPROTO_TCP}, {syscall.AF_INET, syscall.IPPROTO_UDP}, {syscall.AF_INET6, syscall.IPPROTO_TCP}, {syscall.AF_INET6, syscall.IPPROTO_UDP}, } for _, socket := range socketTypes { socketList, err := SocketsDump(socket.family, socket.proto) if err != nil { return nil, err } for idx := range socketList { if uint32(inode) == socketList[idx].INode { return socketList[idx], nil } } } return nil, fmt.Errorf("Inode not found") } // KillSocket kills a socket given the properties of a connection. func KillSocket(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) { family := uint8(syscall.AF_INET) ipproto := uint8(syscall.IPPROTO_TCP) protoLen := len(proto) if proto[protoLen-1:protoLen] == "6" { family = syscall.AF_INET6 } if proto[:3] == "udp" { ipproto = syscall.IPPROTO_UDP if protoLen >= 7 && proto[:7] == "udplite" { ipproto = syscall.IPPROTO_UDPLITE } } if sockList, err := SocketGet(family, ipproto, uint16(srcPort), uint16(dstPort), srcIP, dstIP); err == nil { for _, s := range sockList { if err := SocketKill(family, ipproto, s.ID); err != nil { log.Debug("Unable to kill socket: %d, %d, %v", srcPort, dstPort, err) } } } } // KillSockets kills all sockets given a family and a protocol. // Be careful if you don't exclude local sockets, many local servers may misbehave, // entering in an infinite loop. func KillSockets(fam, proto uint8, excludeLocal bool) error { sockListTCP, err := SocketsDump(fam, proto) if err != nil { return fmt.Errorf("eBPF could not dump TCP (%d/%d) sockets via netlink: %v", fam, proto, err) } for _, sock := range sockListTCP { if excludeLocal && (isPrivate(sock.ID.Destination) || sock.ID.Source.IsUnspecified() || sock.ID.Destination.IsUnspecified()) { continue } if err := SocketKill(fam, proto, sock.ID); err != nil { log.Debug("Unable to kill socket (%+v): %s", sock.ID, err) } } return nil } // KillAllSockets kills the sockets for the given families and protocols. func KillAllSockets() { type opts struct { fam uint8 proto uint8 } optList := []opts{ // add families and protos as wish {unix.AF_INET, uint8(syscall.IPPROTO_TCP)}, {unix.AF_INET6, uint8(syscall.IPPROTO_TCP)}, {unix.AF_INET, uint8(syscall.IPPROTO_UDP)}, {unix.AF_INET6, uint8(syscall.IPPROTO_UDP)}, {unix.AF_INET, uint8(syscall.IPPROTO_SCTP)}, {unix.AF_INET6, uint8(syscall.IPPROTO_SCTP)}, } for _, opt := range optList { KillSockets(opt.fam, opt.proto, true) } } // FlushConnections flushes conntrack as soon as netfilter rule is set. // This ensures that already-established connections will go to netfilter queue. func FlushConnections() { if err := netlink.ConntrackTableFlush(netlink.ConntrackTable); err != nil { log.Error("error flushing ConntrackTable %s", err) } if err := netlink.ConntrackTableFlush(netlink.ConntrackExpectTable); err != nil { log.Error("error flusing ConntrackExpectTable %s", err) } // Force established connections to reestablish again. KillAllSockets() } // SocketsAreEqual compares 2 different sockets to see if they match. func SocketsAreEqual(aSocket, bSocket *Socket) bool { return ((*aSocket).INode == (*bSocket).INode && //inodes are unique enough, so the matches below will never have to be checked (*aSocket).ID.SourcePort == (*bSocket).ID.SourcePort && (*aSocket).ID.Source.Equal((*bSocket).ID.Source) && (*aSocket).ID.Destination.Equal((*bSocket).ID.Destination) && (*aSocket).ID.DestinationPort == (*bSocket).ID.DestinationPort && (*aSocket).UID == (*bSocket).UID) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netlink/socket_linux.go�����������������������������������������������������0000664�0000000�0000000�00000014546�15003540030�0021646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netlink import ( "encoding/binary" "errors" "fmt" "net" "syscall" "github.com/evilsocket/opensnitch/daemon/log" "github.com/vishvananda/netlink/nl" ) // This is a modification of https://github.com/vishvananda/netlink socket_linux.go - Apache2.0 license // which adds support for query UDP, UDPLITE and IPv6 sockets to SocketGet() const ( SOCK_DESTROY = 21 sizeofSocketID = 0x30 sizeofSocketRequest = sizeofSocketID + 0x8 sizeofSocket = sizeofSocketID + 0x18 ) var ( native = nl.NativeEndian() networkOrder = binary.BigEndian TCP_ALL = uint32(0xfff) ) // https://elixir.bootlin.com/linux/latest/source/include/net/tcp_states.h const ( TCP_INVALID = iota TCP_ESTABLISHED TCP_SYN_SENT TCP_SYN_RECV TCP_FIN_WAIT1 TCP_FIN_WAIT2 TCP_TIME_WAIT TCP_CLOSE TCP_CLOSE_WAIT TCP_LAST_ACK TCP_LISTEN TCP_CLOSING TCP_NEW_SYN_REC TCP_MAX_STATES ) // TCPStatesMap holds the list of TCP states var TCPStatesMap = map[uint8]string{ TCP_INVALID: "invalid", TCP_ESTABLISHED: "established", TCP_SYN_SENT: "syn_sent", TCP_SYN_RECV: "syn_recv", TCP_FIN_WAIT1: "fin_wait1", TCP_FIN_WAIT2: "fin_wait2", TCP_TIME_WAIT: "time_wait", TCP_CLOSE: "close", TCP_CLOSE_WAIT: "close_wait", TCP_LAST_ACK: "last_ack", TCP_LISTEN: "listen", TCP_CLOSING: "closing", } // SocketID holds the socket information of a request/response to the kernel type SocketID struct { SourcePort uint16 DestinationPort uint16 Source net.IP Destination net.IP Interface uint32 Cookie [2]uint32 } // Socket represents a netlink socket. type Socket struct { Family uint8 State uint8 Timer uint8 Retrans uint8 ID SocketID Expires uint32 RQueue uint32 WQueue uint32 UID uint32 INode uint32 } // SocketRequest holds the request/response of a connection to the kernel type SocketRequest struct { Family uint8 Protocol uint8 Ext uint8 pad uint8 States uint32 ID SocketID } type writeBuffer struct { Bytes []byte pos int } func (b *writeBuffer) Write(c byte) { b.Bytes[b.pos] = c b.pos++ } func (b *writeBuffer) Next(n int) []byte { s := b.Bytes[b.pos : b.pos+n] b.pos += n return s } // Serialize convert SocketRequest struct to bytes. func (r *SocketRequest) Serialize() []byte { b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)} b.Write(r.Family) b.Write(r.Protocol) b.Write(r.Ext) b.Write(r.pad) native.PutUint32(b.Next(4), r.States) networkOrder.PutUint16(b.Next(2), r.ID.SourcePort) networkOrder.PutUint16(b.Next(2), r.ID.DestinationPort) if r.Family == syscall.AF_INET6 { copy(b.Next(16), r.ID.Source) copy(b.Next(16), r.ID.Destination) } else { copy(b.Next(4), r.ID.Source.To4()) b.Next(12) copy(b.Next(4), r.ID.Destination.To4()) b.Next(12) } native.PutUint32(b.Next(4), r.ID.Interface) native.PutUint32(b.Next(4), r.ID.Cookie[0]) native.PutUint32(b.Next(4), r.ID.Cookie[1]) return b.Bytes } // Len returns the size of a socket request func (r *SocketRequest) Len() int { return sizeofSocketRequest } type readBuffer struct { Bytes []byte pos int } func (b *readBuffer) Read() byte { c := b.Bytes[b.pos] b.pos++ return c } func (b *readBuffer) Next(n int) []byte { s := b.Bytes[b.pos : b.pos+n] b.pos += n return s } func (s *Socket) deserialize(b []byte) error { if len(b) < sizeofSocket { return fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofSocket) } rb := readBuffer{Bytes: b} s.Family = rb.Read() s.State = rb.Read() s.Timer = rb.Read() s.Retrans = rb.Read() s.ID.SourcePort = networkOrder.Uint16(rb.Next(2)) s.ID.DestinationPort = networkOrder.Uint16(rb.Next(2)) if s.Family == syscall.AF_INET6 { s.ID.Source = net.IP(rb.Next(16)) s.ID.Destination = net.IP(rb.Next(16)) } else { s.ID.Source = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) rb.Next(12) s.ID.Destination = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) rb.Next(12) } s.ID.Interface = native.Uint32(rb.Next(4)) s.ID.Cookie[0] = native.Uint32(rb.Next(4)) s.ID.Cookie[1] = native.Uint32(rb.Next(4)) s.Expires = native.Uint32(rb.Next(4)) s.RQueue = native.Uint32(rb.Next(4)) s.WQueue = native.Uint32(rb.Next(4)) s.UID = native.Uint32(rb.Next(4)) s.INode = native.Uint32(rb.Next(4)) return nil } // SocketKill kills a connection func SocketKill(family, proto uint8, sockID SocketID) error { sockReq := &SocketRequest{ Family: family, Protocol: proto, ID: sockID, } req := nl.NewNetlinkRequest(SOCK_DESTROY, syscall.NLM_F_REQUEST|syscall.NLM_F_ACK) req.AddData(sockReq) _, err := req.Execute(syscall.NETLINK_INET_DIAG, 0) if err != nil { return err } return nil } // SocketGet returns the list of active connections in the kernel // filtered by several fields. Currently it returns connections // filtered by source port and protocol. func SocketGet(family uint8, proto uint8, srcPort, dstPort uint16, local, remote net.IP) ([]*Socket, error) { _Id := SocketID{ SourcePort: srcPort, Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE}, } sockReq := &SocketRequest{ Family: family, Protocol: proto, States: TCP_ALL, ID: _Id, } return netlinkRequest(sockReq, family, proto, srcPort, dstPort, local, remote) } // SocketsDump returns the list of all connections from the kernel func SocketsDump(family uint8, proto uint8) ([]*Socket, error) { sockReq := &SocketRequest{ Family: family, Protocol: proto, States: TCP_ALL, } return netlinkRequest(sockReq, 0, 0, 0, 0, nil, nil) } func netlinkRequest(sockReq *SocketRequest, family uint8, proto uint8, srcPort, dstPort uint16, local, remote net.IP) ([]*Socket, error) { req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, syscall.NLM_F_DUMP) req.AddData(sockReq) msgs, err := req.Execute(syscall.NETLINK_INET_DIAG, 0) if err != nil { return nil, err } if len(msgs) == 0 { return nil, errors.New("Warning, no message nor error from netlink, or no connections found") } var sock []*Socket for n, m := range msgs { s := &Socket{} if err = s.deserialize(m); err != nil { log.Error("[%d] netlink socket error: %s, %d:%v -> %v:%d - %d:%v -> %v:%d", n, TCPStatesMap[s.State], srcPort, local, remote, dstPort, s.ID.SourcePort, s.ID.Source, s.ID.Destination, s.ID.DestinationPort) continue } if s.INode == 0 { continue } sock = append([]*Socket{s}, sock...) } return sock, err } ����������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netlink/socket_test.go������������������������������������������������������0000664�0000000�0000000�00000005646�15003540030�0021467�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netlink import ( "fmt" "net" "os" "strconv" "strings" "testing" ) type Connection struct { SrcIP net.IP DstIP net.IP Protocol string SrcPort uint DstPort uint OutConn net.Conn Listener net.Listener } func EstablishConnection(proto, dst string) (net.Conn, error) { c, err := net.Dial(proto, dst) if err != nil { fmt.Println(err) return nil, err } return c, nil } func ListenOnPort(proto, port string) (net.Listener, error) { // TODO: UDP -> ListenUDP() or ListenPacket() l, err := net.Listen(proto, port) if err != nil { fmt.Println(err) return nil, err } return l, nil } func setupConnection(proto string, connChan chan *Connection) { listnr, _ := ListenOnPort(proto, "127.0.0.1:55555") conn, err := EstablishConnection(proto, "127.0.0.1:55555") if err != nil { connChan <- nil return } laddr := strings.Split(conn.LocalAddr().String(), ":") daddr := strings.Split(conn.RemoteAddr().String(), ":") sport, _ := strconv.Atoi(laddr[1]) dport, _ := strconv.Atoi(daddr[1]) lconn := &Connection{ SrcPort: uint(sport), DstPort: uint(dport), SrcIP: net.ParseIP(laddr[0]), DstIP: net.ParseIP(daddr[0]), Protocol: "tcp", Listener: listnr, OutConn: conn, } connChan <- lconn } // TestNetlinkQueries tests queries to the kernel to get the inode of a connection. // When using ProcFS as monitor method, we need that value to get the PID of an application. // We also need it if for any reason auditd or ebpf doesn't return the PID of the application. // TODO: test all the cases described in the GetSocketInfo() description. func TestNetlinkTCPQueries(t *testing.T) { // netlink tests disabled by default, they cause random failures on restricted // environments. if os.Getenv("NETLINK_TESTS") == "" { t.Skip("Skipping netlink tests. Use NETLINK_TESTS=1 to launch these tests.") } connChan := make(chan *Connection) go setupConnection("tcp", connChan) conn := <-connChan if conn == nil { t.Error("TestParseTCPConnection, conn nil") } var inodes []int uid := -1 t.Run("Test GetSocketInfo", func(t *testing.T) { uid, inodes = GetSocketInfo("tcp", conn.SrcIP, conn.SrcPort, conn.DstIP, conn.DstPort) if len(inodes) == 0 { t.Error("inodes empty") } if uid != os.Getuid() { t.Error("GetSocketInfo UID error:", uid, os.Getuid()) } }) t.Run("Test GetSocketInfoByInode", func(t *testing.T) { socket, err := GetSocketInfoByInode(fmt.Sprint(inodes[0])) if err != nil { t.Error("GetSocketInfoByInode error:", err) } if socket == nil { t.Error("GetSocketInfoByInode inode not found") } if socket.ID.SourcePort != uint16(conn.SrcPort) { t.Error("GetSocketInfoByInode dstPort error:", socket) } if socket.ID.DestinationPort != uint16(conn.DstPort) { t.Error("GetSocketInfoByInode dstPort error:", socket) } if socket.UID != uint32(os.Getuid()) { t.Error("GetSocketInfoByInode UID error:", socket, os.Getuid()) } }) conn.Listener.Close() } ������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netstat/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016614�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netstat/entry.go������������������������������������������������������������0000664�0000000�0000000�00000001404�15003540030�0020303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netstat import ( "net" ) // Entry holds the information of a /proc/net/* entry. // For example, /proc/net/tcp: // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode // 0: 0100007F:13AD 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 18083222 type Entry struct { Proto string SrcIP net.IP DstIP net.IP UserId int INode int SrcPort uint DstPort uint } // NewEntry creates a new entry with values from /proc/net/ func NewEntry(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint, userId int, iNode int) Entry { return Entry{ Proto: proto, SrcIP: srcIP, SrcPort: srcPort, DstIP: dstIP, DstPort: dstPort, UserId: userId, INode: iNode, } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netstat/find.go�������������������������������������������������������������0000664�0000000�0000000�00000002462�15003540030�0020067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netstat import ( "net" "strings" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) // FindEntry looks for the connection in the list of known connections in ProcFS. func FindEntry(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) *Entry { if entry := findEntryForProtocol(proto, srcIP, srcPort, dstIP, dstPort); entry != nil { return entry } ipv6Suffix := "6" if core.IPv6Enabled && strings.HasSuffix(proto, ipv6Suffix) == false { otherProto := proto + ipv6Suffix log.Debug("Searching for %s netstat entry instead of %s", otherProto, proto) if entry := findEntryForProtocol(otherProto, srcIP, srcPort, dstIP, dstPort); entry != nil { return entry } } return &Entry{ Proto: proto, SrcIP: srcIP, SrcPort: srcPort, DstIP: dstIP, DstPort: dstPort, UserId: -1, INode: -1, } } func findEntryForProtocol(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) *Entry { entries, err := Parse(proto) if err != nil { log.Warning("Error while searching for %s netstat entry: %s", proto, err) return nil } for _, entry := range entries { if srcIP.Equal(entry.SrcIP) && srcPort == entry.SrcPort && dstIP.Equal(entry.DstIP) && dstPort == entry.DstPort { return &entry } } return nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/netstat/parse.go������������������������������������������������������������0000664�0000000�0000000�00000005252�15003540030�0020261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netstat import ( "bufio" "encoding/binary" "net" "os" "regexp" "strconv" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) var ( parser = regexp.MustCompile(`(?i)` + `\d+:\s+` + // sl `([a-f0-9]{8,32}):([a-f0-9]{4})\s+` + // local_address `([a-f0-9]{8,32}):([a-f0-9]{4})\s+` + // rem_address `[a-f0-9]{2}\s+` + // st `[a-f0-9]{8}:[a-f0-9]{8}\s+` + // tx_queue rx_queue `[a-f0-9]{2}:[a-f0-9]{8}\s+` + // tr tm->when `[a-f0-9]{8}\s+` + // retrnsmt `(\d+)\s+` + // uid `\d+\s+` + // timeout `(\d+)\s+` + // inode `.+`) // stuff we don't care about ) func decToInt(n string) int { d, err := strconv.ParseInt(n, 10, 64) if err != nil { log.Fatal("Error while parsing %s to int: %s", n, err) } return int(d) } func hexToInt(h string) uint { d, err := strconv.ParseUint(h, 16, 64) if err != nil { log.Fatal("Error while parsing %s to int: %s", h, err) } return uint(d) } func hexToInt2(h string) (uint, uint) { if len(h) > 16 { d, err := strconv.ParseUint(h[:16], 16, 64) if err != nil { log.Fatal("Error while parsing %s to int: %s", h[16:], err) } d2, err := strconv.ParseUint(h[16:], 16, 64) if err != nil { log.Fatal("Error while parsing %s to int: %s", h[16:], err) } return uint(d), uint(d2) } d, err := strconv.ParseUint(h, 16, 64) if err != nil { log.Fatal("Error while parsing %s to int: %s", h[16:], err) } return uint(d), 0 } func hexToIP(h string) net.IP { n, m := hexToInt2(h) var ip net.IP if m != 0 { ip = make(net.IP, 16) // TODO: Check if this depends on machine endianness? binary.LittleEndian.PutUint32(ip, uint32(n>>32)) binary.LittleEndian.PutUint32(ip[4:], uint32(n)) binary.LittleEndian.PutUint32(ip[8:], uint32(m>>32)) binary.LittleEndian.PutUint32(ip[12:], uint32(m)) } else { ip = make(net.IP, 4) binary.LittleEndian.PutUint32(ip, uint32(n)) } return ip } // Parse scans and retrieves the opened connections, from /proc/net/ files func Parse(proto string) ([]Entry, error) { filename := core.ConcatStrings("/proc/net/", proto) fd, err := os.Open(filename) if err != nil { return nil, err } defer fd.Close() entries := make([]Entry, 0) scanner := bufio.NewScanner(fd) for lineno := 0; scanner.Scan(); lineno++ { // skip column names if lineno == 0 { continue } line := core.Trim(scanner.Text()) m := parser.FindStringSubmatch(line) if m == nil { log.Warning("Could not parse netstat line from %s: %s", filename, line) continue } entries = append(entries, NewEntry( proto, hexToIP(m[1]), hexToInt(m[2]), hexToIP(m[3]), hexToInt(m[4]), decToInt(m[5]), decToInt(m[6]), )) } return entries, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/opensnitchd-dinit�����������������������������������������������������������0000664�0000000�0000000�00000000325�15003540030�0020500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Application firewall OpenSnitch type = process command = /usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules restart = true smooth-recovery = yes restart-delay = 15 stop-timeout = 10 restart-limit-count = 0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/opensnitchd-openrc����������������������������������������������������������0000775�0000000�0000000�00000002004�15003540030�0020656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/sbin/openrc-run # OpenSnitch firewall service depend() { before net after iptables ip6tables use logger provide firewall } start_pre() { /bin/mkdir -p /etc/opensnitchd/rules /bin/chown -R root:root /etc/opensnitchd /bin/chown root:root /var/log/opensnitchd.log /bin/chmod -R 755 /etc/opensnitchd /bin/chmod -R 0644 /etc/opensnitchd/rules /bin/chmod 0600 /var/log/opensnitchd.log } start() { ebegin "Starting application firewall" # only if the verbose flag is not set (rc-service opensnitchd start -v) if [ -z "$VERBOSE" ]; then # redirect stdout and stderr to /dev/null /usr/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules -log-file /var/log/opensnitchd.log > /dev/null 2>&1 & else /usr/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules -log-file /var/log/opensnitchd.log fi eend $? } stop() { ebegin "Stopping application firewall" /usr/bin/pkill -SIGINT opensnitchd eend $? } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/opensnitchd.service���������������������������������������������������������0000664�0000000�0000000�00000000557�15003540030�0021041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Unit] Description=Application firewall OpenSnitch Documentation=https://github.com/evilsocket/opensnitch/wiki [Service] Type=simple PermissionsStartOnly=true ExecStartPre=/bin/mkdir -p /etc/opensnitchd/rules ExecStart=/usr/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules Restart=always RestartSec=30 TimeoutStopSec=10 [Install] WantedBy=multi-user.target �������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016607�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/activepids.go�������������������������������������������������������0000664�0000000�0000000�00000005070�15003540030�0021273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "io/ioutil" "strconv" "strings" "sync" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) type value struct { Process *Process //Starttime uniquely identifies a process, it is the 22nd value in /proc/<PID>/stat //if another process starts with the same PID, it's Starttime will be unique Starttime uint64 } var ( activePids = make(map[uint64]value) activePidsLock = sync.RWMutex{} ) //MonitorActivePids checks that each process in activePids //is still running and if not running (or another process with the same pid is running), //removes the pid from activePids func MonitorActivePids() { for { time.Sleep(time.Second) activePidsLock.Lock() for k, v := range activePids { data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.FormatUint(k, 10), "/stat")) if err != nil { //file does not exists, pid has quit delete(activePids, k) pidsCache.delete(int(k)) continue } startTime, err := strconv.ParseInt(strings.Split(string(data), " ")[21], 10, 64) if err != nil { log.Error("Could not find or convert Starttime. This should never happen. Please report this incident to the Opensnitch developers: %v", err) delete(activePids, k) pidsCache.delete(int(k)) continue } if uint64(startTime) != v.Starttime { //extremely unlikely: the original process has quit and another process //was started with the same PID - all this in less than 1 second log.Error("Same PID but different Starttime. Please report this incident to the Opensnitch developers.") delete(activePids, k) pidsCache.delete(int(k)) continue } } activePidsLock.Unlock() } } func findProcessInActivePidsCache(pid uint64) *Process { activePidsLock.Lock() defer activePidsLock.Unlock() if value, ok := activePids[pid]; ok { return value.Process } return nil } // AddToActivePidsCache adds the given pid to a list of known processes. func AddToActivePidsCache(pid uint64, proc *Process) { data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.FormatUint(pid, 10), "/stat")) if err != nil { //most likely the process has quit by now return } startTime, err2 := strconv.ParseInt(strings.Split(string(data), " ")[21], 10, 64) if err2 != nil { log.Error("Could not find or convert Starttime. This should never happen. Please report this incident to the Opensnitch developers: %v", err) return } activePidsLock.Lock() activePids[pid] = value{ Process: proc, Starttime: uint64(startTime), } activePidsLock.Unlock() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/activepids_test.go��������������������������������������������������0000664�0000000�0000000�00000006033�15003540030�0022332�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "math/rand" "os" "os/exec" "syscall" "testing" "time" ) //TestMonitorActivePids starts helper processes, adds them to activePids //and then kills them and checks if monitorActivePids() removed the killed processes //from activePids func TestMonitorActivePids(t *testing.T) { if os.Getenv("helperBinaryMode") == "on" { //we are in the "helper binary" mode, we were started with helperCmd.Start() (see below) //do nothing, just wait to be killed time.Sleep(time.Second * 10) os.Exit(1) //will never get here; but keep it here just in case } //we are in a normal "go test" mode tmpDir := "/tmp/ostest_" + randString() os.Mkdir(tmpDir, 0777) fmt.Println("tmp dir", tmpDir) defer os.RemoveAll(tmpDir) go MonitorActivePids() //build a "helper binary" with "go test -c -o /tmp/path" and put it into a tmp dir helperBinaryPath := tmpDir + "/helper1" goExecutable, _ := exec.LookPath("go") cmd := exec.Command(goExecutable, "test", "-c", "-o", helperBinaryPath) if err := cmd.Run(); err != nil { t.Error("Error running go test -c", err) } var numberOfHelpers = 5 var helperProcs []*Process //start helper binaries for i := 0; i < numberOfHelpers; i++ { var helperCmd *exec.Cmd helperCmd = &exec.Cmd{ Path: helperBinaryPath, Args: []string{helperBinaryPath}, Env: []string{"helperBinaryMode=on"}, } if err := helperCmd.Start(); err != nil { t.Error("Error starting helper binary", err) } go func() { helperCmd.Wait() //must Wait(), otherwise the helper process becomes a zombie when kill()ed }() pid := helperCmd.Process.Pid proc := NewProcess(pid, helperBinaryPath) helperProcs = append(helperProcs, proc) AddToActivePidsCache(uint64(pid), proc) } //sleep to make sure all processes started before we proceed time.Sleep(time.Second * 1) //make sure all PIDS are in the cache for i := 0; i < numberOfHelpers; i++ { proc := helperProcs[i] pid := proc.ID foundProc := findProcessInActivePidsCache(uint64(pid)) if foundProc == nil { t.Error("PID not found among active processes", pid) } if proc.Path != foundProc.Path || proc.ID != foundProc.ID { t.Error("PID or path doesn't match with the found process") } } //kill all helpers except for one for i := 0; i < numberOfHelpers-1; i++ { if err := syscall.Kill(helperProcs[i].ID, syscall.SIGTERM); err != nil { t.Error("error in syscall.Kill", err) } } //give the cache time to remove killed processes time.Sleep(time.Second * 1) //make sure only the alive process is in the cache foundProc := findProcessInActivePidsCache(uint64(helperProcs[numberOfHelpers-1].ID)) if foundProc == nil { t.Error("last alive PID is not found among active processes", foundProc) } if len(activePids) != 1 { t.Error("more than 1 active PIDs left in cache") } } func randString() string { rand.Seed(time.Now().UnixNano()) var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, 10) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } return string(b) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/audit/��������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017715�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/audit/client.go�����������������������������������������������������0000664�0000000�0000000�00000022076�15003540030�0021531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Package audit reads auditd events from the builtin af_unix plugin, and parses // the messages in order to proactively monitor pids which make connections. // Once a connection is made and redirected to us via NFQUEUE, we // lookup the connection inode in /proc, and add the corresponding PID with all // the information of the process to a list of known PIDs. // // TODO: Prompt the user to allow/deny a connection/program as soon as it's // started. // // Requisities: // - install auditd and audispd-plugins // - enable af_unix plugin /etc/audisp/plugins.d/af_unix.conf (active = yes) // - auditctl -a always,exit -F arch=b64 -S socket,connect,execve -k opensnitchd // - increase /etc/audisp/audispd.conf q_depth if there're dropped events // - set write_logs to no if you don't need/want audit logs to be stored in the disk. // // read messages from the pipe to verify that it's working: // socat unix-connect:/var/run/audispd_events stdio // // Audit event fields: // https://github.com/linux-audit/audit-documentation/blob/master/specs/fields/field-dictionary.csv // Record types: // https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Audit_Record_Types.html // // Documentation: // https://github.com/linux-audit/audit-documentation package audit import ( "bufio" "fmt" "io" "net" "os" "runtime" "sort" "sync" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) // Event represents an audit event, which in our case can be an event of type // socket, execve, socketpair or connect. type Event struct { Timestamp string // audit(xxxxxxx:nnnn) Serial string ProcName string // comm ProcPath string // exe ProcCmdLine string // proctitle ProcDir string // cwd ProcMode string // mode TTY string Pid int UID int Gid int PPid int EUid int EGid int OUid int OGid int UserName string // auid DstHost net.IP DstPort int NetFamily string // inet, inet6, local Success string INode int Dev string Syscall int Exit int EventType string RawEvent string LastSeen time.Time } // MaxEventAge is the maximum minutes an audit process can live without network activity. const ( MaxEventAge = int(10) ) var ( // Lock holds a mutex Lock sync.RWMutex ourPid = os.Getpid() // cache of events events []*Event eventsCleaner *time.Ticker eventsCleanerChan = (chan bool)(nil) // TODO: EventChan is an output channel where incoming auditd events will be written. // If a client opens it. EventChan = (chan Event)(nil) eventsExitChan = (chan bool)(nil) auditConn net.Conn // TODO: we may need arm arch rule64 = []string{"exit,always", "-F", "arch=b64", "-F", fmt.Sprint("ppid!=", ourPid), "-F", fmt.Sprint("pid!=", ourPid), "-S", "socket,connect", "-k", "opensnitch"} rule32 = []string{"exit,always", "-F", "arch=b32", "-F", fmt.Sprint("ppid!=", ourPid), "-F", fmt.Sprint("pid!=", ourPid), "-S", "socketcall", "-F", "a0=1", "-k", "opensnitch"} audispdPath = "/var/run/audispd_events" ) // OPENSNITCH_RULES_KEY is the mark we place on every event we are interested in. const ( OpensnitchRulesKey = "key=\"opensnitch\"" ) // GetEvents returns the list of processes which have opened a connection. func GetEvents() []*Event { return events } // GetEventByPid returns an event given a pid. func GetEventByPid(pid int) *Event { Lock.RLock() defer Lock.RUnlock() for _, event := range events { if pid == event.Pid { return event } } return nil } // sortEvents sorts received events by time and elapsed time since latest network activity. // newest PIDs will be placed on top of the list. func sortEvents() { sort.Slice(events, func(i, j int) bool { now := time.Now() elapsedTimeT := now.Sub(events[i].LastSeen) elapsedTimeU := now.Sub(events[j].LastSeen) t := events[i].LastSeen.UnixNano() u := events[j].LastSeen.UnixNano() return t > u && elapsedTimeT < elapsedTimeU }) } // cleanOldEvents deletes the PIDs which do not exist or that are too old to // live. // We start searching from the oldest to the newest. // If the last network activity of a PID has been greater than MaxEventAge, // then it'll be deleted. func cleanOldEvents() { Lock.Lock() defer Lock.Unlock() for n := len(events) - 1; n >= 0; n-- { now := time.Now() elapsedTime := now.Sub(events[n].LastSeen) if int(elapsedTime.Minutes()) >= MaxEventAge { events = append(events[:n], events[n+1:]...) continue } if core.Exists(fmt.Sprint("/proc/", events[n].Pid)) == false { events = append(events[:n], events[n+1:]...) } } } func deleteEvent(pid int) { for n := range events { if events[n].Pid == pid || events[n].PPid == pid { deleteEventByIndex(n) break } } } func deleteEventByIndex(index int) { Lock.Lock() events = append(events[:index], events[index+1:]...) Lock.Unlock() } // AddEvent adds new event to the list of PIDs which have generate network // activity. // If the PID is already in the list, the LastSeen field is updated, to keep // it alive. func AddEvent(aevent *Event) { if aevent == nil { return } Lock.Lock() defer Lock.Unlock() for n := 0; n < len(events); n++ { if events[n].Pid == aevent.Pid && events[n].Syscall == aevent.Syscall { if aevent.ProcCmdLine != "" || (aevent.ProcCmdLine == events[n].ProcCmdLine) { events[n] = aevent } events[n].LastSeen = time.Now() sortEvents() return } } aevent.LastSeen = time.Now() events = append([]*Event{aevent}, events...) } // startEventsCleaner will review if the events in the cache need to be cleaned // every 5 minutes. func startEventsCleaner() { for { select { case <-eventsCleanerChan: goto Exit case <-eventsCleaner.C: cleanOldEvents() } } Exit: log.Debug("audit: cleanerRoutine stopped") } func addRules() bool { r64 := append([]string{"-A"}, rule64...) r32 := append([]string{"-A"}, rule32...) _, err64 := core.Exec("auditctl", r64) _, err32 := core.Exec("auditctl", r32) if err64 == nil && err32 == nil { return true } log.Error("Error adding audit rule, err32=%v, err=%v", err32, err64) return false } func configureSyscalls() { // XXX: what about a i386 process running on a x86_64 system? if runtime.GOARCH == "386" { syscallSOCKET = "1" syscallCONNECT = "3" syscallSOCKETPAIR = "8" } } func deleteRules() bool { r64 := []string{"-D", "-k", "opensnitch"} r32 := []string{"-D", "-k", "opensnitch"} _, err64 := core.Exec("auditctl", r64) _, err32 := core.Exec("auditctl", r32) if err64 == nil && err32 == nil { return true } log.Error("Error deleting audit rules, err32=%v, err64=%v", err32, err64) return false } func checkRules() bool { // TODO return true } func checkStatus() bool { // TODO return true } // Reader reads events from audisd af_unix pipe plugin. // If the auditd daemon is stopped or restarted, the reader handle // is closed, so we need to restablished the connection. func Reader(r io.Reader, eventChan chan<- Event) { if r == nil { log.Error("Error reading auditd events. Is auditd running? is af_unix plugin enabled?") return } reader := bufio.NewReader(r) go startEventsCleaner() for { select { case <-eventsExitChan: goto Exit default: buf, _, err := reader.ReadLine() if err != nil { if err == io.EOF { log.Error("AuditReader: auditd stopped, reconnecting in 30s %s", err) if newReader, err := reconnect(); err == nil { reader = bufio.NewReader(newReader) log.Important("Auditd reconnected, continue reading") } continue } log.Warning("AuditReader: auditd error %s", err) break } parseEvent(string(buf[0:len(buf)]), eventChan) } } Exit: log.Debug("audit.Reader() closed") } // StartChannel creates a channel to receive events from Audit. // Launch audit.Reader() in a goroutine: // go audit.Reader(c, (chan<- audit.Event)(audit.EventChan)) func StartChannel() { EventChan = make(chan Event, 0) } func reconnect() (net.Conn, error) { deleteRules() time.Sleep(30 * time.Second) return connect() } func connect() (net.Conn, error) { addRules() // TODO: make the unix socket path configurable return net.Dial("unix", audispdPath) } // Stop stops listening for events from auditd and delete the auditd rules. func Stop() { if auditConn != nil { if err := auditConn.Close(); err != nil { log.Warning("audit.Stop() error closing socket: %v", err) } } if eventsCleaner != nil { eventsCleaner.Stop() } if eventsExitChan != nil { eventsExitChan <- true close(eventsExitChan) } if eventsCleanerChan != nil { eventsCleanerChan <- true close(eventsCleanerChan) } deleteRules() if EventChan != nil { close(EventChan) } } // Start makes a new connection to the audisp af_unix socket. func Start() (net.Conn, error) { auditConn, err := connect() if err != nil { log.Error("auditd Start() connection error %v", err) deleteRules() return nil, err } configureSyscalls() eventsCleaner = time.NewTicker(time.Minute * 5) eventsCleanerChan = make(chan bool) eventsExitChan = make(chan bool) return auditConn, err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/audit/parse.go������������������������������������������������������0000664�0000000�0000000�00000017246�15003540030�0021370�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package audit import ( "encoding/hex" "fmt" "net" "regexp" "strconv" "strings" ) var ( newEvent = false netEvent = &Event{} // RegExp for parse audit messages // https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/sec-understanding_audit_log_files auditRE, _ = regexp.Compile(`([a-zA-Z0-9\-_]+)=([a-zA-Z0-9:'\-\/\"\.\,_\(\)]+)`) rawEvent = make(map[string]string) ) // amd64 syscalls definition // if the platform is not amd64, it's redefined on Start() var ( syscallSOCKET = "41" syscallCONNECT = "42" syscallSOCKETPAIR = "53" syscallEXECVE = "59" syscallSOCKETCALL = "102" ) // /usr/include/x86_64-linux-gnu/bits/socket_type.h const ( sockSTREAM = "1" sockDGRAM = "2" sockRAW = "3" sockSEQPACKET = "5" sockPACKET = "10" // /usr/include/x86_64-linux-gnu/bits/socket.h pfUNSPEC = "0" pfLOCAL = "1" // PF_UNIX pfINET = "2" pfINET6 = "10" // /etc/protocols protoIP = "0" protoTCP = "6" protoUDP = "17" ) // https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Audit_Record_Types.html const ( AuditTypePROCTITLE = "type=PROCTITLE" AuditTypeCWD = "type=CWD" AuditTypePATH = "type=PATH" AuditTypeEXECVE = "type=EXECVE" AuditTypeSOCKADDR = "type=SOCKADDR" AuditTypeSOCKETCALL = "type=SOCKETCALL" AuditTypeEOE = "type=EOE" ) var ( syscallSOCKETstr = fmt.Sprint("syscall=", syscallSOCKET) syscallCONNECTstr = fmt.Sprint("syscall=", syscallCONNECT) syscallSOCKETPAIRstr = fmt.Sprint("syscall=", syscallSOCKETPAIR) syscallEXECVEstr = fmt.Sprint("syscall=", syscallEXECVE) syscallSOCKETCALLstr = fmt.Sprint("syscall=", syscallSOCKETCALL) ) // parseNetLine parses a SOCKADDR message type of the form: // saddr string: inet6 host:2001:4860:4860::8888 serv:53 func parseNetLine(line string, decode bool) (family string, dstHost net.IP, dstPort int) { // 0:4 - type // 4:8 - port // 8:16 - ip switch family := line[0:4]; family { // local // case "0100": // ipv4 case "0200": octet2 := decodeString(line[4:8]) octet := decodeString(line[8:16]) host := fmt.Sprint(octet[0], ".", octet[1], ".", octet[2], ".", octet[3]) fmt.Printf("dest ip: %s -- %s:%s\n", line[4:8], octet2, host) // ipv6 //case "0A00": } if decode == true { line = decodeString(line) } pieces := strings.Split(line, " ") family = pieces[0] if family[:4] != "inet" { return family, dstHost, 0 } if len(pieces) > 1 && pieces[1][:5] == "host:" { dstHost = net.ParseIP(strings.Split(pieces[1], "host:")[1]) } if len(pieces) > 2 && pieces[2][:5] == "serv:" { _dstPort, err := strconv.Atoi(strings.Split(line, "serv:")[1]) if err != nil { dstPort = -1 } else { dstPort = _dstPort } } return family, dstHost, dstPort } // decodeString will try to decode a string encoded in hexadecimal. // If the string can not be decoded, the original string will be returned. // In that case, usually it means that it's a non-encoded string. func decodeString(s string) string { decoded, err := hex.DecodeString(s) if err != nil { return s } return fmt.Sprintf("%s", decoded) } // extractFields parsed an audit raw message, and extracts all the fields. func extractFields(rawMessage string, newEvent *map[string]string) { Lock.Lock() defer Lock.Unlock() if auditRE == nil { newEvent = nil return } fieldList := auditRE.FindAllStringSubmatch(rawMessage, -1) if fieldList == nil { newEvent = nil return } for _, field := range fieldList { (*newEvent)[field[1]] = field[2] } } // populateEvent populates our Event from a raw parsed message. func populateEvent(aevent *Event, eventFields *map[string]string) *Event { if aevent == nil { return nil } Lock.Lock() defer Lock.Unlock() for k, v := range *eventFields { switch k { //case "a0": //case "a1": //case "a2": case "fam": if v == "local" { return nil } aevent.NetFamily = v case "lport": aevent.DstPort, _ = strconv.Atoi(v) // TODO /*case "addr": fmt.Println("addr: ", v) case "daddr": fmt.Println("daddr: ", v) case "laddr": aevent.DstHost = net.ParseIP(v) case "saddr": parseNetLine(v, true) fmt.Println("saddr:", v) */ case "exe": aevent.ProcPath = strings.Trim(decodeString(v), "\"") case "comm": aevent.ProcName = strings.Trim(decodeString(v), "\"") // proctitle may be truncated to 128 characters, so don't rely on it, parse /proc/<pid>/instead //case "proctitle": // aevent.ProcCmdLine = strings.Trim(decodeString(v), "\"") case "tty": aevent.TTY = v case "pid": aevent.Pid, _ = strconv.Atoi(v) case "ppid": aevent.PPid, _ = strconv.Atoi(v) case "uid": aevent.UID, _ = strconv.Atoi(v) case "gid": aevent.Gid, _ = strconv.Atoi(v) case "success": aevent.Success = v case "cwd": aevent.ProcDir = strings.Trim(decodeString(v), "\"") case "inode": aevent.INode, _ = strconv.Atoi(v) case "dev": aevent.Dev = v case "mode": aevent.ProcMode = v case "ouid": aevent.OUid, _ = strconv.Atoi(v) case "ogid": aevent.OGid, _ = strconv.Atoi(v) case "syscall": aevent.Syscall, _ = strconv.Atoi(v) case "exit": aevent.Exit, _ = strconv.Atoi(v) case "type": aevent.EventType = v case "msg": parts := strings.Split(v[6:], ":") aevent.Timestamp = parts[0] aevent.Serial = parts[1][:len(parts[1])-1] } } return aevent } // parseEvent parses an auditd event, discards the unwanted ones, and adds // the ones we're interested in to an array. // We're only interested in the socket,socketpair,connect and execve syscalls. // Events from us are excluded. // // When we received an event, we parse and add it to the list as soon as we can. // If the next messages of the set have additional information, we update the // event. func parseEvent(rawMessage string, eventChan chan<- Event) { if newEvent == false && strings.Index(rawMessage, OpensnitchRulesKey) == -1 { return } aEvent := make(map[string]string) if strings.Index(rawMessage, syscallSOCKETstr) != -1 || strings.Index(rawMessage, syscallCONNECTstr) != -1 || strings.Index(rawMessage, syscallSOCKETPAIRstr) != -1 || strings.Index(rawMessage, syscallEXECVEstr) != -1 || strings.Index(rawMessage, syscallSOCKETCALLstr) != -1 { extractFields(rawMessage, &aEvent) if aEvent == nil { return } newEvent = true netEvent = &Event{} netEvent = populateEvent(netEvent, &aEvent) AddEvent(netEvent) } else if newEvent == true && strings.Index(rawMessage, AuditTypePROCTITLE) != -1 { extractFields(rawMessage, &aEvent) if aEvent == nil { return } netEvent = populateEvent(netEvent, &aEvent) AddEvent(netEvent) } else if newEvent == true && strings.Index(rawMessage, AuditTypeCWD) != -1 { extractFields(rawMessage, &aEvent) if aEvent == nil { return } netEvent = populateEvent(netEvent, &aEvent) AddEvent(netEvent) } else if newEvent == true && strings.Index(rawMessage, AuditTypeEXECVE) != -1 { extractFields(rawMessage, &aEvent) if aEvent == nil { return } netEvent = populateEvent(netEvent, &aEvent) AddEvent(netEvent) } else if newEvent == true && strings.Index(rawMessage, AuditTypePATH) != -1 { extractFields(rawMessage, &aEvent) if aEvent == nil { return } netEvent = populateEvent(netEvent, &aEvent) AddEvent(netEvent) } else if newEvent == true && strings.Index(rawMessage, AuditTypeSOCKADDR) != -1 { extractFields(rawMessage, &aEvent) if aEvent == nil { return } netEvent = populateEvent(netEvent, &aEvent) AddEvent(netEvent) if EventChan != nil { eventChan <- *netEvent } } else if newEvent == true && strings.Index(rawMessage, AuditTypeEOE) != -1 { newEvent = false AddEvent(netEvent) if EventChan != nil { eventChan <- *netEvent } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/cache.go������������������������������������������������������������0000664�0000000�0000000�00000016465�15003540030�0020215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "os" "sort" "strconv" "sync" "time" "github.com/evilsocket/opensnitch/daemon/core" ) // InodeItem represents an item of the InodesCache. type InodeItem struct { FdPath string LastSeen int64 Pid int sync.RWMutex } // ProcItem represents an item of the pidsCache type ProcItem struct { FdPath string Descriptors []string LastSeen int64 Pid int sync.RWMutex } // CacheProcs holds the cache of processes that have established connections. type CacheProcs struct { items []*ProcItem sync.RWMutex } // CacheInodes holds the cache of Inodes. // The key is formed as follow: // inode+srcip+srcport+dstip+dstport type CacheInodes struct { items map[string]*InodeItem sync.RWMutex } var ( // cache of inodes, which help to not iterate over all the pidsCache and // descriptors of /proc/<pid>/fd/ // 15-50us vs 50-80ms // we hit this cache when: // - we've blocked a connection and the process retries it several times until it gives up, // - or when a process timeouts connecting to an IP/domain and it retries it again, // - or when a process resolves a domain and then connects to the IP. inodesCache = NewCacheOfInodes() maxTTL = 3 // maximum 3 minutes of inactivity in cache. Really rare, usually they lasts less than a minute. // 2nd cache of already known running pids, which also saves time by // iterating only over a few pids' descriptors, (30us-20ms vs. 50-80ms) // since it's more likely that most of the connections will be made by the // same (running) processes. // The cache is ordered by time, placing in the first places those PIDs with // active connections. pidsCache CacheProcs pidsDescriptorsCache = make(map[int][]string) cacheTicker = time.NewTicker(2 * time.Minute) ) // CacheCleanerTask checks periodically if the inodes in the cache must be removed. func CacheCleanerTask() { for { select { case <-cacheTicker.C: inodesCache.cleanup() } } } // NewCacheOfInodes returns a new cache for inodes. func NewCacheOfInodes() *CacheInodes { return &CacheInodes{ items: make(map[string]*InodeItem), } } //****************************************************************************** // items of the caches. func (i *InodeItem) updateTime() { i.Lock() i.LastSeen = time.Now().UnixNano() i.Unlock() } func (i *InodeItem) getTime() int64 { i.RLock() defer i.RUnlock() return i.LastSeen } func (p *ProcItem) updateTime() { p.Lock() p.LastSeen = time.Now().UnixNano() p.Unlock() } func (p *ProcItem) updateDescriptors(descriptors []string) { p.Lock() p.Descriptors = descriptors p.Unlock() } //****************************************************************************** // cache of processes func (c *CacheProcs) add(fdPath string, fdList []string, pid int) { c.Lock() defer c.Unlock() for n := range c.items { item := c.items[n] if item == nil { continue } if item.Pid == pid { item.updateTime() return } } procItem := &ProcItem{ Pid: pid, FdPath: fdPath, Descriptors: fdList, LastSeen: time.Now().UnixNano(), } c.setItems([]*ProcItem{procItem}, c.items) } func (c *CacheProcs) sort(pid int) { item := c.getItem(0) if item != nil && item.Pid == pid { return } c.RLock() defer c.RUnlock() sort.Slice(c.items, func(i, j int) bool { t := c.items[i].LastSeen u := c.items[j].LastSeen return t > u || t == u }) } func (c *CacheProcs) delete(pid int) { c.Lock() defer c.Unlock() for n, procItem := range c.items { if procItem.Pid == pid { c.deleteItem(n) inodesCache.delete(pid) break } } } func (c *CacheProcs) deleteItem(pos int) { nItems := len(c.items) if pos < nItems { c.setItems(c.items[:pos], c.items[pos+1:]) } } func (c *CacheProcs) setItems(newItems []*ProcItem, oldItems []*ProcItem) { c.items = append(newItems, oldItems...) } func (c *CacheProcs) getItem(index int) *ProcItem { c.RLock() defer c.RUnlock() if index >= len(c.items) { return nil } return c.items[index] } func (c *CacheProcs) getItems() []*ProcItem { return c.items } func (c *CacheProcs) countItems() int { c.RLock() defer c.RUnlock() return len(c.items) } // loop over the processes that have generated connections func (c *CacheProcs) getPid(inode int, inodeKey string, expect string) (int, int) { c.Lock() defer c.Unlock() for n, procItem := range c.items { if procItem == nil { continue } if idxDesc, _ := getPidDescriptorsFromCache(procItem.FdPath, inodeKey, expect, &procItem.Descriptors, procItem.Pid); idxDesc != -1 { procItem.updateTime() return procItem.Pid, n } descriptors := lookupPidDescriptors(procItem.FdPath, procItem.Pid) if descriptors == nil { c.deleteItem(n) continue } procItem.updateDescriptors(descriptors) if idxDesc, _ := getPidDescriptorsFromCache(procItem.FdPath, inodeKey, expect, &descriptors, procItem.Pid); idxDesc != -1 { procItem.updateTime() return procItem.Pid, n } } return -1, -1 } //****************************************************************************** // cache of inodes func (i *CacheInodes) add(key, descLink string, pid int) { i.Lock() defer i.Unlock() if descLink == "" { descLink = core.ConcatStrings("/proc/", strconv.Itoa(pid), "/exe") } i.items[key] = &InodeItem{ FdPath: descLink, Pid: pid, LastSeen: time.Now().UnixNano(), } } func (i *CacheInodes) delete(pid int) { i.Lock() defer i.Unlock() for k, inodeItem := range i.items { if inodeItem.Pid == pid { delete(i.items, k) } } } func (i *CacheInodes) getPid(inodeKey string) int { if item, ok := i.isInCache(inodeKey); ok { // sometimes the process may have disappeared at this point if _, err := os.Lstat(item.FdPath); err == nil { item.updateTime() return item.Pid } pidsCache.delete(item.Pid) i.delItem(inodeKey) } return -1 } func (i *CacheInodes) delItem(inodeKey string) { i.Lock() defer i.Unlock() delete(i.items, inodeKey) } func (i *CacheInodes) getItem(inodeKey string) *InodeItem { i.RLock() defer i.RUnlock() return i.items[inodeKey] } func (i *CacheInodes) getItems() map[string]*InodeItem { i.RLock() defer i.RUnlock() return i.items } func (i *CacheInodes) isInCache(inodeKey string) (*InodeItem, bool) { i.RLock() defer i.RUnlock() if item, found := i.items[inodeKey]; found { return item, true } return nil, false } func (i *CacheInodes) cleanup() { now := time.Now() i.Lock() defer i.Unlock() for k := range i.items { if i.items[k] == nil { continue } lastSeen := now.Sub( time.Unix(0, i.items[k].getTime()), ) if core.Exists(i.items[k].FdPath) == false || int(lastSeen.Minutes()) > maxTTL { delete(i.items, k) } } } func getPidDescriptorsFromCache(fdPath, inodeKey, expect string, descriptors *[]string, pid int) (int, *[]string) { for fdIdx := 0; fdIdx < len(*descriptors); fdIdx++ { descLink := core.ConcatStrings(fdPath, (*descriptors)[fdIdx]) if link, err := os.Readlink(descLink); err == nil && link == expect { if fdIdx > 0 { // reordering helps to reduce look up times by a factor of 10. fd := (*descriptors)[fdIdx] *descriptors = append((*descriptors)[:fdIdx], (*descriptors)[fdIdx+1:]...) *descriptors = append([]string{fd}, *descriptors...) } if _, ok := inodesCache.isInCache(inodeKey); ok { inodesCache.add(inodeKey, descLink, pid) } return fdIdx, descriptors } } return -1, descriptors } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/cache_test.go�������������������������������������������������������0000664�0000000�0000000�00000006605�15003540030�0021247�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "testing" "time" ) func TestCacheProcs(t *testing.T) { fdList := []string{"0", "1", "2"} pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid) t.Log("Pids in cache: ", pidsCache.countItems()) t.Run("Test addProcEntry", func(t *testing.T) { if pidsCache.countItems() != 1 { t.Error("pidsCache should be 1") } }) oldPid := pidsCache.getItem(0) pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid) t.Run("Test addProcEntry update", func(t *testing.T) { if pidsCache.countItems() != 1 { t.Error("pidsCache should still be 1!", pidsCache) } oldTime := time.Unix(0, oldPid.LastSeen) newTime := time.Unix(0, pidsCache.getItem(0).LastSeen) if oldTime.Equal(newTime) == false { t.Error("pidsCache, time not updated: ", oldTime, newTime) } }) pidsCache.add("/proc/2/fd", fdList, 2) pidsCache.delete(2) t.Run("Test deleteProcEntry", func(t *testing.T) { if pidsCache.countItems() != 1 { t.Error("pidsCache should be 1:", pidsCache.countItems()) } }) pid, _ := pidsCache.getPid(0, "", "/dev/null") t.Run("Test getPidFromCache", func(t *testing.T) { if pid != myPid { t.Error("pid not found in cache", pidsCache.countItems()) } }) // should not crash, and the number of items should still be 1 pidsCache.deleteItem(1) t.Run("Test deleteItem check bounds", func(t *testing.T) { if pidsCache.countItems() != 1 { t.Error("deleteItem check bounds error", pidsCache.countItems()) } }) pidsCache.deleteItem(0) t.Run("Test deleteItem", func(t *testing.T) { if pidsCache.countItems() != 0 { t.Error("deleteItem error", pidsCache.countItems()) } }) t.Log("items in cache:", pidsCache.countItems()) // the key of an inodeCache entry is formed as: inodeNumer + srcIP + srcPort + dstIP + dstPort inodeKey := "000000000127.0.0.144444127.0.0.153" // add() expects a path to the inode fd (/proc/<pid>/fd/12345), but as getPid() will check the path in order to retrieve the pid, // we just set it to "" and it'll use /proc/<pid>/exe inodesCache.add(inodeKey, "", myPid) t.Run("Test addInodeEntry", func(t *testing.T) { if _, found := inodesCache.items[inodeKey]; !found { t.Error("inodesCache, inode not added:", len(inodesCache.items), inodesCache.items) } }) pid = inodesCache.getPid(inodeKey) t.Run("Test getPidByInodeFromCache", func(t *testing.T) { if pid != myPid { t.Error("inode not found in cache", pid, inodeKey, len(inodesCache.items), inodesCache.items) } }) // should delete all inodes of a pid inodesCache.delete(myPid) t.Run("Test deleteInodeEntry", func(t *testing.T) { if _, found := inodesCache.items[inodeKey]; found { t.Error("inodesCache, key found in cache but it should not exist", inodeKey, len(inodesCache.items), inodesCache.items) } }) } // Test getPidDescriptorsFromCache descriptors (inodes) reordering. // When an inode (descriptor) is found, if it's pushed to the top of the list, // the next time we look for it will cost -10x. // Without reordering, the inode 0 will always be found on the 10th position, // taking an average of 100us instead of 30. // Benchmark results with reordering: ~5600ns/op, without: ~56000ns/op. func BenchmarkGetPid(b *testing.B) { fdList := []string{"10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"} pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid) for i := 0; i < b.N; i++ { pidsCache.getPid(0, "", "/dev/null") } } ���������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/details.go����������������������������������������������������������0000664�0000000�0000000�00000021252�15003540030�0020565�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "bufio" "fmt" "io/ioutil" "os" "regexp" "strconv" "strings" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/dns" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/netlink" ) var socketsRegex, _ = regexp.Compile(`socket:\[([0-9]+)\]`) // GetInfo collects information of a process. func (p *Process) GetInfo() error { if os.Getpid() == p.ID { return nil } // if the PID dir doesn't exist, the process may have exited or be a kernel connection // XXX: can a kernel connection exist without an entry in ProcFS? if p.Path == "" && p.IsAlive() == false { log.Debug("PID can't be read /proc/ %d %s", p.ID, p.Comm) // The Comm field shouldn't be empty if the proc monitor method is ebpf or audit. // If it's proc and the corresponding entry doesn't exist, there's nothing we can // do to inform the user about this process. if p.Comm == "" { return fmt.Errorf("Unable to get process information") } } p.ReadCmdline() p.ReadComm() p.ReadCwd() if err := p.ReadPath(); err != nil { log.Error("GetInfo() path can't be read") return err } p.ReadEnv() return nil } // GetExtraInfo collects information of a process. func (p *Process) GetExtraInfo() error { p.ReadEnv() p.readDescriptors() p.readIOStats() p.readStatus() return nil } // ReadComm reads the comm name from ProcFS /proc/<pid>/comm func (p *Process) ReadComm() error { if p.Comm != "" { return nil } data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/comm")) if err != nil { return err } p.Comm = core.Trim(string(data)) return nil } // ReadCwd reads the current working directory name from ProcFS /proc/<pid>/cwd func (p *Process) ReadCwd() error { if p.CWD != "" { return nil } link, err := os.Readlink(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/cwd")) if err != nil { return err } p.CWD = link return nil } // ReadEnv reads and parses the environment variables of a process. func (p *Process) ReadEnv() { data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/environ")) if err != nil { return } for _, s := range strings.Split(string(data), "\x00") { parts := strings.SplitN(core.Trim(s), "=", 2) if parts != nil && len(parts) == 2 { key := core.Trim(parts[0]) val := core.Trim(parts[1]) p.Env[key] = val } } } // ReadPath reads the symbolic link that /proc/<pid>/exe points to. // Note 1: this link might not exist on the root filesystem, it might // have been executed from a container, so the real path would be: // /proc/<pid>/root/<path that 'exe' points to> // // Note 2: // There're at least 3 things that a (regular) kernel connection meets // from userspace POV: // - /proc/<pid>/cmdline and /proc/<pid>/maps empty // - /proc/<pid>/exe can't be read func (p *Process) ReadPath() error { // avoid rereading the path if p.Path != "" && core.IsAbsPath(p.Path) { return nil } defer func() { if p.Path == "" { // determine if this process might be of a kernel task. if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/maps")); err == nil && len(data) == 0 { p.Path = "Kernel connection" p.Args = append(p.Args, p.Comm) return } p.Path = p.Comm } }() linkName := core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/exe") if _, err := os.Lstat(linkName); err != nil { return err } // FIXME: this reading can give error: file name too long link, err := os.Readlink(linkName) if err != nil { return err } p.SetPath(link) return nil } // SetPath sets the path of the process, and fixes it if it's needed. func (p *Process) SetPath(path string) { p.Path = path p.CleanPath() } // ReadCmdline reads the cmdline of the process from ProcFS /proc/<pid>/cmdline // This file may be empty if the process is of a kernel task. // It can also be empty for short-lived processes. func (p *Process) ReadCmdline() { if len(p.Args) > 0 { return } if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/cmdline")); err == nil { if len(data) == 0 { return } for i, b := range data { if b == 0x00 { data[i] = byte(' ') } } args := strings.Split(string(data), " ") for _, arg := range args { arg = core.Trim(arg) if arg != "" { p.Args = append(p.Args, arg) } } } p.CleanArgs() } // CleanArgs applies fixes on the cmdline arguments. // - AppImages cmdline reports the execuable launched as /proc/self/exe, // instead of the actual path to the binary. func (p *Process) CleanArgs() { if len(p.Args) > 0 && p.Args[0] == ProcSelf { p.Args[0] = p.Path } } func (p *Process) readDescriptors() { f, err := os.Open(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/fd/")) if err != nil { return } fDesc, err := f.Readdir(-1) f.Close() p.Descriptors = nil for _, fd := range fDesc { tempFd := &procDescriptors{ Name: fd.Name(), } if link, err := os.Readlink(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/fd/", fd.Name())); err == nil { tempFd.SymLink = link socket := socketsRegex.FindStringSubmatch(link) if len(socket) > 0 { socketInfo, err := netlink.GetSocketInfoByInode(socket[1]) if err == nil { tempFd.SymLink = fmt.Sprintf("socket:[%s] - %d:%s -> %s:%d, state: %s", fd.Name(), socketInfo.ID.SourcePort, socketInfo.ID.Source.String(), dns.HostOr(socketInfo.ID.Destination, socketInfo.ID.Destination.String()), socketInfo.ID.DestinationPort, netlink.TCPStatesMap[socketInfo.State]) } } if linkInfo, err := os.Lstat(link); err == nil { tempFd.Size = linkInfo.Size() tempFd.ModTime = linkInfo.ModTime() } } p.Descriptors = append(p.Descriptors, tempFd) } } func (p *Process) readIOStats() { f, err := os.Open(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/io")) if err != nil { return } defer f.Close() p.IOStats = &procIOstats{} scanner := bufio.NewScanner(f) for scanner.Scan() { s := strings.Split(scanner.Text(), " ") switch s[0] { case "rchar:": p.IOStats.RChar, _ = strconv.ParseInt(s[1], 10, 64) case "wchar:": p.IOStats.WChar, _ = strconv.ParseInt(s[1], 10, 64) case "syscr:": p.IOStats.SyscallRead, _ = strconv.ParseInt(s[1], 10, 64) case "syscw:": p.IOStats.SyscallWrite, _ = strconv.ParseInt(s[1], 10, 64) case "read_bytes:": p.IOStats.ReadBytes, _ = strconv.ParseInt(s[1], 10, 64) case "write_bytes:": p.IOStats.WriteBytes, _ = strconv.ParseInt(s[1], 10, 64) } } } func (p *Process) readStatus() { if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/status")); err == nil { p.Status = string(data) } if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/stat")); err == nil { p.Stat = string(data) } if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/stack")); err == nil { p.Stack = string(data) } if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/maps")); err == nil { p.Maps = string(data) } if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/statm")); err == nil { p.Statm = &procStatm{} fmt.Sscanf(string(data), "%d %d %d %d %d %d %d", &p.Statm.Size, &p.Statm.Resident, &p.Statm.Shared, &p.Statm.Text, &p.Statm.Lib, &p.Statm.Data, &p.Statm.Dt) } } // CleanPath applies fixes on the path to the binary: // - Remove extra characters from the link that it points to. // When a running process is deleted, the symlink has the bytes " (deleted") // appended to the link. // - If the path is /proc/self/exe, resolve the symlink that it points to. func (p *Process) CleanPath() { // Sometimes the path to the binary reported is the symbolic link of the process itself. // This is not useful to the user, and besides it's a generic path that can represent // to any process. // Therefore we cannot use /proc/self/exe directly, because it resolves to our own process. if strings.HasPrefix(p.Path, ProcSelf) { if link, err := os.Readlink(core.ConcatStrings(ProcSelf, "/exe")); err == nil { p.Path = link return } if len(p.Args) > 0 && p.Args[0] != "" { p.Path = p.Args[0] return } p.Path = p.Comm } pathLen := len(p.Path) if pathLen >= 10 && p.Path[pathLen-10:] == " (deleted)" { p.Path = p.Path[:len(p.Path)-10] } // We may receive relative paths from kernel, but the path of a process must be absolute if core.IsAbsPath(p.Path) == false { if err := p.ReadPath(); err != nil { log.Debug("ClenPath() error reading process path%s", err) return } } } // IsAlive checks if the process is still running func (p *Process) IsAlive() bool { return core.Exists(core.ConcatStrings("/proc/", strconv.Itoa(p.ID))) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/���������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017523�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/cache.go�������������������������������������������������������0000664�0000000�0000000�00000007117�15003540030�0021123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "sync" "time" "github.com/evilsocket/opensnitch/daemon/procmon" ) // NewExecEvent constructs a new execEvent from the arguments. func NewExecEvent(pid, ppid, uid uint32, path string, comm [16]byte) *execEvent { ev := &execEvent{ Type: EV_TYPE_EXEC, PID: pid, PPID: ppid, UID: uid, Comm: comm, } length := MaxPathLen if len(path) < MaxPathLen { length = len(path) } copy(ev.Filename[:], path[:length]) return ev } type execEventItem struct { Proc procmon.Process Event execEvent LastSeen int64 } type eventsStore struct { execEvents map[uint32]*execEventItem sync.RWMutex } // NewEventsStore creates a new store of events. func NewEventsStore() *eventsStore { return &eventsStore{ execEvents: make(map[uint32]*execEventItem), } } func (e *eventsStore) add(key uint32, event execEvent, proc procmon.Process) { e.Lock() defer e.Unlock() e.execEvents[key] = &execEventItem{ Proc: proc, Event: event, } } func (e *eventsStore) isInStore(key uint32) (item *execEventItem, found bool) { e.RLock() defer e.RUnlock() item, found = e.execEvents[key] return } func (e *eventsStore) delete(key uint32) { e.Lock() defer e.Unlock() delete(e.execEvents, key) } func (e *eventsStore) DeleteOldItems() { e.Lock() defer e.Unlock() for k, item := range e.execEvents { if item.Proc.IsAlive() == false { delete(e.execEvents, k) } } } //----------------------------------------------------------------------------- type ebpfCacheItem struct { Key []byte Proc procmon.Process LastSeen int64 } type ebpfCacheType struct { Items map[interface{}]*ebpfCacheItem sync.RWMutex } var ( maxTTL = 40 // Seconds maxCacheItems = 5000 ebpfCache *ebpfCacheType ebpfCacheTicker *time.Ticker ) // NewEbpfCacheItem creates a new cache item. func NewEbpfCacheItem(key []byte, proc procmon.Process) *ebpfCacheItem { return &ebpfCacheItem{ Key: key, Proc: proc, LastSeen: time.Now().UnixNano(), } } func (i *ebpfCacheItem) isValid() bool { lastSeen := time.Now().Sub( time.Unix(0, i.LastSeen), ) return int(lastSeen.Seconds()) < maxTTL } // NewEbpfCache creates a new cache store. func NewEbpfCache() *ebpfCacheType { ebpfCacheTicker = time.NewTicker(1 * time.Minute) return &ebpfCacheType{ Items: make(map[interface{}]*ebpfCacheItem, 0), } } func (e *ebpfCacheType) addNewItem(key interface{}, itemKey []byte, proc procmon.Process) { e.Lock() e.Items[key] = NewEbpfCacheItem(itemKey, proc) e.Unlock() } func (e *ebpfCacheType) isInCache(key interface{}) (item *ebpfCacheItem, found bool) { leng := e.Len() e.Lock() item, found = e.Items[key] if found { if item.isValid() { e.update(key, item) } else { found = false delete(e.Items, key) } } e.Unlock() if leng > maxCacheItems { e.DeleteOldItems() } return } func (e *ebpfCacheType) update(key interface{}, item *ebpfCacheItem) { item.LastSeen = time.Now().UnixNano() e.Items[key] = item } func (e *ebpfCacheType) Len() int { e.RLock() defer e.RUnlock() return len(e.Items) } func (e *ebpfCacheType) DeleteOldItems() { length := e.Len() e.Lock() defer e.Unlock() for k, item := range e.Items { if length > maxCacheItems || (item != nil && !item.isValid()) { delete(e.Items, k) } } } func (e *ebpfCacheType) delete(key interface{}) { e.Lock() defer e.Unlock() if key, found := e.Items[key]; found { delete(e.Items, key) } } func (e *ebpfCacheType) clear() { if e == nil { return } e.Lock() defer e.Unlock() for k := range e.Items { delete(e.Items, k) } if ebpfCacheTicker != nil { ebpfCacheTicker.Stop() } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/debug.go�������������������������������������������������������0000664�0000000�0000000�00000005151�15003540030�0021142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "fmt" "os/exec" "strconv" "syscall" "unsafe" "github.com/evilsocket/opensnitch/daemon/log" daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" elf "github.com/iovisor/gobpf/elf" ) // print map contents. used only for debugging func dumpMap(bpfmap *elf.Map, isIPv6 bool) { var lookupKey []byte var nextKey []byte var value []byte if !isIPv6 { lookupKey = make([]byte, 12) nextKey = make([]byte, 12) } else { lookupKey = make([]byte, 36) nextKey = make([]byte, 36) } value = make([]byte, 40) firstrun := true i := 0 for { i++ ok, err := m.LookupNextElement(bpfmap, unsafe.Pointer(&lookupKey[0]), unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value[0])) if err != nil { log.Error("eBPF LookupNextElement error: %v", err) return } if firstrun { // on first run lookupKey is a dummy, nothing to delete firstrun = false copy(lookupKey, nextKey) continue } fmt.Println("key, value", lookupKey, value) if !ok { //reached end of map break } copy(lookupKey, nextKey) } } //PrintEverything prints all the stats. used only for debugging func PrintEverything() { bash, _ := exec.LookPath("bash") //get the number of the first map out, err := exec.Command(bash, "-c", "bpftool map show | head -n 1 | cut -d ':' -f1").Output() if err != nil { fmt.Println("bpftool map dump name tcpMap ", err) } i, _ := strconv.Atoi(string(out[:len(out)-1])) fmt.Println("i is", i) //dump all maps for analysis for j := i; j < i+14; j++ { _, _ = exec.Command(bash, "-c", "bpftool map dump id "+strconv.Itoa(j)+" > dump"+strconv.Itoa(j)).Output() } alreadyEstablished.RLock() for sock1, v := range alreadyEstablished.TCP { fmt.Println(*sock1, v) } fmt.Println("---------------------") for sock1, v := range alreadyEstablished.TCPv6 { fmt.Println(*sock1, v) } alreadyEstablished.RUnlock() fmt.Println("---------------------") sockets, _ := daemonNetlink.SocketsDump(syscall.AF_INET, syscall.IPPROTO_TCP) for idx := range sockets { fmt.Println("socket tcp: ", sockets[idx]) } fmt.Println("---------------------") sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET6, syscall.IPPROTO_TCP) for idx := range sockets { fmt.Println("socket tcp6: ", sockets[idx]) } fmt.Println("---------------------") sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET, syscall.IPPROTO_UDP) for idx := range sockets { fmt.Println("socket udp: ", sockets[idx]) } fmt.Println("---------------------") sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET6, syscall.IPPROTO_UDP) for idx := range sockets { fmt.Println("socket udp6: ", sockets[idx]) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/ebpf.go��������������������������������������������������������0000664�0000000�0000000�00000013322�15003540030�0020767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "context" "encoding/binary" "fmt" "sync" "syscall" "unsafe" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" "github.com/evilsocket/opensnitch/daemon/procmon" elf "github.com/iovisor/gobpf/elf" "github.com/vishvananda/netlink" ) // contains pointers to ebpf maps for a given protocol (tcp/udp/v6) type ebpfMapsForProto struct { bpfmap *elf.Map } //Not in use, ~4usec faster lookup compared to m.LookupElement() // mimics union bpf_attr's anonymous struct used by BPF_MAP_*_ELEM commands // from <linux_headers>/include/uapi/linux/bpf.h type bpf_lookup_elem_t struct { map_fd uint64 //even though in bpf.h its type is __u32, we must make it 8 bytes long //because "key" is of type __aligned_u64, i.e. "key" must be aligned on an 8-byte boundary key uintptr value uintptr } type alreadyEstablishedConns struct { TCP map[*daemonNetlink.Socket]int TCPv6 map[*daemonNetlink.Socket]int sync.RWMutex } // list of returned errors const ( NoError = iota NotAvailable EventsNotAvailable ) // Error returns the error type and a message with the explanation type Error struct { Msg error What int } var ( m, perfMod *elf.Module lock = sync.RWMutex{} mapSize = uint(12000) ebpfMaps map[string]*ebpfMapsForProto modulesPath string //connections which were established at the time when opensnitch started alreadyEstablished = alreadyEstablishedConns{ TCP: make(map[*daemonNetlink.Socket]int), TCPv6: make(map[*daemonNetlink.Socket]int), } ctxTasks context.Context cancelTasks context.CancelFunc running = false maxKernelEvents = 32768 kernelEvents = make(chan interface{}, maxKernelEvents) // list of local addresses of this machine localAddresses = make(map[string]netlink.Addr) hostByteOrder binary.ByteOrder ) // Start installs ebpf kprobes func Start(modPath string) *Error { modulesPath = modPath setRunning(false) if err := mountDebugFS(); err != nil { log.Error("ebpf.Start -> mount debugfs error. Report on github please: %s", err) return &Error{ fmt.Errorf("ebpf.Start: mount debugfs error. Report on github please: %s", err), NotAvailable, } } var err error m, err = core.LoadEbpfModule("opensnitch.o", modulesPath) if err != nil { log.Error("%s", err) dispatchErrorEvent(fmt.Sprint("[eBPF]: ", err.Error())) return &Error{ fmt.Errorf("[eBPF] Error loading opensnitch.o: %s", err.Error()), NotAvailable, } } m.EnableOptionCompatProbe() // if previous shutdown was unclean, then we must remove the dangling kprobe // and install it again (close the module and load it again) if err := m.EnableKprobes(0); err != nil { m.Close() if err := m.Load(nil); err != nil { return &Error{ fmt.Errorf("eBPF failed to load /etc/opensnitchd/opensnitch.o (2): %v", err), NotAvailable, } } if err := m.EnableKprobes(0); err != nil { return &Error{ fmt.Errorf("eBPF error when enabling kprobes: %v", err), NotAvailable, } } } determineHostByteOrder() ebpfMaps = map[string]*ebpfMapsForProto{ "tcp": { bpfmap: m.Map("tcpMap")}, "tcp6": { bpfmap: m.Map("tcpv6Map")}, "udp": { bpfmap: m.Map("udpMap")}, "udp6": { bpfmap: m.Map("udpv6Map")}, } for prot, mfp := range ebpfMaps { if mfp.bpfmap == nil { return &Error{ fmt.Errorf("eBPF module opensnitch.o malformed, bpfmap[%s] nil", prot), NotAvailable, } } } ctxTasks, cancelTasks = context.WithCancel(context.Background()) ebpfCache = NewEbpfCache() initEventsStreamer() saveEstablishedConnections(uint8(syscall.AF_INET)) if core.IPv6Enabled { saveEstablishedConnections(uint8(syscall.AF_INET6)) } go monitorCache() go monitorMaps() go monitorLocalAddresses() go monitorAlreadyEstablished() setRunning(true) return nil } func saveEstablishedConnections(commDomain uint8) error { // save already established connections socketListTCP, err := daemonNetlink.SocketsDump(commDomain, uint8(syscall.IPPROTO_TCP)) if err != nil { log.Debug("eBPF could not dump TCP (%d) sockets via netlink: %v", commDomain, err) return err } for _, sock := range socketListTCP { inode := int((*sock).INode) pid := procmon.GetPIDFromINode(inode, fmt.Sprint(inode, (*sock).ID.Source, (*sock).ID.SourcePort, (*sock).ID.Destination, (*sock).ID.DestinationPort)) alreadyEstablished.Lock() alreadyEstablished.TCP[sock] = pid alreadyEstablished.Unlock() } return nil } func setRunning(status bool) { lock.Lock() defer lock.Unlock() running = status } // Stop stops monitoring connections using kprobes func Stop() { lock.RLock() defer lock.RUnlock() if running == false { return } cancelTasks() ebpfCache.clear() if m != nil { m.Close() } for pm := range perfMapList { if pm != nil { pm.PollStop() } } for k, mod := range perfMapList { if mod != nil { mod.Close() delete(perfMapList, k) } } if perfMod != nil { perfMod.Close() } } // make bpf() syscall with bpf_lookup prepared by the caller func makeBpfSyscall(bpf_lookup *bpf_lookup_elem_t) uintptr { BPF_MAP_LOOKUP_ELEM := 1 //cmd number syscall_BPF := 321 //syscall number sizeOfStruct := 40 //sizeof bpf_lookup_elem_t struct r1, _, _ := syscall.Syscall(uintptr(syscall_BPF), uintptr(BPF_MAP_LOOKUP_ELEM), uintptr(unsafe.Pointer(bpf_lookup)), uintptr(sizeOfStruct)) return r1 } func dispatchErrorEvent(what string) { log.Error(what) dispatchEvent(what) } func dispatchEvent(data interface{}) { if len(kernelEvents) > maxKernelEvents-1 { fmt.Printf("kernelEvents queue full (%d)", len(kernelEvents)) <-kernelEvents } select { case kernelEvents <- data: default: } } func Events() <-chan interface{} { return kernelEvents } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/events.go������������������������������������������������������0000664�0000000�0000000�00000014362�15003540030�0021364�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "bytes" "encoding/binary" "fmt" "os" "os/signal" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/procmon" elf "github.com/iovisor/gobpf/elf" ) // MaxPathLen defines the maximum length of a path, as defined by the kernel: // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/limits.h#L13 const MaxPathLen = 4096 // MaxArgs defines the maximum number of arguments allowed const MaxArgs = 20 // MaxArgLen defines the maximum length of each argument. // NOTE: this value is 131072 (PAGE_SIZE * 32) // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/binfmts.h#L16 const MaxArgLen = 256 // TaskCommLen is the maximum num of characters of the comm field const TaskCommLen = 16 type execEvent struct { Type uint64 PID uint32 UID uint32 PPID uint32 RetCode uint32 ArgsCount uint8 ArgsPartial uint8 Filename [MaxPathLen]byte Args [MaxArgs][MaxArgLen]byte Comm [TaskCommLen]byte Pad1 uint16 Pad2 uint32 } // Struct that holds the metadata of a connection. // When we receive a new connection, we look for it on the eBPF maps, // and if it's found, this information is returned. type networkEventT struct { Pid uint64 UID uint64 Comm [TaskCommLen]byte } // List of supported events const ( EV_TYPE_NONE = iota EV_TYPE_EXEC EV_TYPE_EXECVEAT EV_TYPE_FORK EV_TYPE_SCHED_EXIT ) var ( execEvents = NewEventsStore() perfMapList = make(map[*elf.PerfMap]*elf.Module) // total workers spawned by the different events PerfMaps eventWorkers = 0 perfMapName = "proc-events" // default value is 8. // Not enough to handle high loads such http downloads, torent traffic, etc. // (regular desktop usage) ringBuffSize = 64 // * PAGE_SIZE (4k usually) ) func initEventsStreamer() { elfOpts := make(map[string]elf.SectionParams) elfOpts["maps/"+perfMapName] = elf.SectionParams{PerfRingBufferPageCount: ringBuffSize} var err error perfMod, err = core.LoadEbpfModule("opensnitch-procs.o", modulesPath) if err != nil { dispatchErrorEvent(fmt.Sprint("[eBPF events]: ", err)) return } perfMod.EnableOptionCompatProbe() if err = perfMod.Load(elfOpts); err != nil { dispatchErrorEvent(fmt.Sprint("[eBPF events]: ", err)) return } tracepoints := []string{ "tracepoint/sched/sched_process_exit", "tracepoint/syscalls/sys_enter_execve", "tracepoint/syscalls/sys_enter_execveat", "tracepoint/syscalls/sys_exit_execve", "tracepoint/syscalls/sys_exit_execveat", //"tracepoint/sched/sched_process_exec", //"tracepoint/sched/sched_process_fork", } // Enable tracepoints first, that way if kprobes fail loading we'll still have some for _, tp := range tracepoints { err = perfMod.EnableTracepoint(tp) if err != nil { dispatchErrorEvent(fmt.Sprintf("[eBPF events] error enabling tracepoint %s: %s", tp, err)) } } if err = perfMod.EnableKprobes(0); err != nil { // if previous shutdown was unclean, then we must remove the dangling kprobe // and install it again (close the module and load it again) perfMod.Close() if err = perfMod.Load(elfOpts); err != nil { dispatchErrorEvent(fmt.Sprintf("[eBPF events] failed to load /etc/opensnitchd/opensnitch-procs.o (2): %v", err)) return } if err = perfMod.EnableKprobes(0); err != nil { dispatchErrorEvent(fmt.Sprintf("[eBPF events] error enabling kprobes: %v", err)) } } sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, os.Kill) go func(sig chan os.Signal) { <-sig }(sig) eventWorkers = 0 initPerfMap(perfMod) } func initPerfMap(mod *elf.Module) { perfChan := make(chan []byte) lostEvents := make(chan uint64, 1) var err error perfMap, err := elf.InitPerfMap(mod, perfMapName, perfChan, lostEvents) if err != nil { dispatchErrorEvent(fmt.Sprintf("[eBPF events] Error initializing eBPF events perfMap: %s", err)) return } perfMapList[perfMap] = mod eventWorkers += 4 for i := 0; i < eventWorkers; i++ { go streamEventsWorker(i, perfChan, lostEvents, kernelEvents, execEvents) } perfMap.PollStart() } func streamEventsWorker(id int, chn chan []byte, lost chan uint64, kernelEvents chan interface{}, execEvents *eventsStore) { var event execEvent errors := 0 maxErrors := 20 // we should have no errors. tooManyErrors := func() bool { errors++ if errors > maxErrors { log.Error("[eBPF events] too many errors parsing events from kernel") log.Error("verify that you're using the correct eBPF modules for this version (%s)", core.Version) return true } return false } for { select { case <-ctxTasks.Done(): goto Exit case l := <-lost: log.Debug("Lost ebpf events: %d", l) case d := <-chn: if err := binary.Read(bytes.NewBuffer(d), hostByteOrder, &event); err != nil { log.Debug("[eBPF events #%d] error: %s", id, err) if tooManyErrors() { goto Exit } } else { switch event.Type { case EV_TYPE_EXEC, EV_TYPE_EXECVEAT: if _, found := execEvents.isInStore(event.PID); found { log.Debug("[eBPF event inCache] -> %d", event.PID) continue } proc := event2process(&event) if proc == nil { continue } execEvents.add(event.PID, event, *proc) case EV_TYPE_SCHED_EXIT: log.Debug("[eBPF exit event] -> %d", event.PID) if _, found := execEvents.isInStore(event.PID); found { log.Debug("[eBPF exit event inCache] -> %d", event.PID) execEvents.delete(event.PID) } } } } } Exit: log.Debug("perfMap goroutine exited #%d", id) } func event2process(event *execEvent) (proc *procmon.Process) { proc = procmon.NewProcess(int(event.PID), byteArrayToString(event.Comm[:])) // trust process path received from kernel path := byteArrayToString(event.Filename[:]) if path != "" { proc.SetPath(path) } else { if proc.ReadPath() != nil { return nil } } proc.ReadCwd() proc.ReadEnv() proc.UID = int(event.UID) proc.PPID = int(event.PPID) if event.ArgsPartial == 0 { for i := 0; i < int(event.ArgsCount); i++ { proc.Args = append(proc.Args, byteArrayToString(event.Args[i][:])) } proc.CleanArgs() } else { proc.ReadCmdline() } log.Debug("[eBPF exec event] ppid: %d, pid: %d, %s -> %s", event.PPID, event.PID, proc.Path, proc.Args) return } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/find.go��������������������������������������������������������0000664�0000000�0000000�00000020306�15003540030�0020773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "encoding/binary" "fmt" "net" "strconv" "unsafe" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" "github.com/evilsocket/opensnitch/daemon/procmon" ) // we need to manually remove old connections from a bpf map // GetPid looks up process pid in a bpf map. // If it's not found, it searches already-established TCP connections. // Returns the process if found. // Additionally, if the process has been found by swapping fields, it'll return // a flag indicating it. func GetPid(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (*procmon.Process, bool, error) { if proc := getPidFromEbpf(proto, srcPort, srcIP, dstIP, dstPort); proc != nil { return proc, false, nil } if findAddressInLocalAddresses(dstIP) { // FIXME: systemd-resolved sometimes makes a TCP Fast Open connection to a DNS server (8.8.8.8 on my machine) // and we get a packet here with **source** (not detination!!!) IP 8.8.8.8 // Maybe it's an in-kernel response with spoofed IP because resolved's TCP Fast Open packet, nor the response. // Another scenario when systemd-resolved or dnscrypt-proxy is used, is that every outbound connection has // the fields swapped: // 443:public-ip -> local-ip:local-port , like if it was a response (but it's not). // Swapping connection fields helps to identify the connection + pid + process, and continue working as usual // when systemd-resolved is being used. But we should understand why is this happenning. if proc := getPidFromEbpf(proto, dstPort, dstIP, srcIP, srcPort); proc != nil { return proc, true, fmt.Errorf("[ebpf conn] FIXME: found swapping fields, systemd-resolved is that you? set DNS=x.x.x.x to your DNS server in /etc/systemd/resolved.conf to workaround this problem") } return nil, false, fmt.Errorf("[ebpf conn] unknown source IP: %s", srcIP) } //check if it comes from already established TCP if proto == "tcp" || proto == "tcp6" { if pid, uid, err := findInAlreadyEstablishedTCP(proto, srcPort, srcIP, dstIP, dstPort); err == nil { proc := procmon.NewProcess(pid, "") proc.GetInfo() proc.UID = uid return proc, false, nil } } //using netlink.GetSocketInfo to check if UID is 0 (in-kernel connection) if uid, _ := daemonNetlink.GetSocketInfo(proto, srcIP, srcPort, dstIP, dstPort); uid == 0 { return nil, false, nil } return nil, false, nil } // getPidFromEbpf looks up a connection in bpf map and returns PID if found // the lookup keys and values are defined in opensnitch.c , e.g. // // struct tcp_key_t { // u16 sport; // u32 daddr; // u16 dport; // u32 saddr; // }__attribute__((packed)); // struct tcp_value_t{ // u64 pid; // u64 uid; // u64 counter; // char[TASK_COMM_LEN] comm; // 16 bytes // }__attribute__((packed)); func getPidFromEbpf(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (proc *procmon.Process) { // Some connections, like broadcasts, are only seen in eBPF once, // but some applications send 1 connection per network interface. // If we delete the eBPF entry the first time we see it, we won't find // the connection the next times. delItemIfFound := true _, ok := ebpfMaps[proto] if !ok { return } var value networkEventT var key []byte var isIP4 bool = (proto == "tcp") || (proto == "udp") || (proto == "udplite") if isIP4 { key = make([]byte, 12) copy(key[2:6], dstIP) binary.BigEndian.PutUint16(key[6:8], uint16(dstPort)) copy(key[8:12], srcIP) } else { // IPv6 key = make([]byte, 36) copy(key[2:18], dstIP) binary.BigEndian.PutUint16(key[18:20], uint16(dstPort)) copy(key[20:36], srcIP) } hostByteOrder.PutUint16(key[0:2], uint16(srcPort)) k := core.ConcatStrings( proto, strconv.FormatUint(uint64(srcPort), 10), srcIP.String(), dstIP.String(), strconv.FormatUint(uint64(dstPort), 10)) if cacheItem, isInCache := ebpfCache.isInCache(k); isInCache { // should we re-read the info? // environ vars might have changed //proc.GetInfo() deleteEbpfEntry(proto, unsafe.Pointer(&key[0])) proc = &cacheItem.Proc log.Debug("[ebpf conn] in cache: %s, %d -> %s", k, proc.ID, proc.Path) return } err := m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value)) if err != nil { // key not found // sometimes srcIP is 0.0.0.0. Happens especially with UDP sendto() // for example: 57621:10.0.3.1 -> 10.0.3.255:57621 , reported as: 0.0.0.0 -> 10.0.3.255 if isIP4 { zeroes := make([]byte, 4) copy(key[8:12], zeroes) } else { zeroes := make([]byte, 16) copy(key[20:36], zeroes) } err = m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value)) if err == nil { delItemIfFound = false } } if err != nil && proto == "udp" && srcIP.String() == dstIP.String() { // very rarely I see this connection. It has srcIP and dstIP == 0.0.0.0 in ebpf map // it is a localhost to localhost connection // srcIP was already set to 0, set dstIP to zero also // TODO try to reproduce it and look for srcIP/dstIP in other kernel structures zeroes := make([]byte, 4) copy(key[2:6], zeroes) err = m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value)) } if err != nil { // key not found in bpf maps return nil } proc = findConnProcess(&value, k) log.Debug("[ebpf conn] adding item to cache: %s", k) ebpfCache.addNewItem(k, key, *proc) if delItemIfFound { deleteEbpfEntry(proto, unsafe.Pointer(&key[0])) } return } // findConnProcess finds the process' details of a connection. // By default we only receive the PID of the process, so we need to get // the rest of the details. // TODO: get the details from kernel, with mm_struct (exe_file, fd_path, etc). func findConnProcess(value *networkEventT, connKey string) (proc *procmon.Process) { comm := byteArrayToString(value.Comm[:]) proc = procmon.NewProcess(int(value.Pid), comm) // Use socket's UID. A process may have dropped privileges. // This is the UID that we've always used. proc.UID = int(value.UID) err := proc.ReadPath() if ev, found := execEvents.isInStore(uint32(value.Pid)); found { // use socket's UID. See above why ^ ev.Proc.UID = proc.UID ev.Proc.ReadCmdline() // if proc's ReadPath() has been successfull, and the path received via the execve tracepoint differs, // use proc's path. // Sometimes we received from the tracepoint a wrong/non-existent path. // Othertimes we receive a "helper" that executes the real binary which opens the connection. // Downsides: for execveat() executions we won't display the original binary. if err == nil && ev.Proc.Path != proc.Path { proc.ReadCmdline() ev.Proc.Path = proc.Path ev.Proc.Args = proc.Args } proc = &ev.Proc log.Debug("[ebpf conn] not in cache, but in execEvents: %s, %d -> %s", connKey, proc.ID, proc.Path) } else { log.Debug("[ebpf conn] not in cache, NOR in execEvents: %s, %d -> %s", connKey, proc.ID, proc.Path) // We'll end here if the events module has not been loaded, or if the process is not in cache. proc.GetInfo() execEvents.add(uint32(value.Pid), *NewExecEvent(uint32(value.Pid), 0, uint32(value.UID), proc.Path, value.Comm), *proc) } return } // FindInAlreadyEstablishedTCP searches those TCP connections which were already established at the time // when opensnitch started func findInAlreadyEstablishedTCP(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (int, int, error) { alreadyEstablished.RLock() defer alreadyEstablished.RUnlock() var _alreadyEstablished map[*daemonNetlink.Socket]int if proto == "tcp" { _alreadyEstablished = alreadyEstablished.TCP } else if proto == "tcp6" { _alreadyEstablished = alreadyEstablished.TCPv6 } for sock, v := range _alreadyEstablished { if (*sock).ID.SourcePort == uint16(srcPort) && (*sock).ID.Source.Equal(srcIP) && (*sock).ID.Destination.Equal(dstIP) && (*sock).ID.DestinationPort == uint16(dstPort) { return v, int((*sock).UID), nil } } return -1, -1, fmt.Errorf("eBPF inode not found") } //returns true if addr is in the list of this machine's addresses func findAddressInLocalAddresses(addr net.IP) bool { lock.Lock() defer lock.Unlock() _, found := localAddresses[addr.String()] return found } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/monitor.go�����������������������������������������������������0000664�0000000�0000000�00000010032�15003540030�0021535�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "syscall" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" "github.com/vishvananda/netlink" ) // we need to manually remove old connections from a bpf map // since when a bpf map is full it doesn't allow any more insertions func monitorMaps() { for { select { case <-ctxTasks.Done(): goto Exit default: time.Sleep(time.Second * 5) for name := range ebpfMaps { // using a pointer to the map doesn't delete the items. // bpftool still counts them. if items := getItems(name, name == "tcp6" || name == "udp6"); items > 500 { deleted := deleteOldItems(name, name == "tcp6" || name == "udp6", items/2) log.Debug("[ebpf] old items deleted: %d", deleted) } } } } Exit: } func monitorCache() { for { select { case <-ctxTasks.Done(): goto Exit case <-ebpfCacheTicker.C: ebpfCache.DeleteOldItems() execEvents.DeleteOldItems() } } Exit: } // maintain a list of this machine's local addresses func monitorLocalAddresses() { newAddrChan := make(chan netlink.AddrUpdate) done := make(chan struct{}) defer close(done) lock.Lock() localAddresses = daemonNetlink.GetLocalAddrs() lock.Unlock() netlink.AddrSubscribeWithOptions(newAddrChan, done, netlink.AddrSubscribeOptions{ ErrorCallback: func(err error) { log.Error("AddrSubscribeWithOptions error: %s", err) }, ListExisting: true, }) for { select { case <-ctxTasks.Done(): done <- struct{}{} goto Exit case addr := <-newAddrChan: if addr.NewAddr && !findAddressInLocalAddresses(addr.LinkAddress.IP) { log.Debug("local addr added: %+v\n", addr) lock.Lock() localAddresses[addr.LinkAddress.IP.String()] = daemonNetlink.AddrUpdateToAddr(&addr) lock.Unlock() } else if !addr.NewAddr { log.Debug("local addr removed: %+v\n", addr) lock.Lock() delete(localAddresses, addr.LinkAddress.IP.String()) lock.Unlock() } } } Exit: log.Debug("monitorLocalAddresses exited") } // monitorAlreadyEstablished makes sure that when an already-established connection is closed // it will be removed from alreadyEstablished. If we don't do this and keep the alreadyEstablished entry forever, // then after the genuine process quits,a malicious process may reuse PID-srcPort-srcIP-dstPort-dstIP func monitorAlreadyEstablished() { tcperr := 0 errLimitExceeded := func() bool { if tcperr > 100 { log.Debug("monitorAlreadyEstablished() generated too much errors") return true } tcperr++ return false } for { select { case <-ctxTasks.Done(): goto Exit default: time.Sleep(time.Second * 2) socketListTCP, err := daemonNetlink.SocketsDump(uint8(syscall.AF_INET), uint8(syscall.IPPROTO_TCP)) if err != nil { log.Debug("monitorAlreadyEstablished(), error dumping TCP sockets via netlink (%d): %s", tcperr, err) if errLimitExceeded() { goto Exit } continue } alreadyEstablished.Lock() for aesock := range alreadyEstablished.TCP { found := false for _, sock := range socketListTCP { if daemonNetlink.SocketsAreEqual(aesock, sock) { found = true break } } if !found { delete(alreadyEstablished.TCP, aesock) } } alreadyEstablished.Unlock() if core.IPv6Enabled { socketListTCPv6, err := daemonNetlink.SocketsDump(uint8(syscall.AF_INET6), uint8(syscall.IPPROTO_TCP)) if err != nil { if errLimitExceeded() { goto Exit } log.Debug("monitorAlreadyEstablished(), error dumping TCPv6 sockets via netlink (%d): %s", tcperr, err) continue } alreadyEstablished.Lock() for aesock := range alreadyEstablished.TCPv6 { found := false for _, sock := range socketListTCPv6 { if daemonNetlink.SocketsAreEqual(aesock, sock) { found = true break } } if !found { delete(alreadyEstablished.TCPv6, aesock) } } alreadyEstablished.Unlock() } } } Exit: log.Debug("monitorAlreadyEstablished exited") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/ebpf/utils.go�������������������������������������������������������0000664�0000000�0000000�00000010424�15003540030�0021213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "bytes" "encoding/binary" "fmt" "unsafe" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) func determineHostByteOrder() { lock.Lock() //determine host byte order buf := [2]byte{} *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) switch buf { case [2]byte{0xCD, 0xAB}: hostByteOrder = binary.LittleEndian case [2]byte{0xAB, 0xCD}: hostByteOrder = binary.BigEndian default: log.Error("Could not determine host byte order.") } lock.Unlock() } func mountDebugFS() error { debugfsPath := "/sys/kernel/debug/" kprobesPath := fmt.Sprint(debugfsPath, "tracing/kprobe_events") if core.Exists(kprobesPath) == false { if _, err := core.Exec("mount", []string{"-t", "debugfs", "none", debugfsPath}); err != nil { log.Warning("eBPF debugfs error: %s", err) return fmt.Errorf(`%s Unable to access debugfs filesystem, needed for eBPF to work, likely caused by a hardened or customized kernel. Change process monitor method to 'proc' to stop receiving this alert `, err) } } return nil } // Trim null characters, and return the left part of the byte array. // NOTE: using BPF_MAP_TYPE_PERCPU_ARRAY does not initialize strings to 0, // so we end up receiving events as follow: // event.filename -> /usr/bin/iptables // event.filename -> /bin/lsn/iptables (should be /bin/ls) // It turns out, that there's a 0x00 character between "/bin/ls" and "n/iptables": // [47 115 98 105 110 47 100 117 109 112 101 50 102 115 0 0 101 115 // ^^^ // TODO: investigate if there's any way of initializing the struct to 0 // like using __builtin_memset() (can't be used with PERCPU apparently) func byteArrayToString(arr []byte) string { temp := bytes.SplitAfter(arr, []byte("\x00"))[0] return string(bytes.Trim(temp[:], "\x00")) } func deleteEbpfEntry(proto string, key unsafe.Pointer) bool { if err := m.DeleteElement(ebpfMaps[proto].bpfmap, key); err != nil { log.Debug("error deleting ebpf entry: %s", err) return false } return true } func getItems(proto string, isIPv6 bool) (items uint) { isDup := make(map[string]uint8) var lookupKey []byte var nextKey []byte if !isIPv6 { lookupKey = make([]byte, 12) nextKey = make([]byte, 12) } else { lookupKey = make([]byte, 36) nextKey = make([]byte, 36) } var value networkEventT firstrun := true for { mp, ok := ebpfMaps[proto] if !ok { return } ok, err := m.LookupNextElement(mp.bpfmap, unsafe.Pointer(&lookupKey[0]), unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value)) if !ok || err != nil { //reached end of map log.Debug("[ebpf] %s map: %d active items", proto, items) return } if firstrun { // on first run lookupKey is a dummy, nothing to delete firstrun = false copy(lookupKey, nextKey) continue } if counter, duped := isDup[string(lookupKey)]; duped && counter > 1 { deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) continue } isDup[string(lookupKey)]++ copy(lookupKey, nextKey) items++ } return items } // deleteOldItems deletes maps' elements in order to keep them below maximum capacity. // If ebpf maps are full they don't allow any more insertions, ending up lossing events. func deleteOldItems(proto string, isIPv6 bool, maxToDelete uint) (deleted uint) { isDup := make(map[string]uint8) var lookupKey []byte var nextKey []byte if !isIPv6 { lookupKey = make([]byte, 12) nextKey = make([]byte, 12) } else { lookupKey = make([]byte, 36) nextKey = make([]byte, 36) } var value networkEventT firstrun := true i := uint(0) for { i++ if i > maxToDelete { return } ok, err := m.LookupNextElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&lookupKey[0]), unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value)) if !ok || err != nil { //reached end of map return } if _, duped := isDup[string(lookupKey)]; duped { if deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) { deleted++ copy(lookupKey, nextKey) continue } return } if firstrun { // on first run lookupKey is a dummy, nothing to delete firstrun = false copy(lookupKey, nextKey) continue } if !deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) { return } deleted++ isDup[string(lookupKey)]++ copy(lookupKey, nextKey) } return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/find.go�������������������������������������������������������������0000664�0000000�0000000�00000005367�15003540030�0020071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "os" "sort" "strconv" "github.com/evilsocket/opensnitch/daemon/core" ) func sortPidsByTime(fdList []os.FileInfo) []os.FileInfo { sort.Slice(fdList, func(i, j int) bool { t := fdList[i].ModTime().UnixNano() u := fdList[j].ModTime().UnixNano() return t > u }) return fdList } // inodeFound searches for the given inode in /proc/<pid>/fd/ or // /proc/<pid>/task/<tid>/fd/ and gets the symbolink link it points to, // in order to compare it against the given inode. // If the inode is found, the cache is updated ans sorted. func inodeFound(pidsPath, expect, inodeKey string, inode, pid int) bool { fdPath := core.ConcatStrings(pidsPath, strconv.Itoa(pid), "/fd/") fdList := lookupPidDescriptors(fdPath, pid) if fdList == nil { return false } for idx := 0; idx < len(fdList); idx++ { descLink := core.ConcatStrings(fdPath, fdList[idx]) if link, err := os.Readlink(descLink); err == nil && link == expect { inodesCache.add(inodeKey, descLink, pid) pidsCache.add(fdPath, fdList, pid) return true } } return false } // lookupPidInProc searches for an inode in /proc. // First it gets the running PIDs and obtains the opened sockets. // TODO: If the inode is not found, search again in the task/threads // of every PID (costly). func lookupPidInProc(pidsPath, expect, inodeKey string, inode int) int { pidList := getProcPids(pidsPath) for _, pid := range pidList { if inodeFound(pidsPath, expect, inodeKey, inode, pid) { return pid } } return -1 } // lookupPidDescriptors returns the list of descriptors inside // /proc/<pid>/fd/ // TODO: search in /proc/<pid>/task/<tid>/fd/ . func lookupPidDescriptors(fdPath string, pid int) []string { f, err := os.Open(fdPath) if err != nil { return nil } // This is where most of the time is wasted when looking for PIDs. // long running processes like firefox/chrome tend to have a lot of descriptor // references that points to non existent files on disk, but that remains in // memory (those with " (deleted)"). // This causes to have to iterate over 300 to 700 items, that are not sockets. fdList, err := f.Readdir(-1) f.Close() if err != nil { return nil } fdList = sortPidsByTime(fdList) s := make([]string, len(fdList)) for n, f := range fdList { s[n] = f.Name() } return s } // getProcPids returns the list of running PIDs, /proc or /proc/<pid>/task/ . func getProcPids(pidsPath string) (pidList []int) { f, err := os.Open(pidsPath) if err != nil { return pidList } ls, err := f.Readdir(-1) f.Close() if err != nil { return pidList } ls = sortPidsByTime(ls) for _, f := range ls { if f.IsDir() == false { continue } if pid, err := strconv.Atoi(f.Name()); err == nil { pidList = append(pidList, []int{pid}...) } } return pidList } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/find_test.go��������������������������������������������������������0000664�0000000�0000000�00000001574�15003540030�0021124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "testing" ) func TestGetProcPids(t *testing.T) { pids := getProcPids("/proc") if len(pids) == 0 { t.Error("getProcPids() should not be 0", pids) } } func TestLookupPidDescriptors(t *testing.T) { pidsFd := lookupPidDescriptors(fmt.Sprint("/proc/", myPid, "/fd/"), myPid) if len(pidsFd) == 0 { t.Error("getProcPids() should not be 0", pidsFd) } } func TestLookupPidInProc(t *testing.T) { // we expect that the inode 1 points to /dev/null expect := "/dev/null" foundPid := lookupPidInProc("/proc/", expect, "", myPid) if foundPid == -1 { t.Error("lookupPidInProc() should not return -1") } } func BenchmarkGetProcs(b *testing.B) { for i := 0; i < b.N; i++ { getProcPids("/proc") } } func BenchmarkLookupPidDescriptors(b *testing.B) { for i := 0; i < b.N; i++ { lookupPidDescriptors(fmt.Sprint("/proc/", myPid, "/fd/"), myPid) } } ������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/monitor/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020276�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/monitor/init.go�����������������������������������������������������0000664�0000000�0000000�00000005565�15003540030�0021603�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package monitor import ( "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/procmon" "github.com/evilsocket/opensnitch/daemon/procmon/audit" "github.com/evilsocket/opensnitch/daemon/procmon/ebpf" ) var ( cacheMonitorsRunning = false ) // List of errors that this package may return. const ( NoError = iota ProcFsErr AuditdErr EbpfErr EbpfEventsErr ) // Error wraps the type of error with its message type Error struct { What int Msg error } // ReconfigureMonitorMethod configures a new method for parsing connections. func ReconfigureMonitorMethod(newMonitorMethod, ebpfModulesPath string) *Error { if procmon.GetMonitorMethod() == newMonitorMethod { return nil } oldMethod := procmon.GetMonitorMethod() if oldMethod == "" { oldMethod = procmon.MethodProc } End() procmon.SetMonitorMethod(newMonitorMethod) // if the new monitor method fails to start, rollback the change and exit // without saving the configuration. Otherwise we can end up with the wrong // monitor method configured and saved to file. err := Init(ebpfModulesPath) if err.What > NoError { log.Error("Reconf() -> Init() error: %v", err) procmon.SetMonitorMethod(oldMethod) return err } return nil } // End stops the way of parsing new connections. func End() { if procmon.MethodIsAudit() { audit.Stop() } else if procmon.MethodIsEbpf() { ebpf.Stop() } } // Init starts parsing connections using the method specified. func Init(ebpfModulesPath string) (errm *Error) { errm = &Error{} if cacheMonitorsRunning == false { go procmon.MonitorActivePids() go procmon.CacheCleanerTask() cacheMonitorsRunning = true } if procmon.MethodIsEbpf() { err := ebpf.Start(ebpfModulesPath) if err == nil { log.Info("Process monitor method ebpf") return errm } // ebpf main module loaded, we can use ebpf // XXX: this will have to be rewritten when we'll have more events (bind, listen, etc) if err.What == ebpf.EventsNotAvailable { log.Info("Process monitor method ebpf") log.Warning("opensnitch-procs.o not available: %s", err.Msg) return errm } // we need to stop this method even if it has failed to start, in order to clean up the kprobes // It helps with the error "cannot write...kprobe_events: file exists". ebpf.Stop() errm.What = err.What errm.Msg = err.Msg log.Warning("error starting ebpf monitor method: %v", err) } else if procmon.MethodIsAudit() { auditConn, err := audit.Start() if err == nil { log.Info("Process monitor method audit") go audit.Reader(auditConn, (chan<- audit.Event)(audit.EventChan)) return &Error{AuditdErr, err} } errm.What = AuditdErr errm.Msg = err log.Warning("error starting audit monitor method: %v", err) } // if any of the above methods have failed, fallback to proc log.Info("Process monitor method /proc") procmon.SetMonitorMethod(procmon.MethodProc) return errm } �������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/parse.go������������������������������������������������������������0000664�0000000�0000000�00000006703�15003540030�0020256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "net" "time" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/netstat" "github.com/evilsocket/opensnitch/daemon/procmon/audit" ) func getPIDFromAuditEvents(inode int, inodeKey string, expect string) (int, int) { audit.Lock.RLock() defer audit.Lock.RUnlock() auditEvents := audit.GetEvents() for n := 0; n < len(auditEvents); n++ { pid := auditEvents[n].Pid if inodeFound("/proc/", expect, inodeKey, inode, pid) { return pid, n } } for n := 0; n < len(auditEvents); n++ { ppid := auditEvents[n].PPid if inodeFound("/proc/", expect, inodeKey, inode, ppid) { return ppid, n } } return -1, -1 } // GetInodeFromNetstat tries to obtain the inode of a connection from /proc/net/* func GetInodeFromNetstat(netEntry *netstat.Entry, inodeList *[]int, protocol string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) bool { if netEntry = netstat.FindEntry(protocol, srcIP, srcPort, dstIP, dstPort); netEntry == nil { log.Debug("Could not find netstat entry for: (%s) %d:%s -> %s:%d", protocol, srcPort, srcIP, dstIP, dstPort) return false } if netEntry.INode > 0 { log.Debug("connection found in netstat: %#v", netEntry) *inodeList = append([]int{netEntry.INode}, *inodeList...) return true } log.Debug("<== no inodes found for this connection: %#v", netEntry) return false } // GetPIDFromINode tries to get the PID from a socket inode following these steps: // 1. Get the PID from the cache of Inodes. // 2. Get the PID from the cache of PIDs. // 3. Look for the PID using one of these methods: // - audit: listening for socket creation from auditd. // - proc: search /proc // // If the PID is not found by one of the 2 first methods, it'll try it using /proc. func GetPIDFromINode(inode int, inodeKey string) int { found := -1 if inode <= 0 { return found } start := time.Now() expect := fmt.Sprintf("socket:[%d]", inode) if cachedPidInode := inodesCache.getPid(inodeKey); cachedPidInode != -1 { log.Debug("Inode found in cache: %v %v %v %v", time.Since(start), inodesCache.getPid(inodeKey), inode, inodeKey) return cachedPidInode } cachedPid, pos := pidsCache.getPid(inode, inodeKey, expect) if cachedPid != -1 { log.Debug("Socket found in known pids %v, pid: %d, inode: %d, pos: %d, pids in cache: %d", time.Since(start), cachedPid, inode, pos, pidsCache.countItems()) pidsCache.sort(cachedPid) inodesCache.add(inodeKey, "", cachedPid) return cachedPid } if MethodIsAudit() { if aPid, pos := getPIDFromAuditEvents(inode, inodeKey, expect); aPid != -1 { log.Debug("PID found via audit events: %v, position: %d", time.Since(start), pos) return aPid } } if found == -1 || methodIsProc() { found = lookupPidInProc("/proc/", expect, inodeKey, inode) } log.Debug("new pid lookup took (%d): %v", found, time.Since(start)) return found } // FindProcess checks if a process exists given a PID. // If it exists in /proc, a new Process{} object is returned with the details // to identify a process (cmdline, name, environment variables, etc). func FindProcess(pid int, interceptUnknown bool) *Process { if interceptUnknown && pid < 0 { return NewProcess(0, "") } if proc := findProcessInActivePidsCache(uint64(pid)); proc != nil { return proc } proc := NewProcess(pid, "") if err := proc.GetInfo(); err != nil { log.Debug("[%d] FindProcess() error: %s", pid, err) return nil } AddToActivePidsCache(uint64(pid), proc) return proc } �������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/process.go����������������������������������������������������������0000664�0000000�0000000�00000006374�15003540030�0020626�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "sync" "time" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) var ( cacheMonitorsRunning = false lock = sync.RWMutex{} monitorMethod = MethodProc ) // monitor method supported types const ( MethodProc = "proc" MethodAudit = "audit" MethodEbpf = "ebpf" KernelConnection = "Kernel connection" ProcSelf = "/proc/self/" ) // man 5 proc; man procfs type procIOstats struct { RChar int64 WChar int64 SyscallRead int64 SyscallWrite int64 ReadBytes int64 WriteBytes int64 } type procNetStats struct { ReadBytes uint64 WriteBytes uint64 } type procDescriptors struct { ModTime time.Time Name string SymLink string Size int64 } type procStatm struct { Size int64 Resident int64 Shared int64 Text int64 Lib int64 Data int64 // data + stack Dt int } // Process holds the details of a process. type Process struct { Env map[string]string IOStats *procIOstats NetStats *procNetStats Statm *procStatm Maps string // Path is the absolute path to the binary Path string Comm string CWD string Status string Stat string Stack string Descriptors []*procDescriptors // Args is the command that the user typed. It MAY contain the absolute path // of the binary: // $ curl https://... // -> Path: /usr/bin/curl // -> Args: curl https://.... // $ /usr/bin/curl https://... // -> Path: /usr/bin/curl // -> Args: /usr/bin/curl https://.... Args []string ID int PPID int UID int } // NewProcess returns a new Process structure. func NewProcess(pid int, comm string) *Process { return &Process{ ID: pid, Comm: comm, Args: make([]string, 0), Env: make(map[string]string), IOStats: &procIOstats{}, NetStats: &procNetStats{}, Statm: &procStatm{}, } } // Serialize transforms a Process object to gRPC protocol object func (p *Process) Serialize() *protocol.Process { ioStats := p.IOStats netStats := p.NetStats if ioStats == nil { ioStats = &procIOstats{} } if netStats == nil { netStats = &procNetStats{} } return &protocol.Process{ Pid: uint64(p.ID), Ppid: uint64(p.PPID), Uid: uint64(p.UID), Comm: p.Comm, Path: p.Path, Args: p.Args, Env: p.Env, Cwd: p.CWD, IoReads: uint64(ioStats.RChar), IoWrites: uint64(ioStats.WChar), NetReads: netStats.ReadBytes, NetWrites: netStats.WriteBytes, } } // SetMonitorMethod configures a new method for parsing connections. func SetMonitorMethod(newMonitorMethod string) { lock.Lock() defer lock.Unlock() monitorMethod = newMonitorMethod } // GetMonitorMethod configures a new method for parsing connections. func GetMonitorMethod() string { lock.Lock() defer lock.Unlock() return monitorMethod } // MethodIsEbpf returns if the process monitor method is eBPF. func MethodIsEbpf() bool { lock.RLock() defer lock.RUnlock() return monitorMethod == MethodEbpf } // MethodIsAudit returns if the process monitor method is eBPF. func MethodIsAudit() bool { lock.RLock() defer lock.RUnlock() return monitorMethod == MethodAudit } func methodIsProc() bool { lock.RLock() defer lock.RUnlock() return monitorMethod == MethodProc } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/procmon/process_test.go�����������������������������������������������������0000664�0000000�0000000�00000005662�15003540030�0021664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "os" "testing" ) var ( myPid = os.Getpid() proc = NewProcess(myPid, "fakeComm") ) func TestNewProcess(t *testing.T) { if proc.ID != myPid { t.Error("NewProcess PID not equal to ", myPid) } if proc.Comm != "fakeComm" { t.Error("NewProcess Comm not equal to fakeComm") } } func TestProcPath(t *testing.T) { if err := proc.ReadPath(); err != nil { t.Error("Proc path error:", err) } if proc.Path == "/fake/path" { t.Error("Proc path equal to /fake/path, should be different:", proc.Path) } } func TestProcCwd(t *testing.T) { err := proc.ReadCwd() if proc.CWD == "" { t.Error("Proc readCwd() not read:", err) } } func TestProcCmdline(t *testing.T) { proc.ReadCmdline() if len(proc.Args) == 0 { t.Error("Proc Args should not be empty:", proc.Args) } } func TestProcDescriptors(t *testing.T) { proc.readDescriptors() if len(proc.Descriptors) == 0 { t.Error("Proc Descriptors should not be empty:", proc.Descriptors) } } func TestProcEnv(t *testing.T) { proc.ReadEnv() if len(proc.Env) == 0 { t.Error("Proc Env should not be empty:", proc.Env) } } func TestProcIOStats(t *testing.T) { proc.readIOStats() if proc.IOStats.RChar == 0 { t.Error("Proc.IOStats.RChar should not be 0:", proc.IOStats) } if proc.IOStats.WChar == 0 { t.Error("Proc.IOStats.WChar should not be 0:", proc.IOStats) } if proc.IOStats.SyscallRead == 0 { t.Error("Proc.IOStats.SyscallRead should not be 0:", proc.IOStats) } if proc.IOStats.SyscallWrite == 0 { t.Error("Proc.IOStats.SyscallWrite should not be 0:", proc.IOStats) } /*if proc.IOStats.ReadBytes == 0 { t.Error("Proc.IOStats.ReadBytes should not be 0:", proc.IOStats) } if proc.IOStats.WriteBytes == 0 { t.Error("Proc.IOStats.WriteBytes should not be 0:", proc.IOStats) }*/ } func TestProcStatus(t *testing.T) { proc.readStatus() if proc.Status == "" { t.Error("Proc Status should not be empty:", proc) } if proc.Stat == "" { t.Error("Proc Stat should not be empty:", proc) } /*if proc.Stack == "" { t.Error("Proc Stack should not be empty:", proc) }*/ if proc.Maps == "" { t.Error("Proc Maps should not be empty:", proc) } if proc.Statm.Size == 0 { t.Error("Proc Statm Size should not be 0:", proc.Statm) } if proc.Statm.Resident == 0 { t.Error("Proc Statm Resident should not be 0:", proc.Statm) } if proc.Statm.Shared == 0 { t.Error("Proc Statm Shared should not be 0:", proc.Statm) } if proc.Statm.Text == 0 { t.Error("Proc Statm Text should not be 0:", proc.Statm) } if proc.Statm.Lib != 0 { t.Error("Proc Statm Lib should not be 0:", proc.Statm) } if proc.Statm.Data == 0 { t.Error("Proc Statm Data should not be 0:", proc.Statm) } if proc.Statm.Dt != 0 { t.Error("Proc Statm Dt should not be 0:", proc.Statm) } } func TestProcCleanPath(t *testing.T) { proc.Path = "/fake/path/binary (deleted)" proc.CleanPath() if proc.Path != "/fake/path/binary" { t.Error("Proc cleanPath() not cleaned:", proc.Path) } } ������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016101�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/loader.go��������������������������������������������������������������0000664�0000000�0000000�00000025537�15003540030�0017712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "encoding/json" "fmt" "io/ioutil" "os" "path" "path/filepath" "sort" "strings" "sync" "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" "github.com/fsnotify/fsnotify" ) // Loader is the object that holds the rules loaded from disk, as well as the // rules watcher. type Loader struct { rules map[string]*Rule watcher *fsnotify.Watcher path string rulesKeys []string sync.RWMutex liveReload bool liveReloadRunning bool } // NewLoader loads rules from disk, and watches for changes made to the rules files // on disk. func NewLoader(liveReload bool) (*Loader, error) { watcher, err := fsnotify.NewWatcher() if err != nil { return nil, err } return &Loader{ path: "", rules: make(map[string]*Rule), liveReload: liveReload, watcher: watcher, liveReloadRunning: false, }, nil } // NumRules returns he number of loaded rules. func (l *Loader) NumRules() int { l.RLock() defer l.RUnlock() return len(l.rules) } // GetAll returns the loaded rules. func (l *Loader) GetAll() map[string]*Rule { l.RLock() defer l.RUnlock() return l.rules } // Load loads rules files from disk. func (l *Loader) Load(path string) error { if core.Exists(path) == false { return fmt.Errorf("Path '%s' does not exist\nCreate it if you want to save rules to disk", path) } path, err := core.ExpandPath(path) if err != nil { return fmt.Errorf("Error accessing rules path: %s.\nCreate it if you want to save rules to disk", err) } expr := filepath.Join(path, "*.json") matches, err := filepath.Glob(expr) if err != nil { return fmt.Errorf("Error globbing '%s': %s", expr, err) } l.path = path if len(l.rules) == 0 { l.rules = make(map[string]*Rule) } for _, fileName := range matches { log.Debug("Reading rule from %s", fileName) if err := l.loadRule(fileName); err != nil { log.Warning("%s", err) continue } } if l.liveReload && l.liveReloadRunning == false { go l.liveReloadWorker() } return nil } // Add adds a rule to the list of rules, and optionally saves it to disk. func (l *Loader) Add(rule *Rule, saveToDisk bool) error { l.addUserRule(rule) if saveToDisk { fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name)) return l.Save(rule, fileName) } return nil } // Replace adds a rule to the list of rules, and optionally saves it to disk. func (l *Loader) Replace(rule *Rule, saveToDisk bool) error { if err := l.replaceUserRule(rule); err != nil { return err } if saveToDisk { l.Lock() defer l.Unlock() fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name)) return l.Save(rule, fileName) } return nil } // Save a rule to disk. func (l *Loader) Save(rule *Rule, path string) error { rule.Updated = time.Now().Format(time.RFC3339) raw, err := json.MarshalIndent(rule, "", " ") if err != nil { return fmt.Errorf("Error while saving rule %s to %s: %s", rule, path, err) } if err = ioutil.WriteFile(path, raw, 0600); err != nil { return fmt.Errorf("Error while saving rule %s to %s: %s", rule, path, err) } return nil } // Delete deletes a rule from the list by name. // If the duration is Always (i.e: saved on disk), it'll attempt to delete // it from disk. func (l *Loader) Delete(ruleName string) error { l.Lock() defer l.Unlock() rule := l.rules[ruleName] if rule == nil { return nil } l.cleanListsRule(rule) delete(l.rules, ruleName) l.sortRules() if rule.Duration != Always { return nil } log.Info("Delete() rule: %s", rule) return l.deleteRuleFromDisk(ruleName) } func (l *Loader) loadRule(fileName string) error { raw, err := ioutil.ReadFile(fileName) if err != nil { return fmt.Errorf("Error while reading %s: %s", fileName, err) } l.Lock() defer l.Unlock() var r Rule err = json.Unmarshal(raw, &r) if err != nil { return fmt.Errorf("Error parsing rule from %s: %s", fileName, err) } raw = nil if oldRule, found := l.rules[r.Name]; found { l.cleanListsRule(oldRule) } if !r.Enabled { // XXX: we only parse and load the Data field if the rule is disabled and the Data field is not empty // the rule will remain disabled. if err = l.unmarshalOperatorList(&r.Operator); err != nil { return err } } else { if err := r.Operator.Compile(); err != nil { log.Warning("Operator.Compile() error: %s: %s (%s)", err, r.Operator.Data, r.Name) return fmt.Errorf("(1) Error compiling rule: %s", err) } if r.Operator.Type == List { for i := 0; i < len(r.Operator.List); i++ { if err := r.Operator.List[i].Compile(); err != nil { log.Warning("Operator.Compile() error: %s (%s)", err, r.Name) return fmt.Errorf("(1) Error compiling list rule: %s", err) } } } } if oldRule, found := l.rules[r.Name]; found { l.deleteOldRuleFromDisk(oldRule, &r) } log.Debug("Loaded rule from %s: %s", fileName, r.String()) l.rules[r.Name] = &r l.sortRules() if l.isTemporary(&r) { err = l.scheduleTemporaryRule(r) } return nil } // deleteRule deletes a rule from memory if it has been deleted from disk. // This is only called if fsnotify's Remove event is fired, thus it doesn't // have to delete temporary rules (!Always). func (l *Loader) deleteRule(filePath string) { fileName := filepath.Base(filePath) ruleName := fileName[:len(fileName)-5] l.RLock() rule, found := l.rules[ruleName] delRule := found && rule.Duration == Always l.RUnlock() if delRule { l.Delete(ruleName) } } func (l *Loader) deleteRuleFromDisk(ruleName string) error { path := fmt.Sprint(l.path, "/", ruleName, ".json") return os.Remove(path) } // deleteOldRuleFromDisk deletes a rule from disk if the Duration changes // from Always (saved on disk), to !Always (temporary). func (l *Loader) deleteOldRuleFromDisk(oldRule, newRule *Rule) { if oldRule.Duration == Always && newRule.Duration != Always { if err := l.deleteRuleFromDisk(oldRule.Name); err != nil { log.Error("Error deleting old rule from disk: %s", oldRule.Name) } } } // cleanListsRule erases the list of domains of an Operator of type Lists func (l *Loader) cleanListsRule(oldRule *Rule) { if oldRule.Operator.Type == Lists { oldRule.Operator.StopMonitoringLists() } else if oldRule.Operator.Type == List { for i := 0; i < len(oldRule.Operator.List); i++ { if oldRule.Operator.List[i].Type == Lists { oldRule.Operator.List[i].StopMonitoringLists() break } } } } func (l *Loader) isTemporary(r *Rule) bool { return r.Duration != Restart && r.Duration != Always && r.Duration != Once } func (l *Loader) isUniqueName(name string) bool { _, found := l.rules[name] return !found } func (l *Loader) setUniqueName(rule *Rule) { l.Lock() defer l.Unlock() idx := 1 base := rule.Name for l.isUniqueName(rule.Name) == false { idx++ rule.Name = fmt.Sprintf("%s-%d", base, idx) } } // Deprecated: rule.Operator.Data no longer holds the operator list in json format as string. func (l *Loader) unmarshalOperatorList(op *Operator) error { if op.Type == List && len(op.List) == 0 && op.Data != "" { if err := json.Unmarshal([]byte(op.Data), &op.List); err != nil { return fmt.Errorf("error loading rule of type list: %s", err) } op.Data = "" } return nil } func (l *Loader) sortRules() { l.rulesKeys = make([]string, 0, len(l.rules)) for k := range l.rules { l.rulesKeys = append(l.rulesKeys, k) } sort.Strings(l.rulesKeys) } func (l *Loader) addUserRule(rule *Rule) { if rule.Duration == Once { return } l.setUniqueName(rule) l.replaceUserRule(rule) } func (l *Loader) replaceUserRule(rule *Rule) (err error) { l.Lock() oldRule, found := l.rules[rule.Name] l.Unlock() if found { // If the rule has changed from Always (saved on disk) to !Always (temporary), // we need to delete the rule from disk and keep it in memory. l.deleteOldRuleFromDisk(oldRule, rule) // delete loaded lists, if this is a rule of type Lists l.cleanListsRule(oldRule) } if err := l.unmarshalOperatorList(&rule.Operator); err != nil { log.Error(err.Error()) } if rule.Enabled { if err := rule.Operator.Compile(); err != nil { log.Warning("Operator.Compile() error: %s: %s", err, rule.Operator.Data) return fmt.Errorf("(2) error compiling rule: %s", err) } if rule.Operator.Type == List { for i := 0; i < len(rule.Operator.List); i++ { if err := rule.Operator.List[i].Compile(); err != nil { log.Warning("Operator.Compile() error: %s: ", err) return fmt.Errorf("(2) error compiling list rule: %s", err) } } } } l.Lock() l.rules[rule.Name] = rule l.sortRules() l.Unlock() if l.isTemporary(rule) { err = l.scheduleTemporaryRule(*rule) } return err } func (l *Loader) scheduleTemporaryRule(rule Rule) error { tTime, err := time.ParseDuration(string(rule.Duration)) if err != nil { return err } time.AfterFunc(tTime, func() { l.Lock() defer l.Unlock() log.Info("Temporary rule expired: %s - %s", rule.Name, rule.Duration) if newRule, found := l.rules[rule.Name]; found { if newRule.Duration != rule.Duration { log.Debug("%s temporary rule expired, but has new Duration, old: %s, new: %s", rule.Name, rule.Duration, newRule.Duration) return } delete(l.rules, rule.Name) l.sortRules() } }) return nil } func (l *Loader) liveReloadWorker() { l.liveReloadRunning = true log.Debug("Rules watcher started on path %s ...", l.path) if err := l.watcher.Add(l.path); err != nil { log.Error("Could not watch path: %s", err) l.liveReloadRunning = false return } for { select { case event := <-l.watcher.Events: // a new rule json file has been created or updated if event.Op&fsnotify.Write == fsnotify.Write { if strings.HasSuffix(event.Name, ".json") { log.Important("Ruleset changed due to %s, reloading ...", path.Base(event.Name)) if err := l.loadRule(event.Name); err != nil { log.Warning("%s", err) } } } else if event.Op&fsnotify.Remove == fsnotify.Remove { if strings.HasSuffix(event.Name, ".json") { log.Important("Rule deleted %s", path.Base(event.Name)) // we only need to delete from memory rules of type Always, // because the Remove event is of a file, i.e.: Duration == Always l.deleteRule(event.Name) } } case err := <-l.watcher.Errors: log.Error("File system watcher error: %s", err) } } } // FindFirstMatch will try match the connection against the existing rule set. func (l *Loader) FindFirstMatch(con *conman.Connection) (match *Rule) { l.RLock() defer l.RUnlock() for _, idx := range l.rulesKeys { rule, _ := l.rules[idx] if rule.Enabled == false { continue } if rule.Match(con) { // We have a match. // Save the rule in order to don't ask the user to take action, // and keep iterating until a Deny or a Priority rule appears. match = rule if rule.Action == Reject || rule.Action == Deny || rule.Precedence == true { return rule } } } return match } �����������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/loader_test.go���������������������������������������������������������0000664�0000000�0000000�00000023403�15003540030�0020737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "fmt" "io" "math/rand" "os" "testing" "time" ) var tmpDir string func TestMain(m *testing.M) { tmpDir = "/tmp/ostest_" + randString() os.Mkdir(tmpDir, 0777) defer os.RemoveAll(tmpDir) os.Exit(m.Run()) } func TestRuleLoader(t *testing.T) { t.Parallel() t.Log("Test rules loader") var list []Operator dur1s := Duration("1s") dummyOper, _ := NewOperator(Simple, false, OpTrue, "", list) dummyOper.Compile() inMem1sRule := Create("000-xxx-name", "rule description xxx", true, false, false, Allow, dur1s, dummyOper) inMemUntilRestartRule := Create("000-aaa-name", "rule description aaa", true, false, false, Allow, Restart, dummyOper) l, err := NewLoader(false) if err != nil { t.Fail() } if err = l.Load("/non/existent/path/"); err == nil { t.Error("non existent path test: err should not be nil") } if err = l.Load("testdata/"); err != nil { t.Error("Error loading test rules: ", err) } // we expect 6 valid rules (2 invalid), loaded from testdata/ testNumRules(t, l, 6) if err = l.Add(inMem1sRule, false); err != nil { t.Error("Error adding temporary rule") } testNumRules(t, l, 7) // test auto deletion of temporary rule time.Sleep(time.Second * 2) testNumRules(t, l, 6) if err = l.Add(inMemUntilRestartRule, false); err != nil { t.Error("Error adding temporary rule (2)") } testNumRules(t, l, 7) testRulesOrder(t, l) testSortRules(t, l) testFindMatch(t, l) testFindEnabled(t, l) testDurationChange(t, l) } func TestRuleLoaderInvalidRegexp(t *testing.T) { t.Parallel() t.Log("Test rules loader: invalid regexp") l, err := NewLoader(true) if err != nil { t.Fail() } t.Run("loadRule() from disk test (simple)", func(t *testing.T) { if err := l.loadRule("testdata/invalid-regexp.json"); err == nil { t.Error("invalid regexp rule loaded: loadRule()") } }) t.Run("loadRule() from disk test (list)", func(t *testing.T) { if err := l.loadRule("testdata/invalid-regexp-list.json"); err == nil { t.Error("invalid regexp rule loaded: loadRule()") } }) var list []Operator dur30m := Duration("30m") opListData := `[{"type": "regexp", "operand": "process.path", "sensitive": false, "data": "^(/di(rmngr)$"}, {"type": "simple", "operand": "dest.port", "data": "53", "sensitive": false}]` invalidRegexpOp, _ := NewOperator(List, false, OpList, opListData, list) invalidRegexpRule := Create("invalid-regexp", "invalid rule description", true, false, false, Allow, dur30m, invalidRegexpOp) t.Run("replaceUserRule() test list", func(t *testing.T) { if err := l.replaceUserRule(invalidRegexpRule); err == nil { t.Error("invalid regexp rule loaded: replaceUserRule()") } }) } // Test rules of type operator.list. There're these scenarios: // - Enabled rules: // * operator Data field is ignored if it contains the list of operators as json string. // * the operarots list is expanded as json objecs under "list": [] // For new rules (> v1.6.3), Data field will be empty. // // - Disabled rules // * (old) the Data field contains the list of operators as json string, and the list of operarots is empty. // * Data field empty, and the list of operators expanded. // In all cases the list of operators must be loaded. func TestRuleLoaderList(t *testing.T) { l, err := NewLoader(true) if err != nil { t.Fail() } testRules := map[string]string{ "rule-with-operator-list": "testdata/rule-operator-list.json", "rule-disabled-with-operators-list-as-json-string": "testdata/rule-disabled-operator-list.json", "rule-disabled-with-operators-list-expanded": "testdata/rule-disabled-operator-list-expanded.json", "rule-with-operator-list-data-empty": "testdata/rule-operator-list-data-empty.json", } for name, path := range testRules { t.Run(fmt.Sprint("loadRule() ", path), func(t *testing.T) { if err := l.loadRule(path); err != nil { t.Error(fmt.Sprint("loadRule() ", path, " error:"), err) } t.Log("Test: List rule:", name, path) r, found := l.rules[name] if !found { t.Error(fmt.Sprint("loadRule() ", path, " not in the list:"), l.rules) } // Starting from > v1.6.3, after loading a rule of type List, the field Operator.Data is emptied, if the Data contained the list of operators as json. if len(r.Operator.List) != 2 { t.Error(fmt.Sprint("loadRule() ", path, " operator List not loaded:"), r) } if r.Operator.List[0].Type != Simple || r.Operator.List[0].Operand != OpProcessPath || r.Operator.List[0].Data != "/usr/bin/telnet" { t.Error(fmt.Sprint("loadRule() ", path, " operator List 0 not loaded:"), r) } if r.Operator.List[1].Type != Simple || r.Operator.List[1].Operand != OpDstPort || r.Operator.List[1].Data != "53" { t.Error(fmt.Sprint("loadRule() ", path, " operator List 1 not loaded:"), r) } }) } } func TestLiveReload(t *testing.T) { t.Parallel() t.Log("Test rules loader with live reload") l, err := NewLoader(true) if err != nil { t.Fail() } if err = Copy("testdata/000-allow-chrome.json", tmpDir+"/000-allow-chrome.json"); err != nil { t.Error("Error copying rule into a temp dir") } if err = Copy("testdata/001-deny-chrome.json", tmpDir+"/001-deny-chrome.json"); err != nil { t.Error("Error copying rule into a temp dir") } if err = l.Load(tmpDir); err != nil { t.Error("Error loading test rules: ", err) } //wait for watcher to activate time.Sleep(time.Second) if err = Copy("testdata/live_reload/test-live-reload-remove.json", tmpDir+"/test-live-reload-remove.json"); err != nil { t.Error("Error copying rules into temp dir") } if err = Copy("testdata/live_reload/test-live-reload-delete.json", tmpDir+"/test-live-reload-delete.json"); err != nil { t.Error("Error copying rules into temp dir") } //wait for watcher to pick up the changes time.Sleep(time.Second) testNumRules(t, l, 4) if err = os.Remove(tmpDir + "/test-live-reload-remove.json"); err != nil { t.Error("Error Remove()ing file from temp dir") } if err = l.Delete("test-live-reload-delete"); err != nil { t.Error("Error Delete()ing file from temp dir") } //wait for watcher to pick up the changes time.Sleep(time.Second) testNumRules(t, l, 2) } func randString() string { rand.Seed(time.Now().UnixNano()) var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, 10) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } return string(b) } func Copy(src, dst string) error { in, err := os.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(dst) if err != nil { return err } defer out.Close() _, err = io.Copy(out, in) if err != nil { return err } return out.Close() } func testNumRules(t *testing.T, l *Loader, num int) { if l.NumRules() != num { t.Error("rules number should be (2): ", num) } } func testRulesOrder(t *testing.T, l *Loader) { if l.rulesKeys[0] != "000-aaa-name" { t.Error("Rules not in order (0): ", l.rulesKeys) } if l.rulesKeys[1] != "000-allow-chrome" { t.Error("Rules not in order (1): ", l.rulesKeys) } if l.rulesKeys[2] != "001-deny-chrome" { t.Error("Rules not in order (2): ", l.rulesKeys) } } func testSortRules(t *testing.T, l *Loader) { l.rulesKeys[1] = "001-deny-chrome" l.rulesKeys[2] = "000-allow-chrome" l.sortRules() if l.rulesKeys[1] != "000-allow-chrome" { t.Error("Rules not in order (1): ", l.rulesKeys) } if l.rulesKeys[2] != "001-deny-chrome" { t.Error("Rules not in order (2): ", l.rulesKeys) } } func testFindMatch(t *testing.T, l *Loader) { conn.Process.Path = "/opt/google/chrome/chrome" testFindPriorityMatch(t, l) testFindDenyMatch(t, l) testFindAllowMatch(t, l) restoreConnection() } func testFindPriorityMatch(t *testing.T, l *Loader) { match := l.FindFirstMatch(conn) if match == nil { t.Error("FindPriorityMatch didn't match") } // test 000-allow-chrome, priority == true if match.Name != "000-allow-chrome" { t.Error("findPriorityMatch: priority rule failed: ", match) } } func testFindDenyMatch(t *testing.T, l *Loader) { l.rules["000-allow-chrome"].Precedence = false // test 000-allow-chrome, priority == false // 001-deny-chrome must match match := l.FindFirstMatch(conn) if match == nil { t.Error("FindDenyMatch deny didn't match") } if match.Name != "001-deny-chrome" { t.Error("findDenyMatch: deny rule failed: ", match) } } func testFindAllowMatch(t *testing.T, l *Loader) { l.rules["000-allow-chrome"].Precedence = false l.rules["001-deny-chrome"].Action = Allow // test 000-allow-chrome, priority == false // 001-deny-chrome must match match := l.FindFirstMatch(conn) if match == nil { t.Error("FindAllowMatch allow didn't match") } if match.Name != "001-deny-chrome" { t.Error("findAllowMatch: allow rule failed: ", match) } } func testFindEnabled(t *testing.T, l *Loader) { l.rules["000-allow-chrome"].Precedence = false l.rules["001-deny-chrome"].Action = Allow l.rules["001-deny-chrome"].Enabled = false // test 000-allow-chrome, priority == false // 001-deny-chrome must match match := l.FindFirstMatch(conn) if match == nil { t.Error("FindEnabledMatch, match nil") } if match.Name == "001-deny-chrome" { t.Error("findEnabledMatch: deny rule shouldn't have matched: ", match) } } // test that changing the Duration of a temporary rule doesn't delete // the new one, ignoring the old timer. func testDurationChange(t *testing.T, l *Loader) { l.rules["000-aaa-name"].Duration = "2s" if err := l.replaceUserRule(l.rules["000-aaa-name"]); err != nil { t.Error("testDurationChange, error replacing rule: ", err) } l.rules["000-aaa-name"].Duration = "1h" if err := l.replaceUserRule(l.rules["000-aaa-name"]); err != nil { t.Error("testDurationChange, error replacing rule: ", err) } time.Sleep(time.Second * 4) if _, found := l.rules["000-aaa-name"]; !found { t.Error("testDurationChange, error: rule has been deleted") } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/operator.go������������������������������������������������������������0000664�0000000�0000000�00000020034�15003540030�0020262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "fmt" "net" "reflect" "regexp" "strconv" "strings" "sync" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) // Type is the type of rule. // Every type has its own way of checking the user data against connections. type Type string // Sensitive defines if a rule is case-sensitive or not. By default no. type Sensitive bool // Operand is what we check on a connection. type Operand string // Available types const ( Simple = Type("simple") Regexp = Type("regexp") Complex = Type("complex") // for future use List = Type("list") Network = Type("network") Lists = Type("lists") ) // Available operands const ( OpTrue = Operand("true") OpProcessID = Operand("process.id") OpProcessPath = Operand("process.path") OpProcessCmd = Operand("process.command") OpProcessEnvPrefix = Operand("process.env.") OpProcessEnvPrefixLen = 12 OpUserID = Operand("user.id") OpSrcIP = Operand("source.ip") OpSrcPort = Operand("source.port") OpDstIP = Operand("dest.ip") OpDstHost = Operand("dest.host") OpDstPort = Operand("dest.port") OpDstNetwork = Operand("dest.network") OpSrcNetwork = Operand("source.network") OpProto = Operand("protocol") OpIfaceIn = Operand("iface.in") OpIfaceOut = Operand("iface.out") OpList = Operand("list") OpDomainsLists = Operand("lists.domains") OpDomainsRegexpLists = Operand("lists.domains_regexp") OpIPLists = Operand("lists.ips") OpNetLists = Operand("lists.nets") ) type opCallback func(value interface{}) bool // Operator represents what we want to filter of a connection, and how. type Operator struct { cb opCallback re *regexp.Regexp netMask *net.IPNet lists map[string]interface{} exitMonitorChan chan (bool) Operand Operand `json:"operand"` Data string `json:"data"` Type Type `json:"type"` List []Operator `json:"list"` Sensitive Sensitive `json:"sensitive"` listsMonitorRunning bool isCompiled bool sync.RWMutex } // NewOperator returns a new operator object func NewOperator(t Type, s Sensitive, o Operand, data string, list []Operator) (*Operator, error) { op := Operator{ Type: t, Sensitive: s, Operand: o, Data: data, List: list, } return &op, nil } // Compile translates the operator type field to its callback counterpart func (o *Operator) Compile() error { if o.isCompiled { return nil } if o.Type == Simple { o.cb = o.simpleCmp } else if o.Type == Regexp { o.cb = o.reCmp if o.Sensitive == false { o.Data = strings.ToLower(o.Data) } re, err := regexp.Compile(o.Data) if err != nil { return err } o.re = re } else if o.Type == List { o.Operand = OpList } else if o.Type == Network { var err error _, o.netMask, err = net.ParseCIDR(o.Data) if err != nil { return err } o.cb = o.cmpNetwork } else if o.Type == Lists { if o.Data == "" { return fmt.Errorf("Operand lists is empty, nothing to load: %s", o) } if o.Operand == OpDomainsLists { o.loadLists() o.cb = o.domainsListCmp } else if o.Operand == OpDomainsRegexpLists { o.loadLists() o.cb = o.reListCmp } else if o.Operand == OpIPLists { o.loadLists() o.cb = o.ipListCmp } else if o.Operand == OpNetLists { o.loadLists() o.cb = o.ipNetCmp } else { return fmt.Errorf("Unknown Lists operand: %s", o.Operand) } } else { return fmt.Errorf("Unknown type: %s", o.Type) } log.Debug("Operator compiled: %s", o) o.isCompiled = true return nil } func (o *Operator) String() string { how := "is" if o.Type == Regexp { how = "matches" } return fmt.Sprintf("%s %s '%s'", log.Bold(string(o.Operand)), how, log.Yellow(string(o.Data))) } func (o *Operator) simpleCmp(v interface{}) bool { if o.Sensitive == false { return strings.EqualFold(v.(string), o.Data) } return v == o.Data } func (o *Operator) reCmp(v interface{}) bool { if vt := reflect.ValueOf(v).Kind(); vt != reflect.String { log.Warning("Operator.reCmp() bad interface type: %T", v) return false } if o.Sensitive == false { v = strings.ToLower(v.(string)) } return o.re.MatchString(v.(string)) } func (o *Operator) cmpNetwork(destIP interface{}) bool { // 192.0.2.1/24, 2001:db8:a0b:12f0::1/32 if o.netMask == nil { log.Warning("cmpNetwork() NULL: %s", destIP) return false } return o.netMask.Contains(destIP.(net.IP)) } func (o *Operator) domainsListCmp(v interface{}) bool { dstHost := v.(string) if dstHost == "" { return false } if o.Sensitive == false { dstHost = strings.ToLower(dstHost) } o.RLock() defer o.RUnlock() if _, found := o.lists[dstHost]; found { log.Debug("%s: %s, %s", log.Red("domain list match"), dstHost, o.lists[dstHost]) return true } return false } func (o *Operator) ipListCmp(v interface{}) bool { dstIP := v.(string) if dstIP == "" { return false } o.RLock() defer o.RUnlock() if _, found := o.lists[dstIP]; found { log.Debug("%s: %s, %s", log.Red("IP list match"), dstIP, o.lists[dstIP].(string)) return true } return false } func (o *Operator) ipNetCmp(dstIP interface{}) bool { o.RLock() defer o.RUnlock() for host, netMask := range o.lists { n := netMask.(*net.IPNet) if n.Contains(dstIP.(net.IP)) { log.Debug("%s: %s, %s", log.Red("Net list match"), dstIP, host) return true } } return false } func (o *Operator) reListCmp(v interface{}) bool { dstHost := v.(string) if dstHost == "" { return false } if o.Sensitive == false { dstHost = strings.ToLower(dstHost) } o.RLock() defer o.RUnlock() for file, re := range o.lists { r := re.(*regexp.Regexp) if r.MatchString(dstHost) { log.Debug("%s: %s, %s", log.Red("Regexp list match"), dstHost, file) return true } } return false } func (o *Operator) listMatch(con interface{}) bool { res := true for i := 0; i < len(o.List); i++ { res = res && o.List[i].Match(con.(*conman.Connection)) } return res } // Match tries to match parts of a connection with the given operator. func (o *Operator) Match(con *conman.Connection) bool { if o.Operand == OpTrue { return true } else if o.Operand == OpList { return o.listMatch(con) } else if o.Operand == OpProcessPath { return o.cb(con.Process.Path) } else if o.Operand == OpProcessCmd { return o.cb(strings.Join(con.Process.Args, " ")) } else if o.Operand == OpDstHost && con.DstHost != "" { return o.cb(con.DstHost) } else if o.Operand == OpDstIP { return o.cb(con.DstIP.String()) } else if o.Operand == OpDstPort { return o.cb(strconv.FormatUint(uint64(con.DstPort), 10)) } else if o.Operand == OpDomainsLists { return o.cb(con.DstHost) } else if o.Operand == OpIPLists { return o.cb(con.DstIP.String()) } else if o.Operand == OpUserID { return o.cb(strconv.Itoa(con.Entry.UserId)) } else if o.Operand == OpDstNetwork { return o.cb(con.DstIP) } else if o.Operand == OpSrcNetwork { return o.cb(con.SrcIP) } else if o.Operand == OpNetLists { return o.cb(con.DstIP) } else if o.Operand == OpDomainsRegexpLists { return o.cb(con.DstHost) } else if o.Operand == OpIfaceIn { if ifname, err := net.InterfaceByIndex(con.Pkt.IfaceInIdx); err == nil { return o.cb(ifname.Name) } } else if o.Operand == OpIfaceOut { if ifname, err := net.InterfaceByIndex(con.Pkt.IfaceOutIdx); err == nil { return o.cb(ifname.Name) } } else if o.Operand == OpProto { return o.cb(con.Protocol) } else if o.Operand == OpSrcIP { return o.cb(con.SrcIP.String()) } else if o.Operand == OpSrcPort { return o.cb(strconv.FormatUint(uint64(con.SrcPort), 10)) } else if o.Operand == OpProcessID { return o.cb(strconv.Itoa(con.Process.ID)) } else if strings.HasPrefix(string(o.Operand), string(OpProcessEnvPrefix)) { envVarName := core.Trim(string(o.Operand[OpProcessEnvPrefixLen:])) envVarValue, _ := con.Process.Env[envVarName] return o.cb(envVarValue) } return false } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/operator_lists.go������������������������������������������������������0000664�0000000�0000000�00000014673�15003540030�0021514�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "fmt" "io/ioutil" "net" "path/filepath" "regexp" "runtime/debug" "strings" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) func (o *Operator) monitorLists() { log.Info("monitor lists started: %s", o.Data) modTimes := make(map[string]time.Time) totalFiles := 0 needReload := false numFiles := 0 expr := filepath.Join(o.Data, "/*.*") for { select { case <-o.exitMonitorChan: goto Exit default: fileList, err := filepath.Glob(expr) if err != nil { log.Warning("Error reading directory of domains list: %s, %s", o.Data, err) goto Exit } numFiles = 0 for _, filename := range fileList { // ignore hidden files name := filepath.Base(filename) if name[:1] == "." { delete(modTimes, filename) continue } // an overwrite operation performs two tasks: truncate the file and save the new content, // causing the file time to be modified twice. modTime, err := core.GetFileModTime(filename) if err != nil { log.Debug("deleting saved mod time due to error reading the list, %s", filename) delete(modTimes, filename) } else if lastModTime, found := modTimes[filename]; found { if lastModTime.Equal(modTime) == false { log.Debug("list changed: %s, %s, %s", lastModTime, modTime, filename) needReload = true } } modTimes[filename] = modTime numFiles++ } fileList = nil if numFiles != totalFiles { needReload = true } totalFiles = numFiles if needReload { // we can't reload a single list, because the domains of all lists are added to the same map. // we could have the domains separated by lists/files, but then we'd need to iterate the map in order // to match a domain. Reloading the lists shoud only occur once a day. if err := o.readLists(); err != nil { log.Warning("%s", err) } needReload = false } time.Sleep(4 * time.Second) } } Exit: modTimes = nil o.ClearLists() log.Info("lists monitor stopped") } // ClearLists deletes all the entries of a list func (o *Operator) ClearLists() { o.Lock() defer o.Unlock() log.Info("clearing domains lists: %d - %s", len(o.lists), o.Data) for k := range o.lists { delete(o.lists, k) } debug.FreeOSMemory() } // StopMonitoringLists stops the monitoring lists goroutine. func (o *Operator) StopMonitoringLists() { if o.listsMonitorRunning == true { o.exitMonitorChan <- true o.exitMonitorChan = nil o.listsMonitorRunning = false } } func (o *Operator) readDomainsList(raw, fileName string) (dups uint64) { log.Debug("Loading domains list: %s, size: %d", fileName, len(raw)) lines := strings.Split(string(raw), "\n") for _, domain := range lines { if len(domain) < 9 { continue } // exclude not valid lines if domain[:7] != "0.0.0.0" && domain[:9] != "127.0.0.1" { continue } host := domain[8:] // exclude localhost entries if domain[:9] == "127.0.0.1" { host = domain[10:] } if host == "local" || host == "localhost" || host == "localhost.localdomain" || host == "broadcasthost" { continue } host = core.Trim(host) if _, found := o.lists[host]; found { dups++ continue } o.lists[host] = fileName } lines = nil log.Info("%d domains loaded, %s", len(o.lists), fileName) return dups } func (o *Operator) readNetList(raw, fileName string) (dups uint64) { log.Debug("Loading nets list: %s, size: %d", fileName, len(raw)) lines := strings.Split(string(raw), "\n") for _, line := range lines { if line == "" || line[0] == '#' { continue } host := core.Trim(line) if _, found := o.lists[host]; found { dups++ continue } _, netMask, err := net.ParseCIDR(host) if err != nil { log.Warning("Error parsing net from list: %s, (%s)", err, fileName) continue } o.lists[host] = netMask } lines = nil log.Info("%d nets loaded, %s", len(o.lists), fileName) return dups } func (o *Operator) readRegexpList(raw, fileName string) (dups uint64) { log.Debug("Loading regexp list: %s, size: %d", fileName, len(raw)) lines := strings.Split(string(raw), "\n") for n, line := range lines { if line == "" || line[0] == '#' { continue } host := core.Trim(line) if _, found := o.lists[host]; found { dups++ continue } re, err := regexp.Compile(line) if err != nil { log.Warning("Error compiling regexp from list: %s, (%d:%s)", err, n, fileName) continue } o.lists[line] = re } lines = nil log.Info("%d regexps loaded, %s", len(o.lists), fileName) return dups } func (o *Operator) readIPList(raw, fileName string) (dups uint64) { log.Debug("Loading IPs list: %s, size: %d", fileName, len(raw)) lines := strings.Split(string(raw), "\n") for _, line := range lines { if line == "" || line[0] == '#' { continue } ip := core.Trim(line) if _, found := o.lists[ip]; found { dups++ continue } o.lists[ip] = fileName } lines = nil log.Info("%d IPs loaded, %s", len(o.lists), fileName) return dups } func (o *Operator) readLists() error { o.ClearLists() var dups uint64 // this list is particular to this operator and rule o.Lock() defer o.Unlock() o.lists = make(map[string]interface{}) expr := filepath.Join(o.Data, "*.*") fileList, err := filepath.Glob(expr) if err != nil { return fmt.Errorf("Error loading domains lists '%s': %s", expr, err) } for _, fileName := range fileList { // ignore hidden files name := filepath.Base(fileName) if name[:1] == "." { continue } raw, err := ioutil.ReadFile(fileName) if err != nil { log.Warning("Error reading list of IPs (%s): %s", fileName, err) continue } if o.Operand == OpDomainsLists { dups += o.readDomainsList(string(raw), fileName) } else if o.Operand == OpDomainsRegexpLists { dups += o.readRegexpList(string(raw), fileName) } else if o.Operand == OpNetLists { dups += o.readNetList(string(raw), fileName) } else if o.Operand == OpIPLists { dups += o.readIPList(string(raw), fileName) } else { log.Warning("Unknown lists operand type: %s", o.Operand) } } log.Info("%d lists loaded, %d domains, %d duplicated", len(fileList), len(o.lists), dups) return nil } func (o *Operator) loadLists() { log.Info("loading domains lists: %s, %s, %s", o.Type, o.Operand, o.Data) // when loading from disk, we don't use the Operator's constructor, so we need to create this channel if o.exitMonitorChan == nil { o.exitMonitorChan = make(chan bool) o.listsMonitorRunning = true go o.monitorLists() } } ���������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/operator_test.go�������������������������������������������������������0000664�0000000�0000000�00000053132�15003540030�0021326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "encoding/json" "fmt" "net" "testing" "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/netstat" "github.com/evilsocket/opensnitch/daemon/procmon" ) var ( defaultProcPath = "/usr/bin/opensnitchd" defaultProcArgs = "-rules-path /etc/opensnitchd/rules/" defaultDstHost = "opensnitch.io" defaultDstPort = uint(443) defaultDstIP = "185.53.178.14" defaultUserID = 666 netEntry = &netstat.Entry{ UserId: defaultUserID, } proc = &procmon.Process{ ID: 12345, Path: defaultProcPath, Args: []string{"-rules-path", "/etc/opensnitchd/rules/"}, } conn = &conman.Connection{ Protocol: "TCP", SrcPort: 66666, SrcIP: net.ParseIP("192.168.1.111"), DstIP: net.ParseIP(defaultDstIP), DstPort: defaultDstPort, DstHost: defaultDstHost, Process: proc, Entry: netEntry, } ) func compileListOperators(list *[]Operator, t *testing.T) { op := *list for i := 0; i < len(*list); i++ { if err := op[i].Compile(); err != nil { t.Error("NewOperator List, Compile() subitem error:", err) } } } func unmarshalListData(data string, t *testing.T) (op *[]Operator) { if err := json.Unmarshal([]byte(data), &op); err != nil { t.Error("Error unmarshalling list data:", err, data) return nil } return op } func restoreConnection() { conn.Process.Path = defaultProcPath conn.DstHost = defaultDstHost conn.DstPort = defaultDstPort conn.Entry.UserId = defaultUserID } func TestNewOperatorSimple(t *testing.T) { t.Log("Test NewOperator() simple") var list []Operator opSimple, err := NewOperator(Simple, false, OpTrue, "", list) if err != nil { t.Error("NewOperator simple.err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Fail() } if opSimple.Match(nil) == false { t.Error("Test NewOperator() simple.case-insensitive doesn't match") t.Fail() } t.Run("Operator Simple proc.id", func(t *testing.T) { // proc.id not sensitive opSimple, err = NewOperator(Simple, false, OpProcessID, "12345", list) if err != nil { t.Error("NewOperator simple.case-insensitive.proc.id err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Error("NewOperator simple.case-insensitive.proc.id Compile() err:", err) t.Fail() } if opSimple.Match(conn) == false { t.Error("Test NewOperator() simple proc.id doesn't match") t.Fail() } }) opSimple, err = NewOperator(Simple, false, OpProcessPath, defaultProcPath, list) t.Run("Operator Simple proc.path case-insensitive", func(t *testing.T) { // proc path not sensitive if err != nil { t.Error("NewOperator simple proc.path err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Error("NewOperator simple.case-insensitive.proc.path Compile() err:", err) t.Fail() } if opSimple.Match(conn) == false { t.Error("Test NewOperator() simple proc.path doesn't match") t.Fail() } }) t.Run("Operator Simple proc.path sensitive", func(t *testing.T) { // proc path sensitive opSimple.Sensitive = true conn.Process.Path = "/usr/bin/OpenSnitchd" if opSimple.Match(conn) == true { t.Error("Test NewOperator() simple proc.path sensitive match") t.Fail() } }) opSimple, err = NewOperator(Simple, false, OpDstHost, defaultDstHost, list) t.Run("Operator Simple con.dstHost case-insensitive", func(t *testing.T) { // proc dst host not sensitive if err != nil { t.Error("NewOperator simple proc.path err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Error("NewOperator simple.case-insensitive.dstHost Compile() err:", err) t.Fail() } if opSimple.Match(conn) == false { t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't match") t.Fail() } }) t.Run("Operator Simple con.dstHost case-insensitive different host", func(t *testing.T) { conn.DstHost = "www.opensnitch.io" if opSimple.Match(conn) == true { t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't MATCH") t.Fail() } }) t.Run("Operator Simple con.dstHost sensitive", func(t *testing.T) { // proc dst host sensitive opSimple, err = NewOperator(Simple, true, OpDstHost, "OpEnsNitCh.io", list) if err != nil { t.Error("NewOperator simple.dstHost.sensitive err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Error("NewOperator simple.dstHost.sensitive Compile() err:", err) t.Fail() } conn.DstHost = "OpEnsNitCh.io" if opSimple.Match(conn) == false { t.Error("Test NewOperator() simple.dstHost.sensitive doesn't match") t.Fail() } }) t.Run("Operator Simple proc.args case-insensitive", func(t *testing.T) { // proc args case-insensitive opSimple, err = NewOperator(Simple, false, OpProcessCmd, defaultProcArgs, list) if err != nil { t.Error("NewOperator simple proc.args err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Error("NewOperator simple proc.args Compile() err: ", err) t.Fail() } if opSimple.Match(conn) == false { t.Error("Test NewOperator() simple proc.args doesn't match") t.Fail() } }) t.Run("Operator Simple con.dstIp case-insensitive", func(t *testing.T) { // proc dstIp case-insensitive opSimple, err = NewOperator(Simple, false, OpDstIP, defaultDstIP, list) if err != nil { t.Error("NewOperator simple conn.dstip.err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Error("NewOperator simple con.dstIp Compile() err: ", err) t.Fail() } if opSimple.Match(conn) == false { t.Error("Test NewOperator() simple conn.dstip doesn't match") t.Fail() } }) t.Run("Operator Simple UserId case-insensitive", func(t *testing.T) { // conn.uid case-insensitive opSimple, err = NewOperator(Simple, false, OpUserID, fmt.Sprint(defaultUserID), list) if err != nil { t.Error("NewOperator simple conn.userid.err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Error("NewOperator simple UserId Compile() err: ", err) t.Fail() } if opSimple.Match(conn) == false { t.Error("Test NewOperator() simple conn.userid doesn't match") t.Fail() } }) restoreConnection() } func TestNewOperatorNetwork(t *testing.T) { t.Log("Test NewOperator() network") var dummyList []Operator opSimple, err := NewOperator(Network, false, OpDstNetwork, "185.53.178.14/24", dummyList) if err != nil { t.Error("NewOperator network.err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Fail() } if opSimple.Match(conn) == false { t.Error("Test NewOperator() network doesn't match") t.Fail() } opSimple, err = NewOperator(Network, false, OpDstNetwork, "8.8.8.8/24", dummyList) if err != nil { t.Error("NewOperator network.err should be nil: ", err) t.Fail() } if err = opSimple.Compile(); err != nil { t.Fail() } if opSimple.Match(conn) == true { t.Error("Test NewOperator() network doesn't match:", conn.DstIP) t.Fail() } restoreConnection() } func TestNewOperatorRegexp(t *testing.T) { t.Log("Test NewOperator() regexp") var dummyList []Operator opRE, err := NewOperator(Regexp, false, OpProto, "^TCP$", dummyList) if err != nil { t.Error("NewOperator regexp.err should be nil: ", err) t.Fail() } if err = opRE.Compile(); err != nil { t.Fail() } if opRE.Match(conn) == false { t.Error("Test NewOperator() regexp doesn't match") t.Fail() } restoreConnection() } func TestNewOperatorInvalidRegexp(t *testing.T) { t.Log("Test NewOperator() invalid regexp") var dummyList []Operator opRE, err := NewOperator(Regexp, false, OpProto, "^TC(P$", dummyList) if err != nil { t.Error("NewOperator regexp.err should be nil: ", err) t.Fail() } if err = opRE.Compile(); err == nil { t.Error("NewOperator() invalid regexp. It should fail: ", err) t.Fail() } restoreConnection() } func TestNewOperatorRegexpSensitive(t *testing.T) { t.Log("Test NewOperator() regexp sensitive") var dummyList []Operator var sensitive Sensitive sensitive = true conn.Process.Path = "/tmp/cUrL" opRE, err := NewOperator(Regexp, sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList) if err != nil { t.Error("NewOperator regexp.case-sensitive.err should be nil: ", err) t.Fail() } if err = opRE.Compile(); err != nil { t.Fail() } if opRE.Match(conn) == false { t.Error("Test NewOperator() RE sensitive doesn't match:", conn.Process.Path) t.Fail() } t.Run("Operator regexp proc.path case-sensitive", func(t *testing.T) { conn.Process.Path = "/tmp/curl" if opRE.Match(conn) == true { t.Error("Test NewOperator() RE sensitive match:", conn.Process.Path) t.Fail() } }) opRE, err = NewOperator(Regexp, !sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList) if err != nil { t.Error("NewOperator regexp.case-insensitive.err should be nil: ", err) t.Fail() } if err = opRE.Compile(); err != nil { t.Fail() } if opRE.Match(conn) == false { t.Error("Test NewOperator() RE not sensitive match:", conn.Process.Path) t.Fail() } restoreConnection() } func TestNewOperatorList(t *testing.T) { t.Log("Test NewOperator() List") var list []Operator listData := `[{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]` // simple list opList, err := NewOperator(List, false, OpProto, listData, list) t.Run("Operator List simple case-insensitive", func(t *testing.T) { if err != nil { t.Error("NewOperator list.regexp.err should be nil: ", err) t.Fail() } if err = opList.Compile(); err != nil { t.Fail() } opList.List = *unmarshalListData(opList.Data, t) compileListOperators(&opList.List, t) if opList.Match(conn) == false { t.Error("Test NewOperator() list simple doesn't match") t.Fail() } }) t.Run("Operator List regexp case-insensitive", func(t *testing.T) { // list with regexp, case-insensitive listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/bin/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]` opList.List = *unmarshalListData(listData, t) compileListOperators(&opList.List, t) if err = opList.Compile(); err != nil { t.Fail() } if opList.Match(conn) == false { t.Error("Test NewOperator() list regexp doesn't match") t.Fail() } }) t.Run("Operator List regexp case-sensitive", func(t *testing.T) { // list with regexp, case-sensitive // "data": "^/usr/BiN/.*" must match conn.Process.Path (sensitive) listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/BiN/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]` opList.List = *unmarshalListData(listData, t) compileListOperators(&opList.List, t) conn.Process.Path = "/usr/BiN/opensnitchd" opList.Sensitive = true if err = opList.Compile(); err != nil { t.Fail() } if opList.Match(conn) == false { t.Error("Test NewOperator() list.regexp.sensitive doesn't match:", conn.Process.Path) t.Fail() } }) t.Run("Operator List regexp case-insensitive 2", func(t *testing.T) { // "data": "^/usr/BiN/.*" must not match conn.Process.Path (insensitive) opList.Sensitive = false conn.Process.Path = "/USR/BiN/opensnitchd" if err = opList.Compile(); err != nil { t.Fail() } if opList.Match(conn) == false { t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path) t.Fail() } }) t.Run("Operator List regexp case-insensitive 3", func(t *testing.T) { // "data": "^/usr/BiN/.*" must match conn.Process.Path (insensitive) opList.Sensitive = false conn.Process.Path = "/USR/bin/opensnitchd" if err = opList.Compile(); err != nil { t.Fail() } if opList.Match(conn) == false { t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path) t.Fail() } }) restoreConnection() } func TestNewOperatorListsSimple(t *testing.T) { t.Log("Test NewOperator() Lists simple") var dummyList []Operator opLists, err := NewOperator(Lists, false, OpDomainsLists, "testdata/lists/domains/", dummyList) if err != nil { t.Error("NewOperator Lists, shouldn't be nil: ", err) t.Fail() } if err = opLists.Compile(); err != nil { t.Error("NewOperator Lists, Compile() error:", err) } time.Sleep(time.Second) t.Log("testing Lists, DstHost:", conn.DstHost) // The list contains 4 lines, 1 is a comment and there's a domain duplicated. // We should only load lines that start with 0.0.0.0 or 127.0.0.1 if len(opLists.lists) != 2 { t.Error("NewOperator Lists, number of domains error:", opLists.lists, len(opLists.lists)) } if opLists.Match(conn) == false { t.Error("Test NewOperator() lists doesn't match") } opLists.StopMonitoringLists() time.Sleep(time.Second) opLists.Lock() if len(opLists.lists) != 0 { t.Error("NewOperator Lists, number should be 0 after stop:", opLists.lists, len(opLists.lists)) } opLists.Unlock() restoreConnection() } func TestNewOperatorListsIPs(t *testing.T) { t.Log("Test NewOperator() Lists domains_regexp") var subOp *Operator var list []Operator listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.ips", "data": "testdata/lists/ips/", "sensitive": false}]` opLists, err := NewOperator(List, false, OpList, listData, list) if err != nil { t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) t.Fail() } if err := opLists.Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() error:", err) } opLists.List = *unmarshalListData(opLists.Data, t) for i := 0; i < len(opLists.List); i++ { if err := opLists.List[i].Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) } if opLists.List[i].Type == Lists { subOp = &opLists.List[i] } } time.Sleep(time.Second) if opLists.Match(conn) == false { t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) } subOp.Lock() listslen := len(subOp.lists) subOp.Unlock() if listslen != 2 { t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) } //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) if opLists.Match(conn) == false { // we don't care about if it matches, we're testing race conditions t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) } subOp.StopMonitoringLists() time.Sleep(time.Second) subOp.Lock() if len(subOp.lists) != 0 { t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) } subOp.Unlock() restoreConnection() } func TestNewOperatorListsNETs(t *testing.T) { t.Log("Test NewOperator() Lists domains_regexp") var subOp *Operator var list []Operator listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.nets", "data": "testdata/lists/nets/", "sensitive": false}]` opLists, err := NewOperator(List, false, OpList, listData, list) if err != nil { t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) t.Fail() } if err := opLists.Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() error:", err) } opLists.List = *unmarshalListData(opLists.Data, t) for i := 0; i < len(opLists.List); i++ { if err := opLists.List[i].Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) } if opLists.List[i].Type == Lists { subOp = &opLists.List[i] } } time.Sleep(time.Second) if opLists.Match(conn) == false { t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) } subOp.Lock() listslen := len(subOp.lists) subOp.Unlock() if listslen != 2 { t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) } //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) if opLists.Match(conn) == false { // we don't care about if it matches, we're testing race conditions t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) } subOp.StopMonitoringLists() time.Sleep(time.Second) subOp.Lock() if len(subOp.lists) != 0 { t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) } subOp.Unlock() restoreConnection() } func TestNewOperatorListsComplex(t *testing.T) { t.Log("Test NewOperator() Lists complex") var subOp *Operator var list []Operator listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains", "data": "testdata/lists/domains/", "sensitive": false}]` opLists, err := NewOperator(List, false, OpList, listData, list) if err != nil { t.Error("NewOperator Lists complex, shouldn't be nil: ", err) t.Fail() } if err := opLists.Compile(); err != nil { t.Error("NewOperator Lists complex, Compile() error:", err) } opLists.List = *unmarshalListData(opLists.Data, t) for i := 0; i < len(opLists.List); i++ { if err := opLists.List[i].Compile(); err != nil { t.Error("NewOperator Lists complex, Compile() subitem error:", err) } if opLists.List[i].Type == Lists { subOp = &opLists.List[i] } } time.Sleep(time.Second) subOp.Lock() if len(subOp.lists) != 2 { t.Error("NewOperator Lists complex, number of domains error:", subOp.lists) } subOp.Unlock() if opLists.Match(conn) == false { t.Error("Test NewOperator() Lists complex, doesn't match") } subOp.StopMonitoringLists() time.Sleep(time.Second) subOp.Lock() if len(subOp.lists) != 0 { t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) } subOp.Unlock() restoreConnection() } func TestNewOperatorListsDomainsRegexp(t *testing.T) { t.Log("Test NewOperator() Lists domains_regexp") var subOp *Operator var list []Operator listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]` opLists, err := NewOperator(List, false, OpList, listData, list) if err != nil { t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) t.Fail() } if err := opLists.Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() error:", err) } opLists.List = *unmarshalListData(opLists.Data, t) for i := 0; i < len(opLists.List); i++ { if err := opLists.List[i].Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) } if opLists.List[i].Type == Lists { subOp = &opLists.List[i] } } time.Sleep(time.Second) if opLists.Match(conn) == false { t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) } subOp.Lock() listslen := len(subOp.lists) subOp.Unlock() if listslen != 2 { t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) } //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) if opLists.Match(conn) == false { // we don't care about if it matches, we're testing race conditions t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) } subOp.StopMonitoringLists() time.Sleep(time.Second) subOp.Lock() if len(subOp.lists) != 0 { t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) } subOp.Unlock() restoreConnection() } // Must be launched with -race to test that we don't cause leaks // Race occured on operator.go:241 reListCmp().MathString() // fixed here: 53419fe func TestRaceNewOperatorListsDomainsRegexp(t *testing.T) { t.Log("Test NewOperator() Lists domains_regexp") var subOp *Operator var list []Operator listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]` opLists, err := NewOperator(List, false, OpList, listData, list) if err != nil { t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) t.Fail() } if err := opLists.Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() error:", err) } opLists.List = *unmarshalListData(opLists.Data, t) for i := 0; i < len(opLists.List); i++ { if err := opLists.List[i].Compile(); err != nil { t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) } if opLists.List[i].Type == Lists { subOp = &opLists.List[i] } } // touch domains list in background, to force a reload. go func() { touches := 1000 for { if touches < 0 { break } core.Exec("/bin/touch", []string{"testdata/lists/regexp/domainsregexp.txt"}) touches-- time.Sleep(100 * time.Millisecond) //t.Log("touching:", touches) } }() time.Sleep(time.Second) subOp.Lock() listslen := len(subOp.lists) subOp.Unlock() if listslen != 2 { t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) } tries := 10000 for { if tries < 0 { break } //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) if opLists.Match(conn) == false { // we don't care about if it matches, we're testing race conditions t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) } tries-- time.Sleep(10 * time.Millisecond) } subOp.StopMonitoringLists() time.Sleep(time.Second) subOp.Lock() if len(subOp.lists) != 0 { t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) } subOp.Unlock() restoreConnection() } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/rule.go����������������������������������������������������������������0000664�0000000�0000000�00000010666�15003540030�0017410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "fmt" "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // Action of a rule type Action string // Actions of rules const ( Allow = Action("allow") Deny = Action("deny") Reject = Action("reject") ) // Duration of a rule type Duration string // daemon possible durations const ( Once = Duration("once") Restart = Duration("until restart") Always = Duration("always") ) // Rule represents an action on a connection. // The fields match the ones saved as json to disk. // If a .json rule file is modified on disk, it's reloaded automatically. type Rule struct { // Save date fields as string, to avoid issues marshalling Time (#1140). Created string `json:"created"` Updated string `json:"updated"` Name string `json:"name"` Description string `json:"description"` Action Action `json:"action"` Duration Duration `json:"duration"` Operator Operator `json:"operator"` Enabled bool `json:"enabled"` Precedence bool `json:"precedence"` Nolog bool `json:"nolog"` } // Create creates a new rule object with the specified parameters. func Create(name, description string, enabled, precedence, nolog bool, action Action, duration Duration, op *Operator) *Rule { return &Rule{ Created: time.Now().Format(time.RFC3339), Enabled: enabled, Precedence: precedence, Nolog: nolog, Name: name, Description: description, Action: action, Duration: duration, Operator: *op, } } func (r *Rule) String() string { enabled := "Disabled" if r.Enabled { enabled = "Enabled" } return fmt.Sprintf("[%s] %s: if(%s){ %s %s }", enabled, r.Name, r.Operator.String(), r.Action, r.Duration) } // Match performs on a connection the checks a Rule has, to determine if it // must be allowed or denied. func (r *Rule) Match(con *conman.Connection) bool { return r.Operator.Match(con) } // Deserialize translates back the rule received to a Rule object func Deserialize(reply *protocol.Rule) (*Rule, error) { if reply.Operator == nil { log.Warning("Deserialize rule, Operator nil") return nil, fmt.Errorf("invalid operator") } operator, err := NewOperator( Type(reply.Operator.Type), Sensitive(reply.Operator.Sensitive), Operand(reply.Operator.Operand), reply.Operator.Data, make([]Operator, 0), ) if err != nil { log.Warning("Deserialize rule, NewOperator() error: %s", err) return nil, err } newRule := Create( reply.Name, reply.Description, reply.Enabled, reply.Precedence, reply.Nolog, Action(reply.Action), Duration(reply.Duration), operator, ) if Type(reply.Operator.Type) == List { newRule.Operator.Data = "" reply.Operator.Data = "" for i := 0; i < len(reply.Operator.List); i++ { newRule.Operator.List = append( newRule.Operator.List, Operator{ Type: Type(reply.Operator.List[i].Type), Sensitive: Sensitive(reply.Operator.List[i].Sensitive), Operand: Operand(reply.Operator.List[i].Operand), Data: string(reply.Operator.List[i].Data), }, ) } } return newRule, nil } // Serialize translates a Rule to the protocol object func (r *Rule) Serialize() *protocol.Rule { if r == nil { return nil } created, err := time.Parse(time.RFC3339, r.Created) if err != nil { log.Warning("Error parsing rule Created date (it should be in RFC3339 format): %s (%s)", err, string(r.Name)) log.Warning("using current time instead: %s", created) created = time.Now() } protoRule := &protocol.Rule{ Created: created.Unix(), Name: string(r.Name), Description: string(r.Description), Enabled: bool(r.Enabled), Precedence: bool(r.Precedence), Nolog: bool(r.Nolog), Action: string(r.Action), Duration: string(r.Duration), Operator: &protocol.Operator{ Type: string(r.Operator.Type), Sensitive: bool(r.Operator.Sensitive), Operand: string(r.Operator.Operand), Data: string(r.Operator.Data), }, } if r.Operator.Type == List { r.Operator.Data = "" for i := 0; i < len(r.Operator.List); i++ { protoRule.Operator.List = append(protoRule.Operator.List, &protocol.Operator{ Type: string(r.Operator.List[i].Type), Sensitive: bool(r.Operator.List[i].Sensitive), Operand: string(r.Operator.List[i].Operand), Data: string(r.Operator.List[i].Data), }) } } return protoRule } ��������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/rule_test.go�����������������������������������������������������������0000664�0000000�0000000�00000006223�15003540030�0020441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "testing" ) func TestCreate(t *testing.T) { t.Log("Test: Create rule") var list []Operator oper, _ := NewOperator(Simple, false, OpTrue, "", list) r := Create("000-test-name", "rule description 000", true, false, false, Allow, Once, oper) t.Run("New rule must not be nil", func(t *testing.T) { if r == nil { t.Error("Create() returned nil") t.Fail() } }) t.Run("Rule name must be 000-test-name", func(t *testing.T) { if r.Name != "000-test-name" { t.Error("Rule name error:", r.Name) t.Fail() } }) t.Run("Rule must be enabled", func(t *testing.T) { if r.Enabled == false { t.Error("Rule Enabled is false:", r) t.Fail() } }) t.Run("Rule Precedence must be false", func(t *testing.T) { if r.Precedence == true { t.Error("Rule Precedence is true:", r) t.Fail() } }) t.Run("Rule Action must be Allow", func(t *testing.T) { if r.Action != Allow { t.Error("Rule Action is not Allow:", r.Action) t.Fail() } }) t.Run("Rule Duration should be Once", func(t *testing.T) { if r.Duration != Once { t.Error("Rule Duration is not Once:", r.Duration) t.Fail() } }) } func TestRuleSerializers(t *testing.T) { t.Log("Test: Serializers()") var opList []Operator opList = append(opList, Operator{ Type: Simple, Operand: OpProcessPath, Data: "/path/x", }) opList = append(opList, Operator{ Type: Simple, Operand: OpDstPort, Data: "23", }) op, _ := NewOperator(List, false, OpTrue, "", opList) // this string must be erased after Deserialized op.Data = "[\"test\": true]" r := Create("000-test-serializer-list", "rule description 000", true, false, false, Allow, Once, op) rSerialized := r.Serialize() t.Run("Serialize() must not return nil", func(t *testing.T) { if rSerialized == nil { t.Error("rule.Serialize() returned nil") t.Fail() } }) rDeser, err := Deserialize(rSerialized) t.Run("Deserialize must not return error", func(t *testing.T) { if err != nil { t.Error("rule.Serialize() returned error:", err) t.Fail() } }) // commit: b93051026e6a82ba07a5ac2f072880e69f04c238 t.Run("Deserialize. Operator.Data must be empty", func(t *testing.T) { if rDeser.Operator.Data != "" { t.Error("rule.Deserialize() Operator.Data not emptied:", rDeser.Operator.Data) t.Fail() } }) t.Run("Deserialize. Operator.List must be expanded", func(t *testing.T) { if len(rDeser.Operator.List) != 2 { t.Error("rule.Deserialize() invalid Operator.List:", rDeser.Operator.List) t.Fail() } if rDeser.Operator.List[0].Operand != OpProcessPath { t.Error("rule.Deserialize() invalid Operator.List 1:", rDeser.Operator.List) t.Fail() } if rDeser.Operator.List[1].Operand != OpDstPort { t.Error("rule.Deserialize() invalid Operator.List 2:", rDeser.Operator.List) t.Fail() } if rDeser.Operator.List[0].Type != Simple || rDeser.Operator.List[1].Type != Simple { t.Error("rule.Deserialize() invalid Operator.List 3:", rDeser.Operator.List) t.Fail() } if rDeser.Operator.List[0].Data != "/path/x" || rDeser.Operator.List[1].Data != "23" { t.Error("rule.Deserialize() invalid Operator.List 4:", rDeser.Operator.List) t.Fail() } }) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/��������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017712�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/000-allow-chrome.json�����������������������������������������0000664�0000000�0000000�00000000570�15003540030�0023475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2020-12-13T18:06:52.209804547+01:00", "updated": "2020-12-13T18:06:52.209857713+01:00", "name": "000-allow-chrome", "enabled": true, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/opt/google/chrome/chrome", "list": [] } }����������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/001-deny-chrome.json������������������������������������������0000664�0000000�0000000�00000000567�15003540030�0023325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2020-12-13T17:54:49.067148304+01:00", "updated": "2020-12-13T17:54:49.067213602+01:00", "name": "001-deny-chrome", "enabled": true, "precedence": false, "action": "deny", "duration": "always", "operator": { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/opt/google/chrome/chrome", "list": [] } }�����������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/invalid-regexp-list.json��������������������������������������0000664�0000000�0000000�00000001526�15003540030�0024500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2020-12-13T18:06:52.209804547+01:00", "updated": "2020-12-13T18:06:52.209857713+01:00", "name": "invalid-regexp-list", "enabled": true, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "list", "operand": "list", "sensitive": false, "data": "[{\"type\": \"regexp\", \"operand\": \"process.path\", \"sensitive\": false, \"data\": \"^(/di(rmngr$\"}, {\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"53\", \"sensitive\": false}]", "list": [ { "type": "regexp", "operand": "process.path", "sensitive": false, "data": "^(/di(rmngr)$", "list": null }, { "type": "simple", "operand": "dest.port", "sensitive": false, "data": "53", "list": null } ] } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/invalid-regexp.json�������������������������������������������0000664�0000000�0000000�00000000574�15003540030�0023531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2020-12-13T18:06:52.209804547+01:00", "updated": "2020-12-13T18:06:52.209857713+01:00", "name": "invalid-regexp", "enabled": true, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "regexp", "operand": "process.path", "sensitive": false, "data": "/opt/((.*)google/chrome/chrome", "list": [] } } ������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/��������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0021050�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/domains/������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022502�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/domains/domainlists.txt���������������������������������0000664�0000000�0000000�00000000164�15003540030�0025572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# this line must be ignored, 0.0.0.0 www.test.org 0.0.0.0 www.test.org 127.0.0.1 www.test.org 0.0.0.0 opensnitch.io ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/ips/����������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0021643�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/ips/ips.txt���������������������������������������������0000664�0000000�0000000�00000000227�15003540030�0023200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# this line must be ignored, 0.0.0.0 www.test.org # empty lines are also ignored 1.1.1.1 185.53.178.14 # duplicated entries should be ignored 1.1.1.1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/nets/���������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022021�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/nets/nets.txt�������������������������������������������0000664�0000000�0000000�00000000240�15003540030�0023527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# this line must be ignored, 0.0.0.0 www.test.org # empty lines are also ignored 1.1.1.0/24 185.53.178.0/24 # duplicated entries should be ignored 1.1.1.0/24 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/regexp/�������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022342�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/lists/regexp/domainsregexp.txt��������������������������������0000664�0000000�0000000�00000000132�15003540030�0025744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# this line must be ignored, 0.0.0.0 www.test.org www.test.org www.test.org opensnitch.io ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/live_reload/��������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022177�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/live_reload/test-live-reload-delete.json����������������������0000664�0000000�0000000�00000000620�15003540030�0027510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2020-12-13T18:06:52.209804547+01:00", "updated": "2020-12-13T18:06:52.209857713+01:00", "name": "test-live-reload-delete", "enabled": true, "precedence": true, "action": "deny", "duration": "always", "operator": { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/usr/bin/curl", "list": [] } }����������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/live_reload/test-live-reload-remove.json����������������������0000664�0000000�0000000�00000000620�15003540030�0027543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2020-12-13T18:06:52.209804547+01:00", "updated": "2020-12-13T18:06:52.209857713+01:00", "name": "test-live-reload-remove", "enabled": true, "precedence": true, "action": "deny", "duration": "always", "operator": { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/usr/bin/curl", "list": [] } }����������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/rule-disabled-operator-list-expanded.json���������������������0000664�0000000�0000000�00000001253�15003540030�0027712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2023-10-03T18:06:52.209804547+01:00", "updated": "2023-10-03T18:06:52.209857713+01:00", "name": "rule-disabled-with-operators-list-expanded", "enabled": false, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "list", "operand": "list", "sensitive": false, "data": "", "list": [ { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/usr/bin/telnet", "list": null }, { "type": "simple", "operand": "dest.port", "sensitive": false, "data": "53", "list": null } ] } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/rule-disabled-operator-list.json������������������������������0000664�0000000�0000000�00000001104�15003540030�0026117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2023-10-03T18:06:52.209804547+01:00", "updated": "2023-10-03T18:06:52.209857713+01:00", "name": "rule-disabled-with-operators-list-as-json-string", "enabled": false, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "list", "operand": "list", "sensitive": false, "data": "[{\"type\": \"simple\", \"operand\": \"process.path\", \"sensitive\": false, \"data\": \"/usr/bin/telnet\"}, {\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"53\", \"sensitive\": false}]", "list": [ ] } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/rule-operator-list-data-empty.json����������������������������0000664�0000000�0000000�00000001243�15003540030�0026421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2023-10-03T18:06:52.209804547+01:00", "updated": "2023-10-03T18:06:52.209857713+01:00", "name": "rule-with-operator-list-data-empty", "enabled": true, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "list", "operand": "list", "sensitive": false, "data": "", "list": [ { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/usr/bin/telnet", "list": null }, { "type": "simple", "operand": "dest.port", "sensitive": false, "data": "53", "list": null } ] } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/rule/testdata/rule-operator-list.json���������������������������������������0000664�0000000�0000000�00000001537�15003540030�0024364�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "created": "2023-10-03T18:06:52.209804547+01:00", "updated": "2023-10-03T18:06:52.209857713+01:00", "name": "rule-with-operator-list", "enabled": true, "precedence": true, "action": "allow", "duration": "always", "operator": { "type": "list", "operand": "list", "sensitive": false, "data": "[{\"type\": \"simple\", \"operand\": \"process.path\", \"sensitive\": false, \"data\": \"/usr/bin/telnet\"}, {\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"53\", \"sensitive\": false}]", "list": [ { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/usr/bin/telnet", "list": null }, { "type": "simple", "operand": "dest.port", "sensitive": false, "data": "53", "list": null } ] } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/statistics/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017324�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/statistics/event.go���������������������������������������������������������0000664�0000000�0000000�00000001251�15003540030�0020773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package statistics import ( "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) type Event struct { Time time.Time Connection *conman.Connection Rule *rule.Rule } func NewEvent(con *conman.Connection, match *rule.Rule) *Event { return &Event{ Time: time.Now(), Connection: con, Rule: match, } } func (e *Event) Serialize() *protocol.Event { return &protocol.Event{ Time: e.Time.Format("2006-01-02 15:04:05"), Connection: e.Connection.Serialize(), Rule: e.Rule.Serialize(), Unixnano: e.Time.UnixNano(), } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/statistics/stats.go���������������������������������������������������������0000664�0000000�0000000�00000014375�15003540030�0021023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package statistics import ( "strconv" "sync" "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/loggers" "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // StatsConfig holds the stats confguration type StatsConfig struct { MaxEvents int `json:"MaxEvents"` MaxStats int `json:"MaxStats"` Workers int `json:"Workers"` } type conEvent struct { con *conman.Connection match *rule.Rule wasMissed bool } // Statistics holds the connections and statistics the daemon intercepts. // The connections are stored in the Events slice. type Statistics struct { logger *loggers.LoggerManager rules *rule.Loader Started time.Time ByExecutable map[string]uint64 ByPort map[string]uint64 ByProto map[string]uint64 ByAddress map[string]uint64 ByHost map[string]uint64 jobs chan conEvent ByUID map[string]uint64 Events []*Event Dropped int // max number of events to keep in the buffer maxEvents int // max number of entries for each By* map maxStats int DNSResponses int Connections int Ignored int Accepted int RuleHits int RuleMisses int sync.RWMutex } // New returns a new Statistics object and initializes the go routines to update the stats. func New(rules *rule.Loader) (stats *Statistics) { stats = &Statistics{ Started: time.Now(), Events: make([]*Event, 0), ByProto: make(map[string]uint64), ByAddress: make(map[string]uint64), ByHost: make(map[string]uint64), ByPort: make(map[string]uint64), ByUID: make(map[string]uint64), ByExecutable: make(map[string]uint64), rules: rules, jobs: make(chan conEvent), maxEvents: 150, maxStats: 25, } return stats } // SetLoggers sets the configured loggers where we'll write the events. func (s *Statistics) SetLoggers(loggers *loggers.LoggerManager) { s.logger = loggers } // SetLimits configures the max events to keep in the backlog before sending // the stats to the UI, or while the UI is not connected. // if the backlog is full, it'll be shifted by one. func (s *Statistics) SetLimits(config StatsConfig) { if config.MaxEvents > 0 { s.maxEvents = config.MaxEvents } if config.MaxStats > 0 { s.maxStats = config.MaxStats } wrks := config.Workers if wrks == 0 { wrks = 6 } log.Info("Stats, max events: %d, max stats: %d, max workers: %d", s.maxStats, s.maxEvents, wrks) for i := 0; i < wrks; i++ { go s.eventWorker(i) } } // OnConnectionEvent sends the details of a new connection throughout a channel, // in order to add the connection to the stats. func (s *Statistics) OnConnectionEvent(con *conman.Connection, match *rule.Rule, wasMissed bool) { s.jobs <- conEvent{ con: con, match: match, wasMissed: wasMissed, } action := "<nil>" rname := "<nil>" if match != nil { action = string(match.Action) rname = string(match.Name) } s.logger.Log(con.Serialize(), action, rname) } // OnDNSResponse increases the counter of dns and accepted connections. func (s *Statistics) OnDNSResponse() { s.Lock() defer s.Unlock() s.DNSResponses++ s.Accepted++ } // OnIgnored increases the counter of ignored and accepted connections. func (s *Statistics) OnIgnored() { s.Lock() defer s.Unlock() s.Ignored++ s.Accepted++ } func (s *Statistics) incMap(m *map[string]uint64, key string) { if val, found := (*m)[key]; found == false { // do we have enough space left? nElems := len(*m) if nElems >= s.maxStats { // find the element with less hits nMin := uint64(9999999999) minKey := "" for k, v := range *m { if v < nMin { minKey = k nMin = v } } // remove it if minKey != "" { delete(*m, minKey) } } (*m)[key] = 1 } else { (*m)[key] = val + 1 } } func (s *Statistics) eventWorker(id int) { log.Debug("Stats worker #%d started.", id) for true { select { case job := <-s.jobs: s.onConnection(job.con, job.match, job.wasMissed) } } } func (s *Statistics) onConnection(con *conman.Connection, match *rule.Rule, wasMissed bool) { s.Lock() defer s.Unlock() s.Connections++ if wasMissed { s.RuleMisses++ } else { s.RuleHits++ } if wasMissed == false && match.Action == rule.Allow { s.Accepted++ } else { s.Dropped++ } s.incMap(&s.ByProto, con.Protocol) s.incMap(&s.ByAddress, con.DstIP.String()) if con.DstHost != "" { s.incMap(&s.ByHost, con.DstHost) } s.incMap(&s.ByPort, strconv.FormatUint(uint64(con.DstPort), 10)) s.incMap(&s.ByUID, strconv.Itoa(con.Entry.UserId)) s.incMap(&s.ByExecutable, con.Process.Path) // if we reached the limit, shift everything back // by one position nEvents := len(s.Events) if nEvents == s.maxEvents { s.Events = s.Events[1:] } if wasMissed { return } s.Events = append(s.Events, NewEvent(con, match)) } func (s *Statistics) serializeEvents() []*protocol.Event { nEvents := len(s.Events) serialized := make([]*protocol.Event, nEvents) for i, e := range s.Events { serialized[i] = e.Serialize() } return serialized } // emptyStats empties the stats once we've sent them to the GUI. // We don't need them anymore here. func (s *Statistics) emptyStats() { s.Lock() if len(s.Events) > 0 { s.Events = make([]*Event, 0) } s.Unlock() } // Serialize returns the collected statistics. // After return the stats, the Events are emptied, to keep collecting more stats // and not miss connections. func (s *Statistics) Serialize() *protocol.Statistics { s.Lock() defer s.emptyStats() defer s.Unlock() return &protocol.Statistics{ DaemonVersion: core.Version, Rules: uint64(s.rules.NumRules()), Uptime: uint64(time.Since(s.Started).Seconds()), DnsResponses: uint64(s.DNSResponses), Connections: uint64(s.Connections), Ignored: uint64(s.Ignored), Accepted: uint64(s.Accepted), Dropped: uint64(s.Dropped), RuleHits: uint64(s.RuleHits), RuleMisses: uint64(s.RuleMisses), Events: s.serializeEvents(), ByProto: s.ByProto, ByAddress: s.ByAddress, ByHost: s.ByHost, ByPort: s.ByPort, ByUid: s.ByUID, ByExecutable: s.ByExecutable, } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/system-fw.json��������������������������������������������������������������0000664�0000000�0000000�00000014772�15003540030�0017776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "Enabled": true, "Version": 1, "SystemRules": [ { "Rule": { "Table": "mangle", "Chain": "OUTPUT", "Enabled": false, "Position": "0", "Description": "Allow icmp", "Parameters": "-p icmp", "Expressions": [], "Target": "ACCEPT", "TargetParameters": "" }, "Chains": [] }, { "Chains": [ { "Name": "forward", "Table": "filter", "Family": "inet", "Priority": "", "Type": "filter", "Hook": "forward", "Policy": "accept", "Rules": [] }, { "Name": "output", "Table": "filter", "Family": "inet", "Priority": "", "Type": "filter", "Hook": "output", "Policy": "accept", "Rules": [] }, { "Name": "input", "Table": "filter", "Family": "inet", "Priority": "", "Type": "filter", "Hook": "input", "Policy": "accept", "Rules": [ { "Enabled": false, "Position": "0", "Description": "Allow SSH server connections when input policy is DROP", "Parameters": "", "Expressions": [ { "Statement": { "Op": "", "Name": "tcp", "Values": [ { "Key": "dport", "Value": "22" } ] } } ], "Target": "accept", "TargetParameters": "" } ] }, { "Name": "filter-prerouting", "Table": "nat", "Family": "inet", "Priority": "", "Type": "filter", "Hook": "prerouting", "Policy": "accept", "Rules": [] }, { "Name": "prerouting", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "prerouting", "Policy": "accept", "Rules": [] }, { "Name": "postrouting", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "postrouting", "Policy": "accept", "Rules": [] }, { "Name": "prerouting", "Table": "nat", "Family": "inet", "Priority": "", "Type": "natdest", "Hook": "prerouting", "Policy": "accept", "Rules": [] }, { "Name": "postrouting", "Table": "nat", "Family": "inet", "Priority": "", "Type": "natsource", "Hook": "postrouting", "Policy": "accept", "Rules": [] }, { "Name": "input", "Table": "nat", "Family": "inet", "Priority": "", "Type": "natsource", "Hook": "input", "Policy": "accept", "Rules": [] }, { "Name": "output", "Table": "nat", "Family": "inet", "Priority": "", "Type": "natdest", "Hook": "output", "Policy": "accept", "Rules": [] }, { "Name": "output", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "output", "Policy": "accept", "Rules": [ { "Enabled": true, "Position": "0", "Description": "Allow ICMP", "Expressions": [ { "Statement": { "Op": "", "Name": "icmp", "Values": [ { "Key": "type", "Value": "echo-request,echo-reply,destination-unreachable" } ] } } ], "Target": "accept", "TargetParameters": "" }, { "Enabled": true, "Position": "0", "Description": "Allow ICMPv6", "Expressions": [ { "Statement": { "Op": "", "Name": "icmpv6", "Values": [ { "Key": "type", "Value": "echo-request,echo-reply,destination-unreachable" } ] } } ], "Target": "accept", "TargetParameters": "" }, { "Enabled": false, "Position": "0", "Description": "Exclude WireGuard VPN from being intercepted", "Parameters": "", "Expressions": [ { "Statement": { "Op": "", "Name": "udp", "Values": [ { "Key": "dport", "Value": "51820" } ] } } ], "Target": "accept", "TargetParameters": "" } ] }, { "Name": "forward", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "forward", "Policy": "accept", "Rules": [ { "UUID": "7d7394e1-100d-4b87-a90a-cd68c46edb0b", "Enabled": false, "Position": "0", "Description": "Intercept forwarded connections (docker, etc)", "Expressions": [ { "Statement": { "Op": "", "Name": "ct", "Values": [ { "Key": "state", "Value": "new" } ] } } ], "Target": "queue", "TargetParameters": "num 0" } ] } ] } ] } ������opensnitch-1.6.9/daemon/ui/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015547�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/alerts.go����������������������������������������������������������������0000664�0000000�0000000�00000007175�15003540030�0017402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ui import ( "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/procmon" "github.com/evilsocket/opensnitch/daemon/ui/protocol" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/encoding/gzip" ) // NewWarningAlert builts a new warning alert func NewWarningAlert(what protocol.Alert_What, data interface{}) *protocol.Alert { return NewAlert(protocol.Alert_WARNING, what, protocol.Alert_SHOW_ALERT, protocol.Alert_MEDIUM, data) } // NewErrorAlert builts a new error alert func NewErrorAlert(what protocol.Alert_What, data interface{}) *protocol.Alert { return NewAlert(protocol.Alert_ERROR, what, protocol.Alert_SHOW_ALERT, protocol.Alert_HIGH, data) } // NewAlert builts a new generic alert func NewAlert(atype protocol.Alert_Type, what protocol.Alert_What, action protocol.Alert_Action, prio protocol.Alert_Priority, data interface{}) *protocol.Alert { a := &protocol.Alert{ Id: uint64(time.Now().UnixNano()), Type: atype, Action: action, What: what, Priority: prio, } switch what { case protocol.Alert_KERNEL_EVENT: switch data.(type) { case procmon.Process: a.Data = &protocol.Alert_Proc{ data.(*procmon.Process).Serialize(), } case string: a.Data = &protocol.Alert_Text{data.(string)} a.Action = protocol.Alert_SHOW_ALERT } case protocol.Alert_CONNECTION: a.Data = &protocol.Alert_Conn{ data.(*conman.Connection).Serialize(), } case protocol.Alert_GENERIC: a.Data = &protocol.Alert_Text{data.(string)} } return a } // SendInfoAlert sends an info alert func (c *Client) SendInfoAlert(data interface{}) { c.PostAlert(protocol.Alert_INFO, protocol.Alert_GENERIC, protocol.Alert_SHOW_ALERT, protocol.Alert_LOW, data) } // SendWarningAlert sends an warning alert func (c *Client) SendWarningAlert(data interface{}) { c.PostAlert(protocol.Alert_WARNING, protocol.Alert_GENERIC, protocol.Alert_SHOW_ALERT, protocol.Alert_MEDIUM, data) } // SendErrorAlert sends an error alert func (c *Client) SendErrorAlert(data interface{}) { c.PostAlert(protocol.Alert_ERROR, protocol.Alert_GENERIC, protocol.Alert_SHOW_ALERT, protocol.Alert_HIGH, data) } // alertsDispatcher waits to be connected to the GUI. // Once connected, dispatches all the queued alerts. func (c *Client) alertsDispatcher() { queuedAlerts := make(chan protocol.Alert, 32) connected := false isQueueFull := func(qdAlerts chan protocol.Alert) bool { return len(qdAlerts) > 31 } isQueueEmpty := func(qdAlerts chan protocol.Alert) bool { return len(qdAlerts) == 0 } queueAlert := func(qdAlerts chan protocol.Alert, pbAlert protocol.Alert) { if isQueueFull(qdAlerts) { v := <-qdAlerts // empty queue before adding a new one log.Debug("Discarding queued alert (%d): %v", len(qdAlerts), v) } select { case qdAlerts <- pbAlert: default: log.Debug("Alert not sent to queue, full? (%d)", len(qdAlerts)) } } for { select { case pbAlert := <-c.alertsChan: if !connected { queueAlert(queuedAlerts, pbAlert) continue } c.dispatchAlert(pbAlert) case ready := <-c.isConnected: connected = ready if ready { log.Important("UI connected, dispathing queued alerts: %d", len(c.alertsChan)) for { if isQueueEmpty(queuedAlerts) { // no more queued alerts, exit break } c.dispatchAlert(<-queuedAlerts) } } } } } func (c *Client) dispatchAlert(pbAlert protocol.Alert) { if c.client == nil { return } ctx, cancel := context.WithTimeout(context.Background(), time.Second) c.client.PostAlert(ctx, &pbAlert, grpc.UseCompressor(gzip.Name)) cancel() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/auth/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016510�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/auth/auth.go�������������������������������������������������������������0000664�0000000�0000000�00000005437�15003540030�0020011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package auth import ( "crypto/tls" "crypto/x509" "io/ioutil" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/ui/config" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) // client auth types: // https://pkg.go.dev/crypto/tls#ClientAuthType var ( clientAuthType = map[string]tls.ClientAuthType{ "no-client-cert": tls.NoClientCert, "req-cert": tls.RequestClientCert, "req-any-cert": tls.RequireAnyClientCert, "verify-cert": tls.VerifyClientCertIfGiven, "req-and-verify-cert": tls.RequireAndVerifyClientCert, } ) const ( // AuthSimple will use WithInsecure() AuthSimple = "simple" // AuthTLSSimple will use a common CA certificate, shared between the server // and all the clients. AuthTLSSimple = "tls-simple" // AuthTLSMutual will use a CA certificate and a client cert and key // to authenticate each client. AuthTLSMutual = "tls-mutual" ) // New returns the configuration that the UI will use // to connect with the server. func New(config *config.Config) (grpc.DialOption, error) { config.RLock() credsType := config.Server.Authentication.Type tlsOpts := config.Server.Authentication.TLSOptions config.RUnlock() if credsType == "" || credsType == AuthSimple { log.Debug("UI auth: simple") return grpc.WithInsecure(), nil } certPool := x509.NewCertPool() // use CA certificate to authenticate clients if supplied if tlsOpts.CACert != "" { if caPem, err := ioutil.ReadFile(tlsOpts.CACert); err != nil { log.Warning("reading UI auth CA certificate (%s): %s", credsType, err) } else { if !certPool.AppendCertsFromPEM(caPem) { log.Warning("adding UI auth CA certificate (%s): %s", credsType, err) } } } // use server certificate to authenticate clients if supplied if tlsOpts.ServerCert != "" { if serverPem, err := ioutil.ReadFile(tlsOpts.ServerCert); err != nil { log.Warning("reading auth server cert: %s", err) } else { if !certPool.AppendCertsFromPEM(serverPem) { log.Warning("adding UI auth server cert (%s): %s", credsType, err) } } } // set config of tls credential // https://pkg.go.dev/crypto/tls#Config tlsCfg := &tls.Config{ InsecureSkipVerify: tlsOpts.SkipVerify, RootCAs: certPool, } // https://pkg.go.dev/google.golang.org/grpc/credentials#SecurityLevel if credsType == AuthTLSMutual { tlsCfg.ClientAuth = clientAuthType[tlsOpts.ClientAuthType] clientCert, err := tls.LoadX509KeyPair( tlsOpts.ClientCert, tlsOpts.ClientKey, ) if err != nil { return nil, err } log.Debug(" using client cert: %s", tlsOpts.ClientCert) log.Debug(" using client key: %s", tlsOpts.ClientKey) tlsCfg.Certificates = []tls.Certificate{clientCert} } return grpc.WithTransportCredentials( credentials.NewTLS(tlsCfg), ), nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/client.go����������������������������������������������������������������0000664�0000000�0000000�00000022554�15003540030�0017364�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ui import ( "fmt" "net" "sync" "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/firewall/iptables" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/loggers" "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/statistics" "github.com/evilsocket/opensnitch/daemon/ui/auth" "github.com/evilsocket/opensnitch/daemon/ui/config" "github.com/evilsocket/opensnitch/daemon/ui/protocol" "github.com/fsnotify/fsnotify" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/keepalive" ) var ( configFile = "/etc/opensnitchd/default-config.json" dummyOperator, _ = rule.NewOperator(rule.Simple, false, rule.OpTrue, "", make([]rule.Operator, 0)) clientDisconnectedRule = rule.Create("ui.client.disconnected", "", true, false, false, rule.Allow, rule.Once, dummyOperator) // While the GUI is connected, deny by default everything until the user takes an action. clientConnectedRule = rule.Create("ui.client.connected", "", true, false, false, rule.Deny, rule.Once, dummyOperator) clientErrorRule = rule.Create("ui.client.error", "", true, false, false, rule.Allow, rule.Once, dummyOperator) clientConfig config.Config maxQueuedAlerts = 1024 ) // Client holds the connection information of a client. type Client struct { rules *rule.Loader stats *statistics.Statistics con *grpc.ClientConn configWatcher *fsnotify.Watcher client protocol.UIClient clientCtx context.Context clientCancel context.CancelFunc streamNotifications protocol.UI_NotificationsClient isConnected chan bool alertsChan chan protocol.Alert socketPath string unixSockPrefix string //isAsking is set to true if the client is awaiting a decision from the GUI isAsking bool isUnixSocket bool sync.RWMutex } // NewClient creates and configures a new client. func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics, rules *rule.Loader, loggers *loggers.LoggerManager) *Client { if localConfigFile != "" { configFile = localConfigFile } c := &Client{ stats: stats, rules: rules, isUnixSocket: false, isAsking: false, isConnected: make(chan bool), alertsChan: make(chan protocol.Alert, maxQueuedAlerts), } //for i := 0; i < 4; i++ { go c.alertsDispatcher() c.clientCtx, c.clientCancel = context.WithCancel(context.Background()) if watcher, err := fsnotify.NewWatcher(); err == nil { c.configWatcher = watcher } c.loadDiskConfiguration(false) if socketPath != "" { c.setSocketPath(c.getSocketPath(socketPath)) } loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers) stats.SetLimits(clientConfig.Stats) stats.SetLoggers(loggers) return c } // Connect starts the connection poller func (c *Client) Connect() { go c.poller() } // Close cancels the running tasks: pinging the server and (re)connection poller. func (c *Client) Close() { c.clientCancel() } // ProcMonitorMethod returns the monitor method configured. // If it's not present in the config file, it'll return an empty string. func (c *Client) ProcMonitorMethod() string { clientConfig.RLock() defer clientConfig.RUnlock() return clientConfig.ProcMonitorMethod } // InterceptUnknown returns func (c *Client) InterceptUnknown() bool { clientConfig.RLock() defer clientConfig.RUnlock() return clientConfig.InterceptUnknown } // GetFirewallType returns the firewall to use func (c *Client) GetFirewallType() string { clientConfig.RLock() defer clientConfig.RUnlock() if clientConfig.Firewall == "" { return iptables.Name } return clientConfig.Firewall } // DefaultAction returns the default configured action for func (c *Client) DefaultAction() rule.Action { isConnected := c.Connected() c.RLock() defer c.RUnlock() if isConnected { return clientConnectedRule.Action } return clientDisconnectedRule.Action } // DefaultDuration returns the default duration configured for a rule. // For example it can be: once, always, "until restart". func (c *Client) DefaultDuration() rule.Duration { c.RLock() defer c.RUnlock() return clientDisconnectedRule.Duration } // Connected checks if the client has established a connection with the server. func (c *Client) Connected() bool { c.RLock() defer c.RUnlock() if c.con == nil || c.con.GetState() != connectivity.Ready { return false } return true } // GetIsAsking returns the isAsking flag func (c *Client) GetIsAsking() bool { c.RLock() defer c.RUnlock() return c.isAsking } // SetIsAsking sets the isAsking flag func (c *Client) SetIsAsking(flag bool) { c.Lock() defer c.Unlock() c.isAsking = flag } func (c *Client) poller() { log.Debug("UI service poller started for socket %s", c.socketPath) wasConnected := false for { select { case <-c.clientCtx.Done(): log.Info("Client.poller() exit, Done()") goto Exit default: isConnected := c.Connected() if wasConnected != isConnected { c.onStatusChange(isConnected) wasConnected = isConnected } if c.Connected() == false { // connect and create the client if needed if err := c.connect(); err != nil { log.Warning("Error while connecting to UI service: %s", err) } } if c.Connected() == true { // if the client is connected and ready, send a ping if err := c.ping(time.Now()); err != nil { log.Warning("Error while pinging UI service: %s, state: %v", err, c.con.GetState()) } } time.Sleep(1 * time.Second) } } Exit: log.Info("uiClient exit") } func (c *Client) onStatusChange(connected bool) { if connected { log.Info("Connected to the UI service on %s", c.socketPath) go c.Subscribe() select { case c.isConnected <- true: default: } } else { log.Error("Connection to the UI service lost.") c.disconnect() } } func (c *Client) connect() (err error) { if c.Connected() { return } if c.con != nil { if c.con.GetState() == connectivity.TransientFailure || c.con.GetState() == connectivity.Shutdown { c.disconnect() } else { return } } if err := c.openSocket(); err != nil { log.Debug("connect() %s", err) c.disconnect() return err } if c.client == nil { c.client = protocol.NewUIClient(c.con) } return nil } func (c *Client) openSocket() (err error) { c.Lock() defer c.Unlock() dialOption, err := auth.New(&clientConfig) if err != nil { return fmt.Errorf("Invalid client auth options: %s", err) } if c.isUnixSocket { c.con, err = grpc.Dial(c.socketPath, dialOption, grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { return net.DialTimeout(c.unixSockPrefix, addr, timeout) })) } else { // https://pkg.go.dev/google.golang.org/grpc/keepalive#ClientParameters var kacp = keepalive.ClientParameters{ Time: 5 * time.Second, // if there's no activity after ^, wait 20s and close // server timeout is 20s by default. Timeout: 22 * time.Second, // send pings even without active streams PermitWithoutStream: true, } c.con, err = grpc.Dial(c.socketPath, dialOption, grpc.WithKeepaliveParams(kacp)) } return err } func (c *Client) disconnect() { c.Lock() defer c.Unlock() select { case c.isConnected <- false: default: } if c.con != nil { c.con.Close() c.con = nil log.Debug("client.disconnect()") } c.client = nil } func (c *Client) ping(ts time.Time) (err error) { if c.Connected() == false { return fmt.Errorf("service is not connected") } c.Lock() defer c.Unlock() ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() reqID := uint64(ts.UnixNano()) pReq := &protocol.PingRequest{ Id: reqID, Stats: c.stats.Serialize(), } c.stats.RLock() pong, err := c.client.Ping(ctx, pReq) c.stats.RUnlock() if err != nil { return err } if pong.Id != reqID { return fmt.Errorf("Expected pong with id 0x%x, got 0x%x", reqID, pong.Id) } return nil } // Ask sends a request to the server, with the values of a connection to be // allowed or denied. func (c *Client) Ask(con *conman.Connection) *rule.Rule { if c.client == nil { return nil } // FIXME: if timeout is fired, the rule is not added to the list in the GUI ctx, cancel := context.WithTimeout(context.Background(), time.Second*120) defer cancel() reply, err := c.client.AskRule(ctx, con.Serialize()) if err != nil { log.Warning("Error while asking for rule: %s - %v", err, con) return nil } r, err := rule.Deserialize(reply) if err != nil { return nil } return r } // PostAlert queues a new message to be delivered to the server func (c *Client) PostAlert(atype protocol.Alert_Type, awhat protocol.Alert_What, action protocol.Alert_Action, prio protocol.Alert_Priority, data interface{}) { if len(c.alertsChan) > maxQueuedAlerts-1 { // pop oldest alert if channel is full log.Debug("PostAlert() queue full, popping alert (%d)", len(c.alertsChan)) <-c.alertsChan } if c.Connected() == false { log.Debug("UI not connected, queueing alert: %d", len(c.alertsChan)) } c.alertsChan <- *NewAlert(atype, awhat, action, prio, data) } func (c *Client) monitorConfigWorker() { for { select { case event := <-c.configWatcher.Events: if (event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Remove == fsnotify.Remove) { c.loadDiskConfiguration(true) } } } } ����������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/client_test.go�����������������������������������������������������������0000664�0000000�0000000�00000005542�15003540030�0020421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ui import ( "encoding/json" "testing" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/firewall/iptables" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/loggers" "github.com/evilsocket/opensnitch/daemon/procmon" "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/statistics" "github.com/evilsocket/opensnitch/daemon/ui/config" ) var ( defaultConfig = &config.Config{ ProcMonitorMethod: procmon.MethodEbpf, DefaultAction: "allow", DefaultDuration: "once", InterceptUnknown: false, Firewall: "nftables", } reloadConfig = *defaultConfig ) func restoreConfigFile(t *testing.T) { // start from a clean state if _, err := core.Exec("cp", []string{ // unmodified default config "./testdata/orig-default-config.json", // config will be modified by some tests "./testdata/default-config.json", }); err != nil { t.Errorf("error copying default config file: %s", err) } } func validateConfig(t *testing.T, uiClient *Client, cfg *config.Config) { if uiClient.ProcMonitorMethod() != cfg.ProcMonitorMethod { t.Errorf("not expected ProcMonitorMethod value: %s, expected: %s", uiClient.ProcMonitorMethod(), cfg.ProcMonitorMethod) } if uiClient.GetFirewallType() != cfg.Firewall { t.Errorf("not expected FirewallType value: %s, expected: %s", uiClient.GetFirewallType(), cfg.Firewall) } if uiClient.InterceptUnknown() != cfg.InterceptUnknown { t.Errorf("not expected InterceptUnknown value: %v, expected: %v", uiClient.InterceptUnknown(), cfg.InterceptUnknown) } if uiClient.DefaultAction() != rule.Action(cfg.DefaultAction) { t.Errorf("not expected DefaultAction value: %s, expected: %s", clientDisconnectedRule.Action, cfg.DefaultAction) } } func TestClientConfig(t *testing.T) { restoreConfigFile(t) cfgFile := "./testdata/default-config.json" rules, err := rule.NewLoader(false) if err != nil { log.Fatal("") } stats := statistics.New(rules) loggerMgr := loggers.NewLoggerManager() uiClient := NewClient("unix:///tmp/osui.sock", cfgFile, stats, rules, loggerMgr) t.Run("validate-load-config", func(t *testing.T) { validateConfig(t, uiClient, defaultConfig) }) t.Run("validate-reload-config", func(t *testing.T) { reloadConfig.ProcMonitorMethod = procmon.MethodProc reloadConfig.DefaultAction = string(rule.Deny) reloadConfig.InterceptUnknown = true reloadConfig.Firewall = iptables.Name reloadConfig.Server.Address = "unix:///run/user/1000/opensnitch/osui.sock" plainJSON, err := json.Marshal(reloadConfig) if err != nil { t.Errorf("Error marshalling config: %s", err) } if err = config.Save(configFile, string(plainJSON)); err != nil { t.Errorf("error saving config to disk: %s", err) } time.Sleep(time.Second * 3) validateConfig(t, uiClient, &reloadConfig) }) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/config/������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017014�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/config/config.go���������������������������������������������������������0000664�0000000�0000000�00000006562�15003540030�0020621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package config import ( "encoding/json" "fmt" "io/ioutil" "os" "reflect" "sync" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log/loggers" "github.com/evilsocket/opensnitch/daemon/statistics" ) type ( serverTLSOptions struct { CACert string `json:"CACert"` ServerCert string `json:"ServerCert"` ServerKey string `json:"ServerKey"` ClientCert string `json:"ClientCert"` ClientKey string `json:"ClientKey"` // https://pkg.go.dev/crypto/tls#ClientAuthType ClientAuthType string `json:"ClientAuthType"` // https://pkg.go.dev/crypto/tls#Config SkipVerify bool `json:"SkipVerify"` // https://pkg.go.dev/crypto/tls#Conn.VerifyHostname // VerifyHostname bool // https://pkg.go.dev/crypto/tls#example-Config-VerifyConnection // VerifyConnection bool // VerifyPeerCertificate bool } serverAuth struct { // token?, google?, simple-tls, mutual-tls Type string `json:"Type"` TLSOptions serverTLSOptions `json:"TLSOptions"` } serverConfig struct { Loggers []loggers.LoggerConfig `json:"Loggers"` Address string `json:"Address"` LogFile string `json:"LogFile"` Authentication serverAuth `json:"Authentication"` } rulesOptions struct { Path string `json:"Path"` } ebpfOptions struct { ModulesPath string `json:"ModulesPath"` } // InternalOptions struct internalOptions struct { GCPercent int `json:"GCPercent"` FlushConnsOnStart bool `json:"FlushConnsOnStart"` } ) // Config holds the values loaded from configFile type Config struct { LogLevel *int32 `json:"LogLevel"` Firewall string `json:"Firewall"` DefaultAction string `json:"DefaultAction"` DefaultDuration string `json:"DefaultDuration"` ProcMonitorMethod string `json:"ProcMonitorMethod"` Ebpf ebpfOptions `json:"Ebpf"` Rules rulesOptions `json:"Rules"` Server serverConfig `json:"Server"` Stats statistics.StatsConfig `json:"Stats"` Internal internalOptions `json:"Internal"` InterceptUnknown bool `json:"InterceptUnknown"` LogUTC bool `json:"LogUTC"` LogMicro bool `json:"LogMicro"` sync.RWMutex } // Parse determines if the given configuration is ok. func Parse(rawConfig interface{}) (conf Config, err error) { if vt := reflect.ValueOf(rawConfig).Kind(); vt == reflect.String { err = json.Unmarshal([]byte((rawConfig.(string))), &conf) } else { err = json.Unmarshal(rawConfig.([]uint8), &conf) } return conf, err } // Load loads the content of a file from disk. func Load(configFile string) ([]byte, error) { raw, err := ioutil.ReadFile(configFile) if err != nil || len(raw) == 0 { return nil, err } return raw, nil } // Save writes daemon configuration to disk. func Save(configFile, rawConfig string) (err error) { if _, err = Parse(rawConfig); err != nil { return fmt.Errorf("Error parsing configuration %s: %s", rawConfig, err) } if err = os.Chmod(configFile, 0600); err != nil { log.Warning("unable to set permissions to default config: %s", err) } if err = ioutil.WriteFile(configFile, []byte(rawConfig), 0644); err != nil { log.Error("writing configuration to disk: %s", err) return err } return nil } ����������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/config_utils.go����������������������������������������������������������0000664�0000000�0000000�00000007703�15003540030�0020572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ui import ( "fmt" "strings" "runtime/debug" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/netlink" "github.com/evilsocket/opensnitch/daemon/procmon/monitor" "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/ui/config" ) func (c *Client) getSocketPath(socketPath string) string { c.Lock() defer c.Unlock() if strings.HasPrefix(socketPath, "unix:") == true { c.isUnixSocket = true c.unixSockPrefix = "unix" return socketPath[5:] } if strings.HasPrefix(socketPath, "unix-abstract:") == true { c.isUnixSocket = true c.unixSockPrefix = "unix-abstract" return socketPath[14:] } c.isUnixSocket = false return socketPath } func (c *Client) setSocketPath(socketPath string) { c.Lock() defer c.Unlock() c.socketPath = socketPath } func (c *Client) isProcMonitorEqual(newMonitorMethod string) bool { clientConfig.RLock() defer clientConfig.RUnlock() return newMonitorMethod == clientConfig.ProcMonitorMethod } func (c *Client) loadDiskConfiguration(reload bool) { raw, err := config.Load(configFile) if err != nil || len(raw) == 0 { // Sometimes we may receive 2 Write events on monitorConfigWorker, // Which may lead to read 0 bytes. log.Warning("Error loading configuration from disk %s: %s", configFile, err) return } if ok := c.loadConfiguration(raw); ok { if err := c.configWatcher.Add(configFile); err != nil { log.Error("Could not watch path: %s", err) return } } if reload { return } go c.monitorConfigWorker() } func (c *Client) loadConfiguration(rawConfig []byte) bool { var err error clientConfig, err = config.Parse(rawConfig) if err != nil { msg := fmt.Sprintf("Error parsing configuration %s: %s", configFile, err) log.Error(msg) c.SendWarningAlert(msg) return false } clientConfig.Lock() defer clientConfig.Unlock() // firstly load config level, to detect further errors if any if clientConfig.LogLevel != nil { log.SetLogLevel(int(*clientConfig.LogLevel)) } log.SetLogUTC(clientConfig.LogUTC) log.SetLogMicro(clientConfig.LogMicro) if clientConfig.Server.LogFile != "" { log.Close() log.OpenFile(clientConfig.Server.LogFile) } if clientConfig.Server.Address != "" { tempSocketPath := c.getSocketPath(clientConfig.Server.Address) if tempSocketPath != c.socketPath { // disconnect, and let the connection poller reconnect to the new address c.disconnect() } c.setSocketPath(tempSocketPath) } if clientConfig.DefaultAction != "" { clientDisconnectedRule.Action = rule.Action(clientConfig.DefaultAction) clientErrorRule.Action = rule.Action(clientConfig.DefaultAction) // TODO: reconfigure connected rule if changed, but not save it to disk. //clientConnectedRule.Action = rule.Action(clientConfig.DefaultAction) } if clientConfig.DefaultDuration != "" { clientDisconnectedRule.Duration = rule.Duration(clientConfig.DefaultDuration) clientErrorRule.Duration = rule.Duration(clientConfig.DefaultDuration) } reloaded := false if clientConfig.ProcMonitorMethod != "" { err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod, clientConfig.Ebpf.ModulesPath) if err != nil { msg := fmt.Sprintf("Unable to set new process monitor (%s) method from disk: %v", clientConfig.ProcMonitorMethod, err.Msg) log.Warning(msg) c.SendWarningAlert(msg) } else { reloaded = true } } if reloaded && clientConfig.Internal.FlushConnsOnStart { log.Debug("[config] flushing established connections") netlink.FlushConnections() } else { log.Debug("[config] not flushing established connections") } if clientConfig.Internal.GCPercent > 0 { oldgcpercent := debug.SetGCPercent(clientConfig.Internal.GCPercent) log.Info("GC percent set to %d, previously was %d", clientConfig.Internal.GCPercent, oldgcpercent) } // TODO: //c.stats.SetLimits(clientConfig.Stats) //loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers) //stats.SetLoggers(loggers) return true } �������������������������������������������������������������opensnitch-1.6.9/daemon/ui/notifications.go���������������������������������������������������������0000664�0000000�0000000�00000027432�15003540030�0020757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ui import ( "encoding/json" "fmt" "io" "io/ioutil" "strconv" "strings" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/firewall" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/procmon" "github.com/evilsocket/opensnitch/daemon/procmon/monitor" "github.com/evilsocket/opensnitch/daemon/rule" "github.com/evilsocket/opensnitch/daemon/ui/config" "github.com/evilsocket/opensnitch/daemon/ui/protocol" "golang.org/x/net/context" ) var stopMonitoringProcess = make(chan int) // NewReply constructs a new protocol notification reply func NewReply(rID uint64, replyCode protocol.NotificationReplyCode, data string) *protocol.NotificationReply { return &protocol.NotificationReply{ Id: rID, Code: replyCode, Data: data, } } func (c *Client) getClientConfig() *protocol.ClientConfig { raw, _ := ioutil.ReadFile(configFile) nodeName := core.GetHostname() nodeVersion := core.GetKernelVersion() var ts time.Time rulesTotal := len(c.rules.GetAll()) ruleList := make([]*protocol.Rule, rulesTotal) idx := 0 for _, r := range c.rules.GetAll() { ruleList[idx] = r.Serialize() idx++ } sysfw, err := firewall.Serialize() if err != nil { log.Warning("firewall.Serialize() error: %s", err) } return &protocol.ClientConfig{ Id: uint64(ts.UnixNano()), Name: nodeName, Version: nodeVersion, IsFirewallRunning: firewall.IsRunning(), Config: strings.Replace(string(raw), "\n", "", -1), LogLevel: uint32(log.MinLevel), Rules: ruleList, SystemFirewall: sysfw, } } func (c *Client) monitorProcessDetails(pid int, stream protocol.UI_NotificationsClient, notification *protocol.Notification) { p := procmon.NewProcess(pid, "") p.GetInfo() ticker := time.NewTicker(2 * time.Second) for { select { case _pid := <-stopMonitoringProcess: if _pid != pid { continue } goto Exit case <-ticker.C: if err := p.GetExtraInfo(); err != nil { c.sendNotificationReply(stream, notification.Id, notification.Data, err) goto Exit } pJSON, err := json.Marshal(p) notification.Data = string(pJSON) if errs := c.sendNotificationReply(stream, notification.Id, notification.Data, err); errs != nil { goto Exit } } } Exit: ticker.Stop() } func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { log.Info("[notification] Reloading configuration") // Parse received configuration first, to get the new proc monitor method. newConf, err := config.Parse(notification.Data) if err != nil { log.Warning("[notification] error parsing received config: %v", notification.Data) c.sendNotificationReply(stream, notification.Id, "", err) return } if c.GetFirewallType() != newConf.Firewall { firewall.ChangeFw(newConf.Firewall) } if err := monitor.ReconfigureMonitorMethod( newConf.ProcMonitorMethod, clientConfig.Ebpf.ModulesPath, ); err != nil { c.sendNotificationReply(stream, notification.Id, "", err.Msg) return } // this save operation triggers a re-loadConfiguration() err = config.Save(configFile, notification.Data) if err != nil { log.Warning("[notification] CHANGE_CONFIG not applied %s", err) } c.sendNotificationReply(stream, notification.Id, "", err) } func (c *Client) handleActionEnableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { var err error for _, rul := range notification.Rules { log.Info("[notification] enable rule: %s", rul.Name) // protocol.Rule(protobuf) != rule.Rule(json) r, _ := rule.Deserialize(rul) r.Enabled = true // save to disk only if the duration is rule.Always err = c.rules.Replace(r, r.Duration == rule.Always) } c.sendNotificationReply(stream, notification.Id, "", err) } func (c *Client) handleActionDisableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { var err error for _, rul := range notification.Rules { log.Info("[notification] disable rule: %s", rul) r, _ := rule.Deserialize(rul) r.Enabled = false err = c.rules.Replace(r, r.Duration == rule.Always) } c.sendNotificationReply(stream, notification.Id, "", err) } func (c *Client) handleActionChangeRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { var rErr error for _, rul := range notification.Rules { r, err := rule.Deserialize(rul) if r == nil { rErr = fmt.Errorf("Invalid rule, %s", err) continue } log.Info("[notification] change rule: %s %d", r, notification.Id) if err := c.rules.Replace(r, r.Duration == rule.Always); err != nil { log.Warning("[notification] Error changing rule: %s %s", err, r) rErr = err } } c.sendNotificationReply(stream, notification.Id, "", rErr) } func (c *Client) handleActionDeleteRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { var err error for _, rul := range notification.Rules { log.Info("[notification] delete rule: %s %d", rul.Name, notification.Id) err = c.rules.Delete(rul.Name) if err != nil { log.Error("[notification] Error deleting rule: %s %s", err, rul) } } c.sendNotificationReply(stream, notification.Id, "", err) } func (c *Client) handleActionMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { pid, err := strconv.Atoi(notification.Data) if err != nil { log.Error("parsing PID to monitor: %d, err: %s", pid, err) return } if !core.Exists(fmt.Sprint("/proc/", pid)) { c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("The process is no longer running")) return } go c.monitorProcessDetails(pid, stream, notification) } func (c *Client) handleActionStopMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { pid, err := strconv.Atoi(notification.Data) if err != nil { log.Error("parsing PID to stop monitor: %d, err: %s", pid, err) c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error stopping monitor: %s", notification.Data)) return } stopMonitoringProcess <- pid c.sendNotificationReply(stream, notification.Id, "", nil) } func (c *Client) handleActionReloadFw(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { log.Info("[notification] reloading firewall") sysfw, err := firewall.Deserialize(notification.SysFirewall) if err != nil { log.Warning("firewall.Deserialize() error: %s", err) c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error reloading firewall, invalid rules")) return } if err := firewall.SaveConfiguration(sysfw); err != nil { c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error saving system firewall rules: %s", err)) return } // TODO: // - add new API endpoints to delete, add or change rules atomically. // - a global goroutine where errors can be sent to the server (GUI). go func(c *Client) { var errors string for { select { case fwerr := <-firewall.ErrorsChan(): errors = fmt.Sprint(errors, fwerr, ",") if firewall.ErrChanEmpty() { goto ExitWithError } // FIXME: can this operation last longer than 2s? if there're more than.. 100...10000 rules? case <-time.After(2 * time.Second): log.Debug("[notification] reload firewall. timeout fired, no errors?") c.sendNotificationReply(stream, notification.Id, "", nil) goto Exit } } ExitWithError: c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("%s", errors)) Exit: }(c) } func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { switch { case notification.Type == protocol.Action_MONITOR_PROCESS: c.handleActionMonitorProcess(stream, notification) case notification.Type == protocol.Action_STOP_MONITOR_PROCESS: c.handleActionStopMonitorProcess(stream, notification) case notification.Type == protocol.Action_CHANGE_CONFIG: c.handleActionChangeConfig(stream, notification) case notification.Type == protocol.Action_ENABLE_INTERCEPTION: log.Info("[notification] starting interception") if err := firewall.EnableInterception(); err != nil { log.Warning("firewall.EnableInterception() error: %s", err) c.sendNotificationReply(stream, notification.Id, "", err) return } c.sendNotificationReply(stream, notification.Id, "", nil) case notification.Type == protocol.Action_DISABLE_INTERCEPTION: log.Info("[notification] stopping interception") if err := firewall.DisableInterception(); err != nil { log.Warning("firewall.DisableInterception() error: %s", err) c.sendNotificationReply(stream, notification.Id, "", err) return } c.sendNotificationReply(stream, notification.Id, "", nil) case notification.Type == protocol.Action_RELOAD_FW_RULES: c.handleActionReloadFw(stream, notification) // ENABLE_RULE just replaces the rule on disk case notification.Type == protocol.Action_ENABLE_RULE: c.handleActionEnableRule(stream, notification) case notification.Type == protocol.Action_DISABLE_RULE: c.handleActionDisableRule(stream, notification) case notification.Type == protocol.Action_DELETE_RULE: c.handleActionDeleteRule(stream, notification) // CHANGE_RULE can add() or replace) an existing rule. case notification.Type == protocol.Action_CHANGE_RULE: c.handleActionChangeRule(stream, notification) } } func (c *Client) sendNotificationReply(stream protocol.UI_NotificationsClient, nID uint64, data string, err error) error { reply := NewReply(nID, protocol.NotificationReplyCode_OK, data) if err != nil { reply.Code = protocol.NotificationReplyCode_ERROR reply.Data = fmt.Sprint(err) } if err := stream.Send(reply); err != nil { log.Error("Error replying to notification: %s %d", err, reply.Id) return err } return nil } // Subscribe opens a connection with the server (UI), to start // receiving notifications. // It firstly sends the daemon status and configuration. func (c *Client) Subscribe() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() clientCfg, err := c.client.Subscribe(ctx, c.getClientConfig()) if err != nil { log.Error("Subscribing to GUI %s", err) // When connecting to the GUI via TCP, sometimes the notifications channel is // not established, and the main channel is never closed. // We need to disconnect everything after a timeout and try it again. c.disconnect() return } if tempConf, err := config.Parse(clientCfg.Config); err == nil { c.Lock() clientConnectedRule.Action = rule.Action(tempConf.DefaultAction) c.Unlock() } c.listenForNotifications() } // Notifications is the channel where the daemon receives messages from the server. // It consists of 2 grpc streams (send/receive) that are never closed, // this way we can share messages in realtime. // If the GUI is closed, we'll receive an error reading from the channel. func (c *Client) listenForNotifications() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // open the stream channel streamReply := &protocol.NotificationReply{Id: 0, Code: protocol.NotificationReplyCode_OK} notisStream, err := c.client.Notifications(ctx) if err != nil { log.Error("establishing notifications channel %s", err) return } // send the first notification if err := notisStream.Send(streamReply); err != nil { log.Error("sending notification HELLO %s", err) return } log.Info("Start receiving notifications") for { select { case <-c.clientCtx.Done(): goto Exit default: noti, err := notisStream.Recv() if err == io.EOF { log.Warning("notification channel closed by the server") goto Exit } if err != nil { log.Error("getting notifications: %s %s", err, noti) goto Exit } c.handleNotification(notisStream, noti) } } Exit: notisStream.CloseSend() log.Info("Stop receiving notifications") c.disconnect() } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/protocol/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017410�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/protocol/.gitkeep��������������������������������������������������������0000664�0000000�0000000�00000000000�15003540030�0021027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/testdata/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017360�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/daemon/ui/testdata/default-config.json���������������������������������������������0000664�0000000�0000000�00000000726�15003540030�0023147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{"Server":{"Address":"unix:///run/user/1000/opensnitch/osui.sock","Authentication":{"Type":"","TLSOptions":{"CACert":"","ServerCert":"","ServerKey":"","ClientCert":"","ClientKey":"","SkipVerify":false,"ClientAuthType":""}},"LogFile":"","Loggers":null},"DefaultAction":"deny","DefaultDuration":"once","InterceptUnknown":true,"ProcMonitorMethod":"proc","LogLevel":null,"LogUTC":false,"LogMicro":false,"Firewall":"iptables","Stats":{"MaxEvents":0,"MaxStats":0,"Workers":0}}������������������������������������������opensnitch-1.6.9/daemon/ui/testdata/orig-default-config.json����������������������������������������0000664�0000000�0000000�00000000652�15003540030�0024103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "Server": { "Address":"unix:///tmp/osui.sock", "LogFile":"/var/log/opensnitchd.log" }, "DefaultAction": "allow", "DefaultDuration": "once", "InterceptUnknown": false, "ProcMonitorMethod": "ebpf", "LogLevel": 2, "LogUTC": true, "LogMicro": false, "Firewall": "nftables", "Stats": { "MaxEvents": 150, "MaxStats": 25, "Workers": 6 } } ��������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015632�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/Makefile�����������������������������������������������������������������0000664�0000000�0000000�00000003362�15003540030�0017276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# OpenSnitch - 2023 # # On Debian based distros we need the following 2 directories. # Otherwise, just use the kernel headers from the kernel sources. # KERNEL_DIR ?= /lib/modules/$(shell uname -r)/source KERNEL_HEADERS ?= /usr/src/linux-headers-$(shell uname -r)/ CLANG ?= clang LLC ?= llc LLVM_STRIP ?= llvm-strip -g ARCH ?= $(shell uname -m) # as in /usr/src/linux-headers-*/arch/ # TODO: extract correctly the archs, and add more if needed. ifeq ($(ARCH),x86_64) ARCH := x86 else ifeq ($(ARCH),i686) ARCH := x86 else ifeq ($(ARCH),armv7l) ARCH := arm else ifeq ($(ARCH),aarch64) ARCH := arm64 endif ifeq ($(ARCH),arm) # on previous archs, it fails with "SMP not supported on pre-ARMv6" EXTRA_FLAGS = "-D__LINUX_ARM_ARCH__=7" endif BIN := opensnitch.o opensnitch-procs.o opensnitch-dns.o CLANG_FLAGS = -I. \ -I$(KERNEL_HEADERS)/arch/$(ARCH)/include/generated/ \ -I$(KERNEL_HEADERS)/include \ -include $(KERNEL_DIR)/include/linux/kconfig.h \ -I$(KERNEL_DIR)/include \ -I$(KERNEL_DIR)/include/uapi \ -I$(KERNEL_DIR)/include/generated/uapi \ -I$(KERNEL_DIR)/arch/$(ARCH)/include \ -I$(KERNEL_DIR)/arch/$(ARCH)/include/generated \ -I$(KERNEL_DIR)/arch/$(ARCH)/include/uapi \ -I$(KERNEL_DIR)/arch/$(ARCH)/include/generated/uapi \ -I$(KERNEL_DIR)/tools/testing/selftests/bpf/ \ -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \ -D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \ $(EXTRA_FLAGS) \ -Wno-gnu-variable-sized-type-not-at-end \ -Wno-address-of-packed-member -Wno-tautological-compare \ -Wno-unknown-warning-option \ -g -O2 -emit-llvm all: $(BIN) %.o: %.c $(CLANG) $(CLANG_FLAGS) -c $< -o $@.partial $(LLC) -march=bpf -mcpu=generic -filetype=obj -o $@ $@.partial rm -f $@.partial clean: rm -f *.o *.partial ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/README�������������������������������������������������������������������0000664�0000000�0000000�00000004745�15003540030�0016524�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Compilation requires getting kernel sources for now. There's a helper script to automate this process: https://github.com/evilsocket/opensnitch/blob/master/utils/packaging/build_modules.sh The basic steps to compile the modules are: sudo apt install clang llvm libelf-dev libzip-dev flex bison libssl-dev bc rsync python3 cd opensnitch wget https://github.com/torvalds/linux/archive/v5.8.tar.gz tar -xf v5.8.tar.gz cp ebpf_prog/opensnitch*.c ebpf_prog/common* ebpf_prog/Makefile linux-5.8/samples/bpf/ cp -r ebpf_prog/bpf_headers/ linux-5.8/samples/bpf/ cd linux-5.8 && yes "" | make oldconfig && make prepare && make headers_install # (1 min) cd samples/bpf && make KERNEL_DIR=../../linux-5.8/ objdump -h opensnitch.o # you should see many sections, number 1 should be called kprobe/tcp_v4_connect llvm-strip -g opensnitch*.o # remove debug info sudo cp opensnitch*.o /usr/lib/opensnitchd/ebpf/ # or /etc/opensnitchd for < v1.6.x cd ../../../daemon Since v1.6.0, opensnitchd expects to find the opensnitch*.o modules under: /usr/local/lib/opensnitchd/ebpf/ /usr/lib/opensnitchd/ebpf/ /etc/opensnitchd/ # deprecated, only on < v1.5.x start opensnitchd with: opensnitchd -rules-path /etc/opensnitchd/rules -process-monitor-method ebpf --- ### Compiling for Fedora (and others rpm based systems) You need to install the kernel-devel, clang and llvm packages. Then: `cd ebpf_prog/ ; make KERNEL_DIR=/usr/src/kernels/$(uname -r)/` (or just pass the kernel version you want) ### Notes The kernel where you intend to run it must have some options activated: $ grep BPF /boot/config-$(uname -r) CONFIG_CGROUP_BPF=y CONFIG_BPF=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_EVENTS=y CONFIG_KPROBES=y CONFIG_KPROBE_EVENTS=y For the opensnitch-procs.o module to work, this option must be enabled: $ grep FTRACE_SYSCALLS /boot/config-$(uname -r) CONFIG_FTRACE_SYSCALLS=y (https://github.com/iovisor/bcc/blob/master/docs/kernel_config.md) Also, in some distributions debugfs is not mounted automatically. Since v1.6.0 we try to mount it automatically. If you're running a lower version so you'll need to mount it manually: $ sudo mount -t debugfs none /sys/kernel/debug In order to make it permanent add it to /etc/fstab: debugfs /sys/kernel/debug debugfs defaults 0 0 opensnitch-procs.o and opensnitch-dns.o are only compatible with kernels >= 5.5, bpf_probe_read_user*() were added on that kernel on: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#helpers ���������������������������opensnitch-1.6.9/ebpf_prog/arm-clang-asm-fix.patch��������������������������������������������������0000664�0000000�0000000�00000000601�15003540030�0022053�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- ../../arch/arm/include/asm/unified.h 2021-04-20 10:47:54.075834124 +0000 +++ ../../arch/arm/include/asm/unified-clang-fix.h 2021-04-20 10:47:38.943811970 +0000 @@ -11,7 +11,10 @@ #if defined(__ASSEMBLY__) .syntax unified #else -__asm__(".syntax unified"); +//__asm__(".syntax unified"); +#ifndef __clang__ + __asm__(".syntax unified"); +#endif #endif #ifdef CONFIG_CPU_V7M �������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/bpf_headers/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020074�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/bpf_headers/bpf_core_read.h����������������������������������������������0000664�0000000�0000000�00000046132�15003540030�0023025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_CORE_READ_H__ #define __BPF_CORE_READ_H__ /* * enum bpf_field_info_kind is passed as a second argument into * __builtin_preserve_field_info() built-in to get a specific aspect of * a field, captured as a first argument. __builtin_preserve_field_info(field, * info_kind) returns __u32 integer and produces BTF field relocation, which * is understood and processed by libbpf during BPF object loading. See * selftests/bpf for examples. */ enum bpf_field_info_kind { BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ BPF_FIELD_BYTE_SIZE = 1, BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ BPF_FIELD_SIGNED = 3, BPF_FIELD_LSHIFT_U64 = 4, BPF_FIELD_RSHIFT_U64 = 5, }; /* second argument to __builtin_btf_type_id() built-in */ enum bpf_type_id_kind { BPF_TYPE_ID_LOCAL = 0, /* BTF type ID in local program */ BPF_TYPE_ID_TARGET = 1, /* BTF type ID in target kernel */ }; /* second argument to __builtin_preserve_type_info() built-in */ enum bpf_type_info_kind { BPF_TYPE_EXISTS = 0, /* type existence in target kernel */ BPF_TYPE_SIZE = 1, /* type size in target kernel */ BPF_TYPE_MATCHES = 2, /* type match in target kernel */ }; /* second argument to __builtin_preserve_enum_value() built-in */ enum bpf_enum_value_kind { BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */ BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */ }; #define __CORE_RELO(src, field, info) \ __builtin_preserve_field_info((src)->field, BPF_FIELD_##info) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst, \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #else /* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so * for big-endian we need to adjust destination pointer accordingly, based on * field byte size */ #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #endif /* * Extract bitfield, identified by s->field, and return its value as u64. * All this is done in relocatable manner, so bitfield changes such as * signedness, bit size, offset changes, this will be handled automatically. * This version of macro is using bpf_probe_read_kernel() to read underlying * integer storage. Macro functions as an expression and its return type is * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error. */ #define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \ unsigned long long val = 0; \ \ __CORE_BITFIELD_PROBE_READ(&val, s, field); \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) /* * Extract bitfield, identified by s->field, and return its value as u64. * This version of macro is using direct memory reads and should be used from * BPF program types that support such functionality (e.g., typed raw * tracepoints). */ #define BPF_CORE_READ_BITFIELD(s, field) ({ \ const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \ unsigned long long val; \ \ /* This is a so-called barrier_var() operation that makes specified \ * variable "a black box" for optimizing compiler. \ * It forces compiler to perform BYTE_OFFSET relocation on p and use \ * its calculated value in the switch below, instead of applying \ * the same relocation 4 times for each individual memory load. \ */ \ asm volatile("" : "=r"(p) : "0"(p)); \ \ switch (__CORE_RELO(s, field, BYTE_SIZE)) { \ case 1: val = *(const unsigned char *)p; break; \ case 2: val = *(const unsigned short *)p; break; \ case 4: val = *(const unsigned int *)p; break; \ case 8: val = *(const unsigned long long *)p; break; \ } \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) #define ___bpf_field_ref1(field) (field) #define ___bpf_field_ref2(type, field) (((typeof(type) *)0)->field) #define ___bpf_field_ref(args...) \ ___bpf_apply(___bpf_field_ref, ___bpf_narg(args))(args) /* * Convenience macro to check that field actually exists in target kernel's. * Returns: * 1, if matching field is present in target kernel; * 0, if no matching field found. * * Supports two forms: * - field reference through variable access: * bpf_core_field_exists(p->my_field); * - field reference through type and field names: * bpf_core_field_exists(struct my_type, my_field). */ #define bpf_core_field_exists(field...) \ __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_EXISTS) /* * Convenience macro to get the byte size of a field. Works for integers, * struct/unions, pointers, arrays, and enums. * * Supports two forms: * - field reference through variable access: * bpf_core_field_size(p->my_field); * - field reference through type and field names: * bpf_core_field_size(struct my_type, my_field). */ #define bpf_core_field_size(field...) \ __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_SIZE) /* * Convenience macro to get field's byte offset. * * Supports two forms: * - field reference through variable access: * bpf_core_field_offset(p->my_field); * - field reference through type and field names: * bpf_core_field_offset(struct my_type, my_field). */ #define bpf_core_field_offset(field...) \ __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_OFFSET) /* * Convenience macro to get BTF type ID of a specified type, using a local BTF * information. Return 32-bit unsigned integer with type ID from program's own * BTF. Always succeeds. */ #define bpf_core_type_id_local(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL) /* * Convenience macro to get BTF type ID of a target kernel's type that matches * specified local type. * Returns: * - valid 32-bit unsigned type ID in kernel BTF; * - 0, if no matching type was found in a target kernel BTF. */ #define bpf_core_type_id_kernel(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET) /* * Convenience macro to check that provided named type * (struct/union/enum/typedef) exists in a target kernel. * Returns: * 1, if such type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_exists(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS) /* * Convenience macro to check that provided named type * (struct/union/enum/typedef) "matches" that in a target kernel. * Returns: * 1, if the type matches in the target kernel's BTF; * 0, if the type does not match any in the target kernel */ #define bpf_core_type_matches(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES) /* * Convenience macro to get the byte size of a provided named type * (struct/union/enum/typedef) in a target kernel. * Returns: * >= 0 size (in bytes), if type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_size(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE) /* * Convenience macro to check that provided enumerator value is defined in * a target kernel. * Returns: * 1, if specified enum type and its enumerator value are present in target * kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value_exists(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS) /* * Convenience macro to get the integer value of an enumerator value in * a target kernel. * Returns: * 64-bit value, if specified enum type and its enumerator value are * present in target kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE) /* * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures * offset relocation for source address using __builtin_preserve_access_index() * built-in, provided by Clang. * * __builtin_preserve_access_index() takes as an argument an expression of * taking an address of a field within struct/union. It makes compiler emit * a relocation, which records BTF type ID describing root struct/union and an * accessor string which describes exact embedded field that was used to take * an address. See detailed description of this relocation format and * semantics in comments to struct bpf_field_reloc in libbpf_internal.h. * * This relocation allows libbpf to adjust BPF instruction to use correct * actual field offset, based on target kernel BTF type that matches original * (local) BTF, used to record relocation. */ #define bpf_core_read(dst, sz, src) \ bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user(dst, sz, src) \ bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str() * additionally emitting BPF CO-RE field relocation for specified source * argument. */ #define bpf_core_read_str(dst, sz, src) \ bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user_str(dst, sz, src) \ bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) #define ___concat(a, b) a ## b #define ___apply(fn, n) ___concat(fn, n) #define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N /* * return number of provided arguments; used for switch-based variadic macro * definitions (see ___last, ___arrow, etc below) */ #define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) /* * return 0 if no arguments are passed, N - otherwise; used for * recursively-defined macros to specify termination (0) case, and generic * (N) case (e.g., ___read_ptrs, ___core_read) */ #define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0) #define ___last1(x) x #define ___last2(a, x) x #define ___last3(a, b, x) x #define ___last4(a, b, c, x) x #define ___last5(a, b, c, d, x) x #define ___last6(a, b, c, d, e, x) x #define ___last7(a, b, c, d, e, f, x) x #define ___last8(a, b, c, d, e, f, g, x) x #define ___last9(a, b, c, d, e, f, g, h, x) x #define ___last10(a, b, c, d, e, f, g, h, i, x) x #define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___nolast2(a, _) a #define ___nolast3(a, b, _) a, b #define ___nolast4(a, b, c, _) a, b, c #define ___nolast5(a, b, c, d, _) a, b, c, d #define ___nolast6(a, b, c, d, e, _) a, b, c, d, e #define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f #define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g #define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h #define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i #define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___arrow1(a) a #define ___arrow2(a, b) a->b #define ___arrow3(a, b, c) a->b->c #define ___arrow4(a, b, c, d) a->b->c->d #define ___arrow5(a, b, c, d, e) a->b->c->d->e #define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f #define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g #define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h #define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i #define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j #define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___type(...) typeof(___arrow(__VA_ARGS__)) #define ___read(read_fn, dst, src_type, src, accessor) \ read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) /* "recursively" read a sequence of inner pointers using local __t var */ #define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a); #define ___rd_last(fn, ...) \ ___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); #define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__) #define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___read_ptrs(fn, src, ...) \ ___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__) #define ___core_read0(fn, fn_ptr, dst, src, a) \ ___read(fn, dst, ___type(src), src, a); #define ___core_readN(fn, fn_ptr, dst, src, ...) \ ___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__)) \ ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \ ___last(__VA_ARGS__)); #define ___core_read(fn, fn_ptr, dst, src, a, ...) \ ___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst, \ src, a, ##__VA_ARGS__) /* * BPF_CORE_READ_INTO() is a more performance-conscious variant of * BPF_CORE_READ(), in which final field is read into user-provided storage. * See BPF_CORE_READ() below for more details on general usage. */ #define BPF_CORE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_INTO() */ #define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read, bpf_probe_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_USER_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as * BPF_CORE_READ() for intermediate pointers, but then executes (and returns * corresponding error code) bpf_core_read_str() for final string read. */ #define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_str, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user_str, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */ #define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_str, bpf_probe_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user_str, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially * when there are few pointer chasing steps. * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like: * int x = s->a.b.c->d.e->f->g; * can be succinctly achieved using BPF_CORE_READ as: * int x = BPF_CORE_READ(s, a.b.c, d.e, f, g); * * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF * CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically * equivalent to: * 1. const void *__t = s->a.b.c; * 2. __t = __t->d.e; * 3. __t = __t->f; * 4. return __t->g; * * Equivalence is logical, because there is a heavy type casting/preservation * involved, as well as all the reads are happening through * bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to * emit CO-RE relocations. * * N.B. Only up to 9 "field accessors" are supported, which should be more * than enough for any practical purpose. */ #define BPF_CORE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Variant of BPF_CORE_READ() for reading from user-space memory. * * NOTE: all the source types involved are still *kernel types* and need to * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will * fail. Custom user types are not relocatable with CO-RE. * The typical situation in which BPF_CORE_READ_USER() might be used is to * read kernel UAPI types from the user-space memory passed in as a syscall * input argument. */ #define BPF_CORE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* Non-CO-RE variant of BPF_CORE_READ() */ #define BPF_PROBE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/bpf_headers/bpf_helper_defs.h��������������������������������������������0000664�0000000�0000000�00000504244�15003540030�0023365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This is auto-generated file. See bpf_doc.py for details. */ /* Forward declarations of BPF structs */ struct bpf_fib_lookup; struct bpf_sk_lookup; struct bpf_perf_event_data; struct bpf_perf_event_value; struct bpf_pidns_info; struct bpf_redir_neigh; struct bpf_sock; struct bpf_sock_addr; struct bpf_sock_ops; struct bpf_sock_tuple; struct bpf_spin_lock; struct bpf_sysctl; struct bpf_tcp_sock; struct bpf_tunnel_key; struct bpf_xfrm_state; struct linux_binprm; struct pt_regs; struct sk_reuseport_md; struct sockaddr; struct tcphdr; struct seq_file; struct tcp6_sock; struct tcp_sock; struct tcp_timewait_sock; struct tcp_request_sock; struct udp6_sock; struct unix_sock; struct task_struct; struct __sk_buff; struct sk_msg_md; struct xdp_md; struct path; struct btf_ptr; struct inode; struct socket; struct file; struct bpf_timer; struct mptcp_sock; struct bpf_dynptr; struct iphdr; struct ipv6hdr; /* * bpf_map_lookup_elem * * Perform a lookup in *map* for an entry associated to *key*. * * Returns * Map value associated to *key*, or **NULL** if no entry was * found. */ static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; /* * bpf_map_update_elem * * Add or update the value of the entry associated to *key* in * *map* with *value*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * Flag value **BPF_NOEXIST** cannot be used for maps of types * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all * elements always exist), the helper would return an error. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; /* * bpf_map_delete_elem * * Delete entry with *key* from *map*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3; /* * bpf_probe_read * * For tracing programs, safely attempt to read *size* bytes from * kernel space address *unsafe_ptr* and store the data in *dst*. * * Generally, use **bpf_probe_read_user**\ () or * **bpf_probe_read_kernel**\ () instead. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4; /* * bpf_ktime_get_ns * * Return the time elapsed since system boot, in nanoseconds. * Does not include time the system was suspended. * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) * * Returns * Current *ktime*. */ static __u64 (*bpf_ktime_get_ns)(void) = (void *) 5; /* * bpf_trace_printk * * This helper is a "printk()-like" facility for debugging. It * prints a message defined by format *fmt* (of size *fmt_size*) * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if * available. It can take up to three additional **u64** * arguments (as an eBPF helpers, the total number of arguments is * limited to five). * * Each time the helper is called, it appends a line to the trace. * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this. * The format of the trace is customizable, and the exact output * one will get depends on the options set in * *\/sys/kernel/debug/tracing/trace_options* (see also the * *README* file under the same directory). However, it usually * defaults to something like: * * :: * * telnet-470 [001] .N.. 419421.045894: 0x00000001: <formatted msg> * * In the above: * * * ``telnet`` is the name of the current task. * * ``470`` is the PID of the current task. * * ``001`` is the CPU number on which the task is * running. * * In ``.N..``, each character refers to a set of * options (whether irqs are enabled, scheduling * options, whether hard/softirqs are running, level of * preempt_disabled respectively). **N** means that * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** * are set. * * ``419421.045894`` is a timestamp. * * ``0x00000001`` is a fake value used by BPF for the * instruction pointer register. * * ``<formatted msg>`` is the message formatted with * *fmt*. * * The conversion specifiers supported by *fmt* are similar, but * more limited than for printk(). They are **%d**, **%i**, * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size * of field, padding with zeroes, etc.) is available, and the * helper will return **-EINVAL** (but print nothing) if it * encounters an unknown specifier. * * Also, note that **bpf_trace_printk**\ () is slow, and should * only be used for debugging purposes. For this reason, a notice * block (spanning several lines) is printed to kernel logs and * states that the helper should not be used "for production use" * the first time this helper is used (or more precisely, when * **trace_printk**\ () buffers are allocated). For passing values * to user space, perf events should be preferred. * * Returns * The number of bytes written to the buffer, or a negative error * in case of failure. */ static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6; /* * bpf_get_prandom_u32 * * Get a pseudo-random number. * * From a security point of view, this helper uses its own * pseudo-random internal state, and cannot be used to infer the * seed of other random functions in the kernel. However, it is * essential to note that the generator used by the helper is not * cryptographically secure. * * Returns * A random 32-bit unsigned value. */ static __u32 (*bpf_get_prandom_u32)(void) = (void *) 7; /* * bpf_get_smp_processor_id * * Get the SMP (symmetric multiprocessing) processor id. Note that * all programs run with migration disabled, which means that the * SMP processor id is stable during all the execution of the * program. * * Returns * The SMP id of the processor running the program. */ static __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8; /* * bpf_skb_store_bytes * * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. *flags* are a combination of * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the * checksum for the packet after storing the bytes) and * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ * **->swhash** and *skb*\ **->l4hash** to 0). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9; /* * bpf_l3_csum_replace * * Recompute the layer 3 (e.g. IP) checksum for the packet * associated to *skb*. Computation is incremental, so the helper * must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored in *size*. * Alternatively, it is possible to store the difference between * the previous and the new values of the header field in *to*, by * setting *from* and *size* to 0. For both methods, *offset* * indicates the location of the IP checksum within the packet. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10; /* * bpf_l4_csum_replace * * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the * packet associated to *skb*. Computation is incremental, so the * helper must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored on the lowest * four bits of *flags*. Alternatively, it is possible to store * the difference between the previous and the new values of the * header field in *to*, by setting *from* and the four lowest * bits of *flags* to 0. For both methods, *offset* indicates the * location of the IP checksum within the packet. In addition to * the size of the field, *flags* can be added (bitwise OR) actual * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and * for updates resulting in a null checksum the value is set to * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates * the checksum is to be computed against a pseudo-header. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11; /* * bpf_tail_call * * This special helper is used to trigger a "tail call", or in * other words, to jump into another eBPF program. The same stack * frame is used (but values on stack and in registers for the * caller are not accessible to the callee). This mechanism allows * for program chaining, either for raising the maximum number of * available eBPF instructions, or to execute given programs in * conditional blocks. For security reasons, there is an upper * limit to the number of successive tail calls that can be * performed. * * Upon call of this helper, the program attempts to jump into a * program referenced at index *index* in *prog_array_map*, a * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes * *ctx*, a pointer to the context. * * If the call succeeds, the kernel immediately runs the first * instruction of the new program. This is not a function call, * and it never returns to the previous program. If the call * fails, then the helper has no effect, and the caller continues * to run its subsequent instructions. A call can fail if the * destination program for the jump does not exist (i.e. *index* * is superior to the number of entries in *prog_array_map*), or * if the maximum number of tail calls has been reached for this * chain of programs. This limit is defined in the kernel by the * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), * which is currently set to 33. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12; /* * bpf_clone_redirect * * Clone and redirect the packet associated to *skb* to another * net device of index *ifindex*. Both ingress and egress * interfaces can be used for redirection. The **BPF_F_INGRESS** * value in *flags* is used to make the distinction (ingress path * is selected if the flag is present, egress path otherwise). * This is the only flag supported for now. * * In comparison with **bpf_redirect**\ () helper, * **bpf_clone_redirect**\ () has the associated cost of * duplicating the packet buffer, but this can be executed out of * the eBPF program. Conversely, **bpf_redirect**\ () is more * efficient, but it is handled through an action code where the * redirection happens only after the eBPF program has returned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13; /* * bpf_get_current_pid_tgid * * Get the current pid and tgid. * * Returns * A 64-bit integer containing the current tgid and pid, and * created as such: * *current_task*\ **->tgid << 32 \|** * *current_task*\ **->pid**. */ static __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14; /* * bpf_get_current_uid_gid * * Get the current uid and gid. * * Returns * A 64-bit integer containing the current GID and UID, and * created as such: *current_gid* **<< 32 \|** *current_uid*. */ static __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15; /* * bpf_get_current_comm * * Copy the **comm** attribute of the current task into *buf* of * *size_of_buf*. The **comm** attribute contains the name of * the executable (excluding the path) for the current task. The * *size_of_buf* must be strictly positive. On success, the * helper makes sure that the *buf* is NUL-terminated. On failure, * it is filled with zeroes. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16; /* * bpf_get_cgroup_classid * * Retrieve the classid for the current task, i.e. for the net_cls * cgroup to which *skb* belongs. * * This helper can be used on TC egress path, but not on ingress. * * The net_cls cgroup provides an interface to tag network packets * based on a user-provided identifier for all traffic coming from * the tasks belonging to the related cgroup. See also the related * kernel documentation, available from the Linux sources in file * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. * * The Linux kernel has two versions for cgroups: there are * cgroups v1 and cgroups v2. Both are available to users, who can * use a mixture of them, but note that the net_cls cgroup is for * cgroup v1 only. This makes it incompatible with BPF programs * run on cgroups, which is a cgroup-v2-only feature (a socket can * only hold data for one version of cgroups at a time). * * This helper is only available is the kernel was compiled with * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to * "**y**" or to "**m**". * * Returns * The classid, or 0 for the default unconfigured classid. */ static __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17; /* * bpf_skb_vlan_push * * Push a *vlan_tci* (VLAN tag control information) of protocol * *vlan_proto* to the packet associated to *skb*, then update * the checksum. Note that if *vlan_proto* is different from * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to * be **ETH_P_8021Q**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18; /* * bpf_skb_vlan_pop * * Pop a VLAN header from the packet associated to *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19; /* * bpf_skb_get_tunnel_key * * Get tunnel metadata. This helper takes a pointer *key* to an * empty **struct bpf_tunnel_key** of **size**, that will be * filled with tunnel metadata for the packet associated to *skb*. * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which * indicates that the tunnel is based on IPv6 protocol instead of * IPv4. * * The **struct bpf_tunnel_key** is an object that generalizes the * principal parameters used by various tunneling protocols into a * single struct. This way, it can be used to easily make a * decision based on the contents of the encapsulation header, * "summarized" in this struct. In particular, it holds the IP * address of the remote end (IPv4 or IPv6, depending on the case) * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, * this struct exposes the *key*\ **->tunnel_id**, which is * generally mapped to a VNI (Virtual Network Identifier), making * it programmable together with the **bpf_skb_set_tunnel_key**\ * () helper. * * Let's imagine that the following code is part of a program * attached to the TC ingress interface, on one end of a GRE * tunnel, and is supposed to filter out all messages coming from * remote ends with IPv4 address other than 10.0.0.1: * * :: * * int ret; * struct bpf_tunnel_key key = {}; * * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); * if (ret < 0) * return TC_ACT_SHOT; // drop packet * * if (key.remote_ipv4 != 0x0a000001) * return TC_ACT_SHOT; // drop packet * * return TC_ACT_OK; // accept packet * * This interface can also be used with all encapsulation devices * that can operate in "collect metadata" mode: instead of having * one network device per specific configuration, the "collect * metadata" mode only requires a single device where the * configuration can be extracted from this helper. * * This can be used together with various tunnels such as VXLan, * Geneve, GRE or IP in IP (IPIP). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20; /* * bpf_skb_set_tunnel_key * * Populate tunnel metadata for packet associated to *skb.* The * tunnel metadata is set to the contents of *key*, of *size*. The * *flags* can be set to a combination of the following values: * * **BPF_F_TUNINFO_IPV6** * Indicate that the tunnel is based on IPv6 protocol * instead of IPv4. * **BPF_F_ZERO_CSUM_TX** * For IPv4 packets, add a flag to tunnel metadata * indicating that checksum computation should be skipped * and checksum set to zeroes. * **BPF_F_DONT_FRAGMENT** * Add a flag to tunnel metadata indicating that the * packet should not be fragmented. * **BPF_F_SEQ_NUMBER** * Add a flag to tunnel metadata indicating that a * sequence number should be added to tunnel header before * sending the packet. This flag was added for GRE * encapsulation, but might be used with other protocols * as well in the future. * * Here is a typical usage on the transmit path: * * :: * * struct bpf_tunnel_key key; * populate key ... * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); * * See also the description of the **bpf_skb_get_tunnel_key**\ () * helper for additional information. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21; /* * bpf_perf_event_read * * Read the value of a perf event counter. This helper relies on a * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of * the perf event counter is selected when *map* is updated with * perf event file descriptors. The *map* is an array whose size * is the number of available CPUs, and each cell contains a value * relative to one CPU. The value to retrieve is indicated by * *flags*, that contains the index of the CPU to look up, masked * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * Note that before Linux 4.13, only hardware perf event can be * retrieved. * * Also, be aware that the newer helper * **bpf_perf_event_read_value**\ () is recommended over * **bpf_perf_event_read**\ () in general. The latter has some ABI * quirks where error and counter value are used as a return code * (which is wrong to do since ranges may overlap). This issue is * fixed with **bpf_perf_event_read_value**\ (), which at the same * time provides more features over the **bpf_perf_event_read**\ * () interface. Please refer to the description of * **bpf_perf_event_read_value**\ () for details. * * Returns * The value of the perf event counter read from the map, or a * negative error code in case of failure. */ static __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22; /* * bpf_redirect * * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_clone_redirect**\ * (), except that the packet is not cloned, which provides * increased performance. * * Except for XDP, both ingress and egress interfaces can be used * for redirection. The **BPF_F_INGRESS** value in *flags* is used * to make the distinction (ingress path is selected if the flag * is present, egress path otherwise). Currently, XDP only * supports redirection to the egress interface, and accepts no * flag at all. * * The same effect can also be attained with the more generic * **bpf_redirect_map**\ (), which uses a BPF map to store the * redirect target instead of providing it directly to the helper. * * Returns * For XDP, the helper returns **XDP_REDIRECT** on success or * **XDP_ABORTED** on error. For other program types, the values * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on * error. */ static long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23; /* * bpf_get_route_realm * * Retrieve the realm or the route, that is to say the * **tclassid** field of the destination for the *skb*. The * identifier retrieved is a user-provided tag, similar to the * one used with the net_cls cgroup (see description for * **bpf_get_cgroup_classid**\ () helper), but here this tag is * held by a route (a destination entry), not by a task. * * Retrieving this identifier works with the clsact TC egress hook * (see also **tc-bpf(8)**), or alternatively on conventional * classful egress qdiscs, but not on TC ingress path. In case of * clsact TC egress hook, this has the advantage that, internally, * the destination entry has not been dropped yet in the transmit * path. Therefore, the destination entry does not need to be * artificially held via **netif_keep_dst**\ () for a classful * qdisc until the *skb* is freed. * * This helper is available only if the kernel was compiled with * **CONFIG_IP_ROUTE_CLASSID** configuration option. * * Returns * The realm of the route for the packet associated to *skb*, or 0 * if none was found. */ static __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24; /* * bpf_perf_event_output * * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * The context of the program *ctx* needs also be passed to the * helper. * * On user space, a program willing to read the values needs to * call **perf_event_open**\ () on the perf event (either for * one or for all CPUs) and to store the file descriptor into the * *map*. This must be done before the eBPF program can send data * into it. An example is available in file * *samples/bpf/trace_output_user.c* in the Linux kernel source * tree (the eBPF program counterpart is in * *samples/bpf/trace_output_kern.c*). * * **bpf_perf_event_output**\ () achieves better performance * than **bpf_trace_printk**\ () for sharing data with user * space, and is much better suitable for streaming data from eBPF * programs. * * Note that this helper is not restricted to tracing use cases * and can be used with programs attached to TC or XDP as well, * where it allows for passing data to user space listeners. Data * can be: * * * Only custom structs, * * Only the packet payload, or * * A combination of both. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25; /* * bpf_skb_load_bytes * * This helper was provided as an easy way to load data from a * packet. It can be used to load *len* bytes from *offset* from * the packet associated to *skb*, into the buffer pointed by * *to*. * * Since Linux 4.7, usage of this helper has mostly been replaced * by "direct packet access", enabling packet data to be * manipulated with *skb*\ **->data** and *skb*\ **->data_end** * pointing respectively to the first byte of packet data and to * the byte after the last byte of packet data. However, it * remains useful if one wishes to read large quantities of data * at once from a packet into the eBPF stack. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26; /* * bpf_get_stackid * * Walk a user or a kernel stack and return its id. To achieve * this, the helper needs *ctx*, which is a pointer to the context * on which the tracing program is executed, and a pointer to a * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * a combination of the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_FAST_STACK_CMP** * Compare stacks by hash only. * **BPF_F_REUSE_STACKID** * If two different stacks hash into the same *stackid*, * discard the old one. * * The stack id retrieved is a 32 bit long integer handle which * can be further combined with other data (including other stack * ids) and used as a key into maps. This can be useful for * generating a variety of graphs (such as flame graphs or off-cpu * graphs). * * For walking a stack, this helper is an improvement over * **bpf_probe_read**\ (), which can be used with unrolled loops * but is not efficient and consumes a lot of eBPF instructions. * Instead, **bpf_get_stackid**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack=<new value> * * Returns * The positive or null stack id on success, or a negative error * in case of failure. */ static long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27; /* * bpf_csum_diff * * Compute a checksum difference, from the raw buffer pointed by * *from*, of length *from_size* (that must be a multiple of 4), * towards the raw buffer pointed by *to*, of size *to_size* * (same remark). An optional *seed* can be added to the value * (this can be cascaded, the seed may come from a previous call * to the helper). * * This is flexible enough to be used in several ways: * * * With *from_size* == 0, *to_size* > 0 and *seed* set to * checksum, it can be used when pushing new data. * * With *from_size* > 0, *to_size* == 0 and *seed* set to * checksum, it can be used when removing data from a packet. * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it * can be used to compute a diff. Note that *from_size* and * *to_size* do not need to be equal. * * This helper can be used in combination with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to * which one can feed in the difference computed with * **bpf_csum_diff**\ (). * * Returns * The checksum result, or a negative error code in case of * failure. */ static __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28; /* * bpf_skb_get_tunnel_opt * * Retrieve tunnel options metadata for the packet associated to * *skb*, and store the raw tunnel option data to the buffer *opt* * of *size*. * * This helper can be used with encapsulation devices that can * operate in "collect metadata" mode (please refer to the related * note in the description of **bpf_skb_get_tunnel_key**\ () for * more details). A particular example where this can be used is * in combination with the Geneve encapsulation protocol, where it * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) * and retrieving arbitrary TLVs (Type-Length-Value headers) from * the eBPF program. This allows for full customization of these * headers. * * Returns * The size of the option data retrieved. */ static long (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29; /* * bpf_skb_set_tunnel_opt * * Set tunnel options metadata for the packet associated to *skb* * to the option data contained in the raw buffer *opt* of *size*. * * See also the description of the **bpf_skb_get_tunnel_opt**\ () * helper for additional information. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30; /* * bpf_skb_change_proto * * Change the protocol of the *skb* to *proto*. Currently * supported are transition from IPv4 to IPv6, and from IPv6 to * IPv4. The helper takes care of the groundwork for the * transition, including resizing the socket buffer. The eBPF * program is expected to fill the new headers, if any, via * **skb_store_bytes**\ () and to recompute the checksums with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ * (). The main case for this helper is to perform NAT64 * operations out of an eBPF program. * * Internally, the GSO type is marked as dodgy so that headers are * checked and segments are recalculated by the GSO/GRO engine. * The size for GSO target is adapted as well. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31; /* * bpf_skb_change_type * * Change the packet type for the packet associated to *skb*. This * comes down to setting *skb*\ **->pkt_type** to *type*, except * the eBPF program does not have a write access to *skb*\ * **->pkt_type** beside this helper. Using a helper here allows * for graceful handling of errors. * * The major use case is to change incoming *skb*s to * **PACKET_HOST** in a programmatic way instead of having to * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for * example. * * Note that *type* only allows certain values. At this time, they * are: * * **PACKET_HOST** * Packet is for us. * **PACKET_BROADCAST** * Send packet to all. * **PACKET_MULTICAST** * Send packet to group. * **PACKET_OTHERHOST** * Send packet to someone else. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32; /* * bpf_skb_under_cgroup * * Check whether *skb* is a descendant of the cgroup2 held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * * Returns * The return value depends on the result of the test, and can be: * * * 0, if the *skb* failed the cgroup2 descendant test. * * 1, if the *skb* succeeded the cgroup2 descendant test. * * A negative error code, if an error occurred. */ static long (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33; /* * bpf_get_hash_recalc * * Retrieve the hash of the packet, *skb*\ **->hash**. If it is * not set, in particular if the hash was cleared due to mangling, * recompute this hash. Later accesses to the hash can be done * directly with *skb*\ **->hash**. * * Calling **bpf_set_hash_invalid**\ (), changing a packet * prototype with **bpf_skb_change_proto**\ (), or calling * **bpf_skb_store_bytes**\ () with the * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear * the hash and to trigger a new computation for the next call to * **bpf_get_hash_recalc**\ (). * * Returns * The 32-bit hash. */ static __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34; /* * bpf_get_current_task * * Get the current task. * * Returns * A pointer to the current task struct. */ static __u64 (*bpf_get_current_task)(void) = (void *) 35; /* * bpf_probe_write_user * * Attempt in a safe way to write *len* bytes from the buffer * *src* to *dst* in memory. It only works for threads that are in * user context, and *dst* must be a valid user space address. * * This helper should not be used to implement any kind of * security mechanism because of TOC-TOU attacks, but rather to * debug, divert, and manipulate execution of semi-cooperative * processes. * * Keep in mind that this feature is meant for experiments, and it * has a risk of crashing the system and running programs. * Therefore, when an eBPF program using this helper is attached, * a warning including PID and process name is printed to kernel * logs. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36; /* * bpf_current_task_under_cgroup * * Check whether the probe is being run is the context of a given * subset of the cgroup2 hierarchy. The cgroup2 to test is held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * * Returns * The return value depends on the result of the test, and can be: * * * 1, if current task belongs to the cgroup2. * * 0, if current task does not belong to the cgroup2. * * A negative error code, if an error occurred. */ static long (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37; /* * bpf_skb_change_tail * * Resize (trim or grow) the packet associated to *skb* to the * new *len*. The *flags* are reserved for future usage, and must * be left at zero. * * The basic idea is that the helper performs the needed work to * change the size of the packet, then the eBPF program rewrites * the rest via helpers like **bpf_skb_store_bytes**\ (), * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () * and others. This helper is a slow path utility intended for * replies with control messages. And because it is targeted for * slow path, the helper itself can afford to be slow: it * implicitly linearizes, unclones and drops offloads from the * *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38; /* * bpf_skb_pull_data * * Pull in non-linear data in case the *skb* is non-linear and not * all of *len* are part of the linear section. Make *len* bytes * from *skb* readable and writable. If a zero value is passed for * *len*, then all bytes in the linear part of *skb* will be made * readable and writable. * * This helper is only needed for reading and writing with direct * packet access. * * For direct packet access, testing that offsets to access * are within packet boundaries (test on *skb*\ **->data_end**) is * susceptible to fail if offsets are invalid, or if the requested * data is in non-linear parts of the *skb*. On failure the * program can just bail out, or in the case of a non-linear * buffer, use a helper to make the data available. The * **bpf_skb_load_bytes**\ () helper is a first solution to access * the data. Another one consists in using **bpf_skb_pull_data** * to pull in once the non-linear parts, then retesting and * eventually access the data. * * At the same time, this also makes sure the *skb* is uncloned, * which is a necessary condition for direct write. As this needs * to be an invariant for the write part only, the verifier * detects writes and adds a prologue that is calling * **bpf_skb_pull_data()** to effectively unclone the *skb* from * the very beginning in case it is indeed cloned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39; /* * bpf_csum_update * * Add the checksum *csum* into *skb*\ **->csum** in case the * driver has supplied a checksum for the entire packet into that * field. Return an error otherwise. This helper is intended to be * used in combination with **bpf_csum_diff**\ (), in particular * when the checksum needs to be updated after data has been * written into the packet through direct packet access. * * Returns * The checksum on success, or a negative error code in case of * failure. */ static __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40; /* * bpf_set_hash_invalid * * Invalidate the current *skb*\ **->hash**. It can be used after * mangling on headers through direct packet access, in order to * indicate that the hash is outdated and to trigger a * recalculation the next time the kernel tries to access this * hash or when the **bpf_get_hash_recalc**\ () helper is called. * * Returns * void. */ static void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41; /* * bpf_get_numa_node_id * * Return the id of the current NUMA node. The primary use case * for this helper is the selection of sockets for the local NUMA * node, when the program is attached to sockets using the * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), * but the helper is also available to other eBPF program types, * similarly to **bpf_get_smp_processor_id**\ (). * * Returns * The id of current NUMA node. */ static long (*bpf_get_numa_node_id)(void) = (void *) 42; /* * bpf_skb_change_head * * Grows headroom of packet associated to *skb* and adjusts the * offset of the MAC header accordingly, adding *len* bytes of * space. It automatically extends and reallocates memory as * required. * * This helper can be used on a layer 3 *skb* to push a MAC header * for redirection into a layer 2 device. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43; /* * bpf_xdp_adjust_head * * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that * it is possible to use a negative value for *delta*. This helper * can be used to prepare the packet for pushing or popping * headers. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44; /* * bpf_probe_read_str * * Copy a NUL terminated string from an unsafe kernel address * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for * more details. * * Generally, use **bpf_probe_read_user_str**\ () or * **bpf_probe_read_kernel_str**\ () instead. * * Returns * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. */ static long (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45; /* * bpf_get_socket_cookie * * If the **struct sk_buff** pointed by *skb* has a known socket, * retrieve the cookie (generated by the kernel) of this socket. * If no cookie has been set yet, generate a new cookie. Once * generated, the socket cookie remains stable for the life of the * socket. This helper can be useful for monitoring per socket * networking traffic statistics as it provides a global socket * identifier that can be assumed unique. * * Returns * A 8-byte long unique number on success, or 0 if the socket * field is missing inside *skb*. */ static __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46; /* * bpf_get_socket_uid * * Get the owner UID of the socked associated to *skb*. * * Returns * The owner UID of the socket associated to *skb*. If the socket * is **NULL**, or if it is not a full socket (i.e. if it is a * time-wait or a request socket instead), **overflowuid** value * is returned (note that **overflowuid** might also be the actual * UID value for the socket). */ static __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47; /* * bpf_set_hash * * Set the full hash for *skb* (set the field *skb*\ **->hash**) * to value *hash*. * * Returns * 0 */ static long (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48; /* * bpf_setsockopt * * Emulate a call to **setsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **setsockopt(2)** for more information. * The option value of length *optlen* is pointed by *optval*. * * *bpf_socket* should be one of the following: * * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** * and **BPF_CGROUP_INET6_CONNECT**. * * This helper actually implements a subset of **setsockopt()**. * It supports the following *level*\ s: * * * **SOL_SOCKET**, which supports the following *optname*\ s: * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. * * **IPPROTO_TCP**, which supports the following *optname*\ s: * **TCP_CONGESTION**, **TCP_BPF_IW**, * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_setsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49; /* * bpf_skb_adjust_room * * Grow or shrink the room for data in the packet associated to * *skb* by *len_diff*, and according to the selected *mode*. * * By default, the helper will reset any offloaded checksum * indicator of the skb to CHECKSUM_NONE. This can be avoided * by the following flag: * * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded * checksum data of the skb to CHECKSUM_NONE. * * There are two supported modes at this time: * * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer * (room space is added or removed below the layer 2 header). * * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer * (room space is added or removed below the layer 3 header). * * The following flags are supported at this time: * * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. * Adjusting mss in this way is not allowed for datagrams. * * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: * Any new space is reserved to hold a tunnel header. * Configure skb offsets and other fields accordingly. * * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: * Use with ENCAP_L3 flags to further specify the tunnel type. * * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): * Use with ENCAP_L3/L4 flags to further specify the tunnel * type; *len* is the length of the inner MAC header. * * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**: * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the * L2 type as Ethernet. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50; /* * bpf_redirect_map * * Redirect the packet to the endpoint referenced by *map* at * index *key*. Depending on its type, this *map* can contain * references to net devices (for forwarding packets through other * ports), or to CPUs (for redirecting XDP frames to another CPU; * but this is only implemented for native XDP (with driver * support) as of this writing). * * The lower two bits of *flags* are used as the return code if * the map lookup fails. This is so that the return value can be * one of the XDP program return codes up to **XDP_TX**, as chosen * by the caller. The higher bits of *flags* can be set to * BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below. * * With BPF_F_BROADCAST the packet will be broadcasted to all the * interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress * interface will be excluded when do broadcasting. * * See also **bpf_redirect**\ (), which only supports redirecting * to an ifindex, but doesn't require a map to do so. * * Returns * **XDP_REDIRECT** on success, or the value of the two lower bits * of the *flags* argument on error. */ static long (*bpf_redirect_map)(void *map, __u32 key, __u64 flags) = (void *) 51; /* * bpf_sk_redirect_map * * Redirect the packet to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52; /* * bpf_sock_map_update * * Add an entry to, or update a *map* referencing sockets. The * *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53; /* * bpf_xdp_adjust_meta * * Adjust the address pointed by *xdp_md*\ **->data_meta** by * *delta* (which can be positive or negative). Note that this * operation modifies the address stored in *xdp_md*\ **->data**, * so the latter must be loaded only after the helper has been * called. * * The use of *xdp_md*\ **->data_meta** is optional and programs * are not required to use it. The rationale is that when the * packet is processed with XDP (e.g. as DoS filter), it is * possible to push further meta data along with it before passing * to the stack, and to give the guarantee that an ingress eBPF * program attached as a TC classifier on the same device can pick * this up for further post-processing. Since TC works with socket * buffers, it remains possible to set from XDP the **mark** or * **priority** pointers, or other pointers for the socket buffer. * Having this scratch space generic and programmable allows for * more flexibility as the user is free to store whatever meta * data they need. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54; /* * bpf_perf_event_read_value * * Read the value of a perf event counter, and store it into *buf* * of size *buf_size*. This helper relies on a *map* of type * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event * counter is selected when *map* is updated with perf event file * descriptors. The *map* is an array whose size is the number of * available CPUs, and each cell contains a value relative to one * CPU. The value to retrieve is indicated by *flags*, that * contains the index of the CPU to look up, masked with * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * This helper behaves in a way close to * **bpf_perf_event_read**\ () helper, save that instead of * just returning the value observed, it fills the *buf* * structure. This allows for additional data to be retrieved: in * particular, the enabled and running times (in *buf*\ * **->enabled** and *buf*\ **->running**, respectively) are * copied. In general, **bpf_perf_event_read_value**\ () is * recommended over **bpf_perf_event_read**\ (), which has some * ABI issues and provides fewer functionalities. * * These values are interesting, because hardware PMU (Performance * Monitoring Unit) counters are limited resources. When there are * more PMU based perf events opened than available counters, * kernel will multiplex these events so each event gets certain * percentage (but not all) of the PMU time. In case that * multiplexing happens, the number of samples or counter value * will not reflect the case compared to when no multiplexing * occurs. This makes comparison between different runs difficult. * Typically, the counter value should be normalized before * comparing to other experiments. The usual normalization is done * as follows. * * :: * * normalized_counter = counter * t_enabled / t_running * * Where t_enabled is the time enabled for event and t_running is * the time running for event since last normalization. The * enabled and running times are accumulated since the perf event * open. To achieve scaling factor between two invocations of an * eBPF program, users can use CPU id as the key (which is * typical for perf array usage model) to remember the previous * value and do the calculation inside the eBPF program. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55; /* * bpf_perf_prog_read_value * * For en eBPF program attached to a perf event, retrieve the * value of the event counter associated to *ctx* and store it in * the structure pointed by *buf* and of size *buf_size*. Enabled * and running times are also stored in the structure (see * description of helper **bpf_perf_event_read_value**\ () for * more details). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56; /* * bpf_getsockopt * * Emulate a call to **getsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **getsockopt(2)** for more information. * The retrieved value is stored in the structure pointed by * *opval* and of length *optlen*. * * *bpf_socket* should be one of the following: * * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** * and **BPF_CGROUP_INET6_CONNECT**. * * This helper actually implements a subset of **getsockopt()**. * It supports the following *level*\ s: * * * **IPPROTO_TCP**, which supports *optname* * **TCP_CONGESTION**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_getsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57; /* * bpf_override_return * * Used for error injection, this helper uses kprobes to override * the return value of the probed function, and to set it to *rc*. * The first argument is the context *regs* on which the kprobe * works. * * This helper works by setting the PC (program counter) * to an override function which is run in place of the original * probed function. This means the probed function is not run at * all. The replacement function just returns with the required * value. * * This helper has security implications, and thus is subject to * restrictions. It is only available if the kernel was compiled * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration * option, and in this case it only works on functions tagged with * **ALLOW_ERROR_INJECTION** in the kernel code. * * Also, the helper is only available for the architectures having * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, * x86 architecture is the only one to support this feature. * * Returns * 0 */ static long (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58; /* * bpf_sock_ops_cb_flags_set * * Attempt to set the value of the **bpf_sock_ops_cb_flags** field * for the full TCP socket associated to *bpf_sock_ops* to * *argval*. * * The primary use of this field is to determine if there should * be calls to eBPF programs of type * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP * code. A program of the same type can change its value, per * connection and as necessary, when the connection is * established. This field is directly accessible for reading, but * this helper must be used for updates in order to return an * error if an eBPF program tries to set a callback that is not * supported in the current kernel. * * *argval* is a flag array which can combine these flags: * * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) * * Therefore, this function can be used to clear a callback flag by * setting the appropriate bit to zero. e.g. to disable the RTO * callback: * * **bpf_sock_ops_cb_flags_set(bpf_sock,** * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** * * Here are some examples of where one could call such eBPF * program: * * * When RTO fires. * * When a packet is retransmitted. * * When the connection terminates. * * When a packet is sent. * * When a packet is received. * * Returns * Code **-EINVAL** if the socket is not a full TCP socket; * otherwise, a positive number containing the bits that could not * be set is returned (which comes down to 0 if all bits were set * as required). */ static long (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59; /* * bpf_msg_redirect_map * * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60; /* * bpf_msg_apply_bytes * * For socket policies, apply the verdict of the eBPF program to * the next *bytes* (number of bytes) of message *msg*. * * For example, this helper can be used in the following cases: * * * A single **sendmsg**\ () or **sendfile**\ () system call * contains multiple logical messages that the eBPF program is * supposed to read and for which it should apply a verdict. * * An eBPF program only cares to read the first *bytes* of a * *msg*. If the message has a large payload, then setting up * and calling the eBPF program repeatedly for all bytes, even * though the verdict is already known, would create unnecessary * overhead. * * When called from within an eBPF program, the helper sets a * counter internal to the BPF infrastructure, that is used to * apply the last verdict to the next *bytes*. If *bytes* is * smaller than the current data being processed from a * **sendmsg**\ () or **sendfile**\ () system call, the first * *bytes* will be sent and the eBPF program will be re-run with * the pointer for start of data pointing to byte number *bytes* * **+ 1**. If *bytes* is larger than the current data being * processed, then the eBPF verdict will be applied to multiple * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are * consumed. * * Note that if a socket closes with the internal counter holding * a non-zero value, this is not a problem because data is not * being buffered for *bytes* and is sent as it is received. * * Returns * 0 */ static long (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61; /* * bpf_msg_cork_bytes * * For socket policies, prevent the execution of the verdict eBPF * program for message *msg* until *bytes* (byte number) have been * accumulated. * * This can be used when one needs a specific number of bytes * before a verdict can be assigned, even if the data spans * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme * case would be a user calling **sendmsg**\ () repeatedly with * 1-byte long message segments. Obviously, this is bad for * performance, but it is still valid. If the eBPF program needs * *bytes* bytes to validate a header, this helper can be used to * prevent the eBPF program to be called again until *bytes* have * been accumulated. * * Returns * 0 */ static long (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62; /* * bpf_msg_pull_data * * For socket policies, pull in non-linear data from user space * for *msg* and set pointers *msg*\ **->data** and *msg*\ * **->data_end** to *start* and *end* bytes offsets into *msg*, * respectively. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it can only parse data that the (**data**, **data_end**) * pointers have already consumed. For **sendmsg**\ () hooks this * is likely the first scatterlist element. But for calls relying * on the **sendpage** handler (e.g. **sendfile**\ ()) this will * be the range (**0**, **0**) because the data is shared with * user space and by default the objective is to avoid allowing * user space to modify data while (or after) eBPF verdict is * being decided. This helper can be used to pull in data and to * set the start and end pointer to given values. Data will be * copied if necessary (i.e. if data was not linear and if start * and end pointers do not point to the same chunk). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63; /* * bpf_bind * * Bind the socket associated to *ctx* to the address pointed by * *addr*, of length *addr_len*. This allows for making outgoing * connection from the desired IP address, which can be useful for * example when all processes inside a cgroup should use one * single IP address on a host that has multiple IP configured. * * This helper works for IPv4 and IPv6, TCP and UDP sockets. The * domain (*addr*\ **->sa_family**) must be **AF_INET** (or * **AF_INET6**). It's advised to pass zero port (**sin_port** * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like * behavior and lets the kernel efficiently pick up an unused * port as long as 4-tuple is unique. Passing non-zero port might * lead to degraded performance. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64; /* * bpf_xdp_adjust_tail * * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is * possible to both shrink and grow the packet tail. * Shrink done via *delta* being a negative integer. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65; /* * bpf_skb_get_xfrm_state * * Retrieve the XFRM state (IP transform framework, see also * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. * * The retrieved value is stored in the **struct bpf_xfrm_state** * pointed by *xfrm_state* and of length *size*. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_XFRM** configuration option. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66; /* * bpf_get_stack * * Return a user or a kernel stack in bpf program provided buffer. * To achieve this, the helper needs *ctx*, which is a pointer * to the context on which the tracing program is executed. * To store the stacktrace, the bpf program provides *buf* with * a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_USER_BUILD_ID** * Collect buildid+offset instead of ips for user stack, * only valid if **BPF_F_USER_STACK** is also specified. * * **bpf_get_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack=<new value> * * Returns * The non-negative copied *buf* length equal to or less than * *size* on success, or a negative error in case of failure. */ static long (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67; /* * bpf_skb_load_bytes_relative * * This helper is similar to **bpf_skb_load_bytes**\ () in that * it provides an easy way to load *len* bytes from *offset* * from the packet associated to *skb*, into the buffer pointed * by *to*. The difference to **bpf_skb_load_bytes**\ () is that * a fifth argument *start_header* exists in order to select a * base offset to start from. *start_header* can be one of: * * **BPF_HDR_START_MAC** * Base offset to load data from is *skb*'s mac header. * **BPF_HDR_START_NET** * Base offset to load data from is *skb*'s network header. * * In general, "direct packet access" is the preferred method to * access packet data, however, this helper is in particular useful * in socket filters where *skb*\ **->data** does not always point * to the start of the mac header and where "direct packet access" * is not available. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68; /* * bpf_fib_lookup * * Do FIB lookup in kernel tables using parameters in *params*. * If lookup is successful and result shows packet is to be * forwarded, the neighbor tables are searched for the nexthop. * If successful (ie., FIB lookup shows forwarding and nexthop * is resolved), the nexthop address is returned in ipv4_dst * or ipv6_dst based on family, smac is set to mac address of * egress device, dmac is set to nexthop mac address, rt_metric * is set to metric from route (IPv4/IPv6 only), and ifindex * is set to the device index of the nexthop from the FIB lookup. * * *plen* argument is the size of the passed in struct. * *flags* argument can be a combination of one or more of the * following values: * * **BPF_FIB_LOOKUP_DIRECT** * Do a direct table lookup vs full lookup using FIB * rules. * **BPF_FIB_LOOKUP_OUTPUT** * Perform lookup from an egress perspective (default is * ingress). * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** tc cls_act programs. * * Returns * * < 0 if any input argument is invalid * * 0 on success (packet is forwarded, nexthop neighbor exists) * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the * packet is not forwarded or needs assist from full stack * * If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU * was exceeded and output params->mtu_result contains the MTU. */ static long (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69; /* * bpf_sock_hash_update * * Add an entry to, or update a sockhash *map* referencing sockets. * The *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70; /* * bpf_msg_redirect_hash * * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71; /* * bpf_sk_redirect_hash * * This helper is used in programs implementing policies at the * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. * if the verdict eBPF program returns **SK_PASS**), redirect it * to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress otherwise). This is the only flag supported for now. * * Returns * **SK_PASS** on success, or **SK_DROP** on error. */ static long (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72; /* * bpf_lwt_push_encap * * Encapsulate the packet associated to *skb* within a Layer 3 * protocol header. This header is provided in the buffer at * address *hdr*, with *len* its size in bytes. *type* indicates * the protocol of the header and can be one of: * * **BPF_LWT_ENCAP_SEG6** * IPv6 encapsulation with Segment Routing Header * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, * the IPv6 header is computed by the kernel. * **BPF_LWT_ENCAP_SEG6_INLINE** * Only works if *skb* contains an IPv6 packet. Insert a * Segment Routing Header (**struct ipv6_sr_hdr**) inside * the IPv6 header. * **BPF_LWT_ENCAP_IP** * IP encapsulation (GRE/GUE/IPIP/etc). The outer header * must be IPv4 or IPv6, followed by zero or more * additional headers, up to **LWT_BPF_MAX_HEADROOM** * total bytes in all prepended headers. Please note that * if **skb_is_gso**\ (*skb*) is true, no more than two * headers can be prepended, and the inner header, if * present, should be either GRE or UDP/GUE. * * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and * **BPF_PROG_TYPE_LWT_XMIT**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73; /* * bpf_lwt_seg6_store_bytes * * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. Only the flags, tag and TLVs * inside the outermost IPv6 Segment Routing Header can be * modified through this helper. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74; /* * bpf_lwt_seg6_adjust_srh * * Adjust the size allocated to TLVs in the outermost IPv6 * Segment Routing Header contained in the packet associated to * *skb*, at position *offset* by *delta* bytes. Only offsets * after the segments are accepted. *delta* can be as well * positive (growing) as negative (shrinking). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75; /* * bpf_lwt_seg6_action * * Apply an IPv6 Segment Routing action of type *action* to the * packet associated to *skb*. Each action takes a parameter * contained at address *param*, and of length *param_len* bytes. * *action* can be one of: * * **SEG6_LOCAL_ACTION_END_X** * End.X action: Endpoint with Layer-3 cross-connect. * Type of *param*: **struct in6_addr**. * **SEG6_LOCAL_ACTION_END_T** * End.T action: Endpoint with specific IPv6 table lookup. * Type of *param*: **int**. * **SEG6_LOCAL_ACTION_END_B6** * End.B6 action: Endpoint bound to an SRv6 policy. * Type of *param*: **struct ipv6_sr_hdr**. * **SEG6_LOCAL_ACTION_END_B6_ENCAP** * End.B6.Encap action: Endpoint bound to an SRv6 * encapsulation policy. * Type of *param*: **struct ipv6_sr_hdr**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76; /* * bpf_rc_repeat * * This helper is used in programs implementing IR decoding, to * report a successfully decoded repeat key message. This delays * the generation of a key up event for previously generated * key down event. * * Some IR protocols like NEC have a special IR message for * repeating last button, for when a button is held down. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * * Returns * 0 */ static long (*bpf_rc_repeat)(void *ctx) = (void *) 77; /* * bpf_rc_keydown * * This helper is used in programs implementing IR decoding, to * report a successfully decoded key press with *scancode*, * *toggle* value in the given *protocol*. The scancode will be * translated to a keycode using the rc keymap, and reported as * an input key down event. After a period a key up event is * generated. This period can be extended by calling either * **bpf_rc_keydown**\ () again with the same values, or calling * **bpf_rc_repeat**\ (). * * Some protocols include a toggle bit, in case the button was * released and pressed again between consecutive scancodes. * * The *ctx* should point to the lirc sample as passed into * the program. * * The *protocol* is the decoded protocol number (see * **enum rc_proto** for some predefined values). * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * * Returns * 0 */ static long (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78; /* * bpf_skb_cgroup_id * * Return the cgroup v2 id of the socket associated with the *skb*. * This is roughly similar to the **bpf_get_cgroup_classid**\ () * helper for cgroup v1 by providing a tag resp. identifier that * can be matched on or used for map lookups e.g. to implement * policy. The cgroup v2 id of a given path in the hierarchy is * exposed in user space through the f_handle API in order to get * to the same 64-bit id. * * This helper can be used on TC egress path, but not on ingress, * and is available only if the kernel was compiled with the * **CONFIG_SOCK_CGROUP_DATA** configuration option. * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79; /* * bpf_get_current_cgroup_id * * Get the current cgroup id based on the cgroup within which * the current task is running. * * Returns * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. */ static __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80; /* * bpf_get_local_storage * * Get the pointer to the local storage area. * The type and the size of the local storage is defined * by the *map* argument. * The *flags* meaning is specific for each map type, * and has to be 0 for cgroup local storage. * * Depending on the BPF program type, a local storage area * can be shared between multiple instances of the BPF program, * running simultaneously. * * A user should care about the synchronization by himself. * For example, by using the **BPF_ATOMIC** instructions to alter * the shared data. * * Returns * A pointer to the local storage area. */ static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81; /* * bpf_sk_select_reuseport * * Select a **SO_REUSEPORT** socket from a * **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*. * It checks the selected socket is matching the incoming * request in the socket buffer. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82; /* * bpf_skb_ancestor_cgroup_id * * Return id of cgroup v2 that is ancestor of cgroup associated * with the *skb* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *skb*, then return value will be same as that * of **bpf_skb_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *skb*. * * The format of returned id and helper limitations are same as in * **bpf_skb_cgroup_id**\ (). * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83; /* * bpf_sk_lookup_tcp * * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * * Returns * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. */ static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84; /* * bpf_sk_lookup_udp * * Look for UDP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * * Returns * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. */ static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85; /* * bpf_sk_release * * Release the reference held by *sock*. *sock* must be a * non-**NULL** pointer that was returned from * **bpf_sk_lookup_xxx**\ (). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_sk_release)(void *sock) = (void *) 86; /* * bpf_map_push_elem * * Push an element *value* in *map*. *flags* is one of: * * **BPF_EXIST** * If the queue/stack is full, the oldest element is * removed to make room for this. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87; /* * bpf_map_pop_elem * * Pop an element from *map*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88; /* * bpf_map_peek_elem * * Get an element from *map* without removing it. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89; /* * bpf_msg_push_data * * For socket policies, insert *len* bytes into *msg* at offset * *start*. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it may want to insert metadata or options into the *msg*. * This can later be read and used by any of the lower layer BPF * hooks. * * This helper may fail if under memory pressure (a malloc * fails) in these cases BPF programs will get an appropriate * error and BPF programs will need to handle them. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90; /* * bpf_msg_pop_data * * Will remove *len* bytes from a *msg* starting at byte *start*. * This may result in **ENOMEM** errors under certain situations if * an allocation and copy are required due to a full ring buffer. * However, the helper will try to avoid doing the allocation * if possible. Other errors can occur if input parameters are * invalid either due to *start* byte not being valid part of *msg* * payload and/or *pop* value being to large. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91; /* * bpf_rc_pointer_rel * * This helper is used in programs implementing IR decoding, to * report a successfully decoded pointer movement. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * * Returns * 0 */ static long (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92; /* * bpf_spin_lock * * Acquire a spinlock represented by the pointer *lock*, which is * stored as part of a value of a map. Taking the lock allows to * safely update the rest of the fields in that value. The * spinlock can (and must) later be released with a call to * **bpf_spin_unlock**\ (\ *lock*\ ). * * Spinlocks in BPF programs come with a number of restrictions * and constraints: * * * **bpf_spin_lock** objects are only allowed inside maps of * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this * list could be extended in the future). * * BTF description of the map is mandatory. * * The BPF program can take ONE lock at a time, since taking two * or more could cause dead locks. * * Only one **struct bpf_spin_lock** is allowed per map element. * * When the lock is taken, calls (either BPF to BPF or helpers) * are not allowed. * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not * allowed inside a spinlock-ed region. * * The BPF program MUST call **bpf_spin_unlock**\ () to release * the lock, on all execution paths, before it returns. * * The BPF program can access **struct bpf_spin_lock** only via * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () * helpers. Loading or storing data into the **struct * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. * * To use the **bpf_spin_lock**\ () helper, the BTF description * of the map value must be a struct and have **struct * bpf_spin_lock** *anyname*\ **;** field at the top level. * Nested lock inside another struct is not allowed. * * The **struct bpf_spin_lock** *lock* field in a map value must * be aligned on a multiple of 4 bytes in that value. * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy * the **bpf_spin_lock** field to user space. * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from * a BPF program, do not update the **bpf_spin_lock** field. * * **bpf_spin_lock** cannot be on the stack or inside a * networking packet (it can only be inside of a map values). * * **bpf_spin_lock** is available to root only. * * Tracing programs and socket filter programs cannot use * **bpf_spin_lock**\ () due to insufficient preemption checks * (but this may change in the future). * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. * * Returns * 0 */ static long (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93; /* * bpf_spin_unlock * * Release the *lock* previously locked by a call to * **bpf_spin_lock**\ (\ *lock*\ ). * * Returns * 0 */ static long (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94; /* * bpf_sk_fullsock * * This helper gets a **struct bpf_sock** pointer such * that all the fields in this **bpf_sock** can be accessed. * * Returns * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. */ static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95; /* * bpf_tcp_sock * * This helper gets a **struct bpf_tcp_sock** pointer from a * **struct bpf_sock** pointer. * * Returns * A **struct bpf_tcp_sock** pointer on success, or **NULL** in * case of failure. */ static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96; /* * bpf_skb_ecn_set_ce * * Set ECN (Explicit Congestion Notification) field of IP header * to **CE** (Congestion Encountered) if current value is **ECT** * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 * and IPv4. * * Returns * 1 if the **CE** flag is set (either by the current helper call * or because it was already present), 0 if it is not set. */ static long (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97; /* * bpf_get_listener_sock * * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. * **bpf_sk_release**\ () is unnecessary and not allowed. * * Returns * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. */ static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98; /* * bpf_skc_lookup_tcp * * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * This function is identical to **bpf_sk_lookup_tcp**\ (), except * that it also returns timewait or request sockets. Use * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the * full structure. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * * Returns * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. */ static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99; /* * bpf_tcp_check_syncookie * * Check whether *iph* and *th* contain a valid SYN cookie ACK for * the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ipv6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative * error otherwise. */ static long (*bpf_tcp_check_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100; /* * bpf_sysctl_get_name * * Get name of sysctl in /proc/sys/ and copy it into provided by * program buffer *buf* of size *buf_len*. * * The buffer is always NUL terminated, unless it's zero-sized. * * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name * only (e.g. "tcp_mem"). * * Returns * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). */ static long (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101; /* * bpf_sysctl_get_current_value * * Get current value of sysctl as it is presented in /proc/sys * (incl. newline, etc), and copy it as a string into provided * by program buffer *buf* of size *buf_len*. * * The whole value is copied, no matter what file position user * space issued e.g. sys_read at. * * The buffer is always NUL terminated, unless it's zero-sized. * * Returns * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if current value was unavailable, e.g. because * sysctl is uninitialized and read returns -EIO for it. */ static long (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102; /* * bpf_sysctl_get_new_value * * Get new value being written by user space to sysctl (before * the actual write happens) and copy it as a string into * provided by program buffer *buf* of size *buf_len*. * * User space may write new value at file position > 0. * * The buffer is always NUL terminated, unless it's zero-sized. * * Returns * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if sysctl is being read. */ static long (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103; /* * bpf_sysctl_set_new_value * * Override new value being written by user space to sysctl with * value provided by program in buffer *buf* of size *buf_len*. * * *buf* should contain a string in same form as provided by user * space on sysctl write. * * User space may write new value at file position > 0. To override * the whole sysctl value file position should be set to zero. * * Returns * 0 on success. * * **-E2BIG** if the *buf_len* is too big. * * **-EINVAL** if sysctl is being read. */ static long (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104; /* * bpf_strtol * * Convert the initial part of the string from buffer *buf* of * size *buf_len* to a long integer according to the given base * and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)) followed by a single * optional '**-**' sign. * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtol**\ (3). * * Returns * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. */ static long (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105; /* * bpf_strtoul * * Convert the initial part of the string from buffer *buf* of * size *buf_len* to an unsigned long integer according to the * given base and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)). * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtoul**\ (3). * * Returns * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. */ static long (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106; /* * bpf_sk_storage_get * * Get a bpf-local-storage from a *sk*. * * Logically, it could be thought of getting the value from * a *map* with *sk* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this * helper enforces the key must be a full socket and the map must * be a **BPF_MAP_TYPE_SK_STORAGE** also. * * Underneath, the value is stored locally at *sk* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf-local-storages residing at *sk*. * * *sk* is a kernel **struct sock** pointer for LSM program. * *sk* is a **struct bpf_sock** pointer for other program types. * * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be * used such that a new bpf-local-storage will be * created if one does not exist. *value* can be used * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf-local-storage. If *value* is * **NULL**, the new bpf-local-storage will be zero initialized. * * Returns * A bpf-local-storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf-local-storage. */ static void *(*bpf_sk_storage_get)(void *map, void *sk, void *value, __u64 flags) = (void *) 107; /* * bpf_sk_storage_delete * * Delete a bpf-local-storage from a *sk*. * * Returns * 0 on success. * * **-ENOENT** if the bpf-local-storage cannot be found. * **-EINVAL** if sk is not a fullsock (e.g. a request_sock). */ static long (*bpf_sk_storage_delete)(void *map, void *sk) = (void *) 108; /* * bpf_send_signal * * Send signal *sig* to the process of the current task. * The signal may be delivered to any of this process's threads. * * Returns * 0 on success or successfully queued. * * **-EBUSY** if work queue under nmi is full. * * **-EINVAL** if *sig* is invalid. * * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. */ static long (*bpf_send_signal)(__u32 sig) = (void *) 109; /* * bpf_tcp_gen_syncookie * * Try to issue a SYN cookie for the packet with corresponding * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ipv6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header with options (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** SYN cookie cannot be issued due to error * * **-ENOENT** SYN cookie should not be issued (no SYN flood) * * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies * * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 */ static __s64 (*bpf_tcp_gen_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110; /* * bpf_skb_output * * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * *ctx* is a pointer to in-kernel struct sk_buff. * * This helper is similar to **bpf_perf_event_output**\ () but * restricted to raw_tracepoint bpf programs. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111; /* * bpf_probe_read_user * * Safely attempt to read *size* bytes from user space address * *unsafe_ptr* and store the data in *dst*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112; /* * bpf_probe_read_kernel * * Safely attempt to read *size* bytes from kernel space address * *unsafe_ptr* and store the data in *dst*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113; /* * bpf_probe_read_user_str * * Copy a NUL terminated string from an unsafe user address * *unsafe_ptr* to *dst*. The *size* should include the * terminating NUL byte. In case the string length is smaller than * *size*, the target is not padded with further NUL bytes. If the * string length is larger than *size*, just *size*-1 bytes are * copied and the last byte is set to NUL. * * On success, returns the number of bytes that were written, * including the terminal NUL. This makes this helper useful in * tracing programs for reading strings, and more importantly to * get its length at runtime. See the following snippet: * * :: * * SEC("kprobe/sys_open") * void bpf_sys_open(struct pt_regs *ctx) * { * char buf[PATHLEN]; // PATHLEN is defined to 256 * int res = bpf_probe_read_user_str(buf, sizeof(buf), * ctx->di); * * // Consume buf, for example push it to * // userspace via bpf_perf_event_output(); we * // can use res (the string length) as event * // size, after checking its boundaries. * } * * In comparison, using **bpf_probe_read_user**\ () helper here * instead to read the string would require to estimate the length * at compile time, and would often result in copying more memory * than necessary. * * Another useful use case is when parsing individual process * arguments or individual environment variables navigating * *current*\ **->mm->arg_start** and *current*\ * **->mm->env_start**: using this helper and the return value, * one can quickly iterate at the right offset of the memory area. * * Returns * On success, the strictly positive length of the output string, * including the trailing NUL character. On error, a negative * value. */ static long (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114; /* * bpf_probe_read_kernel_str * * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. * * Returns * On success, the strictly positive length of the string, including * the trailing NUL character. On error, a negative value. */ static long (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115; /* * bpf_tcp_send_ack * * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. * *rcv_nxt* is the ack_seq to be sent out. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_tcp_send_ack)(void *tp, __u32 rcv_nxt) = (void *) 116; /* * bpf_send_signal_thread * * Send signal *sig* to the thread corresponding to the current task. * * Returns * 0 on success or successfully queued. * * **-EBUSY** if work queue under nmi is full. * * **-EINVAL** if *sig* is invalid. * * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. */ static long (*bpf_send_signal_thread)(__u32 sig) = (void *) 117; /* * bpf_jiffies64 * * Obtain the 64bit jiffies * * Returns * The 64 bit jiffies */ static __u64 (*bpf_jiffies64)(void) = (void *) 118; /* * bpf_read_branch_records * * For an eBPF program attached to a perf event, retrieve the * branch records (**struct perf_branch_entry**) associated to *ctx* * and store it in the buffer pointed by *buf* up to size * *size* bytes. * * Returns * On success, number of bytes written to *buf*. On error, a * negative value. * * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to * instead return the number of bytes required to store all the * branch entries. If this flag is set, *buf* may be NULL. * * **-EINVAL** if arguments invalid or **size** not a multiple * of **sizeof**\ (**struct perf_branch_entry**\ ). * * **-ENOENT** if architecture does not support branch records. */ static long (*bpf_read_branch_records)(struct bpf_perf_event_data *ctx, void *buf, __u32 size, __u64 flags) = (void *) 119; /* * bpf_get_ns_current_pid_tgid * * Returns 0 on success, values for *pid* and *tgid* as seen from the current * *namespace* will be returned in *nsdata*. * * Returns * 0 on success, or one of the following in case of failure: * * **-EINVAL** if dev and inum supplied don't match dev_t and inode number * with nsfs of current task, or if dev conversion to dev_t lost high bits. * * **-ENOENT** if pidns does not exists for the current task. */ static long (*bpf_get_ns_current_pid_tgid)(__u64 dev, __u64 ino, struct bpf_pidns_info *nsdata, __u32 size) = (void *) 120; /* * bpf_xdp_output * * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * *ctx* is a pointer to in-kernel struct xdp_buff. * * This helper is similar to **bpf_perf_eventoutput**\ () but * restricted to raw_tracepoint bpf programs. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 121; /* * bpf_get_netns_cookie * * Retrieve the cookie (generated by the kernel) of the network * namespace the input *ctx* is associated with. The network * namespace cookie remains stable for its lifetime and provides * a global identifier that can be assumed unique. If *ctx* is * NULL, then the helper returns the cookie for the initial * network namespace. The cookie itself is very similar to that * of **bpf_get_socket_cookie**\ () helper, but for network * namespaces instead of sockets. * * Returns * A 8-byte long opaque number. */ static __u64 (*bpf_get_netns_cookie)(void *ctx) = (void *) 122; /* * bpf_get_current_ancestor_cgroup_id * * Return id of cgroup v2 that is ancestor of the cgroup associated * with the current task at the *ancestor_level*. The root cgroup * is at *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with the current task, then return value will be the * same as that of **bpf_get_current_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with the current task. * * The format of returned id and helper limitations are same as in * **bpf_get_current_cgroup_id**\ (). * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_get_current_ancestor_cgroup_id)(int ancestor_level) = (void *) 123; /* * bpf_sk_assign * * Helper is overloaded depending on BPF program type. This * description applies to **BPF_PROG_TYPE_SCHED_CLS** and * **BPF_PROG_TYPE_SCHED_ACT** programs. * * Assign the *sk* to the *skb*. When combined with appropriate * routing configuration to receive the packet towards the socket, * will cause *skb* to be delivered to the specified socket. * Subsequent redirection of *skb* via **bpf_redirect**\ (), * **bpf_clone_redirect**\ () or other methods outside of BPF may * interfere with successful delivery to the socket. * * This operation is only valid from TC ingress path. * * The *flags* argument must be zero. * * Returns * 0 on success, or a negative error in case of failure: * * **-EINVAL** if specified *flags* are not supported. * * **-ENOENT** if the socket is unavailable for assignment. * * **-ENETUNREACH** if the socket is unreachable (wrong netns). * * **-EOPNOTSUPP** if the operation is not supported, for example * a call from outside of TC ingress. * * **-ESOCKTNOSUPPORT** if the socket type is not supported * (reuseport). */ static long (*bpf_sk_assign)(void *ctx, void *sk, __u64 flags) = (void *) 124; /* * bpf_ktime_get_boot_ns * * Return the time elapsed since system boot, in nanoseconds. * Does include the time the system was suspended. * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) * * Returns * Current *ktime*. */ static __u64 (*bpf_ktime_get_boot_ns)(void) = (void *) 125; /* * bpf_seq_printf * * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print * out the format string. * The *m* represents the seq_file. The *fmt* and *fmt_size* are for * the format string itself. The *data* and *data_len* are format string * arguments. The *data* are a **u64** array and corresponding format string * values are stored in the array. For strings and pointers where pointees * are accessed, only the pointer values are stored in the *data* array. * The *data_len* is the size of *data* in bytes - must be a multiple of 8. * * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. * Reading kernel memory may fail due to either invalid address or * valid address but requiring a major memory fault. If reading kernel memory * fails, the string for **%s** will be an empty string, and the ip * address for **%p{i,I}{4,6}** will be 0. Not returning error to * bpf program is consistent with what **bpf_trace_printk**\ () does for now. * * Returns * 0 on success, or a negative error in case of failure: * * **-EBUSY** if per-CPU memory copy buffer is busy, can try again * by returning 1 from bpf program. * * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. * * **-E2BIG** if *fmt* contains too many format specifiers. * * **-EOVERFLOW** if an overflow happened: The same object will be tried again. */ static long (*bpf_seq_printf)(struct seq_file *m, const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 126; /* * bpf_seq_write * * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. * The *m* represents the seq_file. The *data* and *len* represent the * data to write in bytes. * * Returns * 0 on success, or a negative error in case of failure: * * **-EOVERFLOW** if an overflow happened: The same object will be tried again. */ static long (*bpf_seq_write)(struct seq_file *m, const void *data, __u32 len) = (void *) 127; /* * bpf_sk_cgroup_id * * Return the cgroup v2 id of the socket *sk*. * * *sk* must be a non-**NULL** pointer to a socket, e.g. one * returned from **bpf_sk_lookup_xxx**\ (), * **bpf_sk_fullsock**\ (), etc. The format of returned id is * same as in **bpf_skb_cgroup_id**\ (). * * This helper is available only if the kernel was compiled with * the **CONFIG_SOCK_CGROUP_DATA** configuration option. * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_sk_cgroup_id)(void *sk) = (void *) 128; /* * bpf_sk_ancestor_cgroup_id * * Return id of cgroup v2 that is ancestor of cgroup associated * with the *sk* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *sk*, then return value will be same as that * of **bpf_sk_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *sk*. * * The format of returned id and helper limitations are same as in * **bpf_sk_cgroup_id**\ (). * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_sk_ancestor_cgroup_id)(void *sk, int ancestor_level) = (void *) 129; /* * bpf_ringbuf_output * * Copy *size* bytes from *data* into a ring buffer *ringbuf*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * An adaptive notification is a notification sent whenever the user-space * process has caught up and consumed all available payloads. In case the user-space * process is still processing a previous payload, then no notification is needed * as it will process the newly added payload automatically. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130; /* * bpf_ringbuf_reserve * * Reserve *size* bytes of payload in a ring buffer *ringbuf*. * *flags* must be 0. * * Returns * Valid pointer with *size* bytes of memory available; NULL, * otherwise. */ static void *(*bpf_ringbuf_reserve)(void *ringbuf, __u64 size, __u64 flags) = (void *) 131; /* * bpf_ringbuf_submit * * Submit reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_submit)(void *data, __u64 flags) = (void *) 132; /* * bpf_ringbuf_discard * * Discard reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_discard)(void *data, __u64 flags) = (void *) 133; /* * bpf_ringbuf_query * * Query various characteristics of provided ring buffer. What * exactly is queries is determined by *flags*: * * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. * * **BPF_RB_RING_SIZE**: The size of ring buffer. * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). * * Data returned is just a momentary snapshot of actual values * and could be inaccurate, so this facility should be used to * power heuristics and for reporting, not to make 100% correct * calculation. * * Returns * Requested value, or 0, if *flags* are not recognized. */ static __u64 (*bpf_ringbuf_query)(void *ringbuf, __u64 flags) = (void *) 134; /* * bpf_csum_level * * Change the skbs checksum level by one layer up or down, or * reset it entirely to none in order to have the stack perform * checksum validation. The level is applicable to the following * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | * through **bpf_skb_adjust_room**\ () helper with passing in * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since * the UDP header is removed. Similarly, an encap of the latter * into the former could be accompanied by a helper call to * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the * skb is still intended to be processed in higher layers of the * stack instead of just egressing at tc. * * There are three supported level settings at this time: * * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs * with CHECKSUM_UNNECESSARY. * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs * with CHECKSUM_UNNECESSARY. * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and * sets CHECKSUM_NONE to force checksum validation by the stack. * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current * skb->csum_level. * * Returns * 0 on success, or a negative error in case of failure. In the * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level * is returned or the error code -EACCES in case the skb is not * subject to CHECKSUM_UNNECESSARY. */ static long (*bpf_csum_level)(struct __sk_buff *skb, __u64 level) = (void *) 135; /* * bpf_skc_to_tcp6_sock * * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp6_sock *(*bpf_skc_to_tcp6_sock)(void *sk) = (void *) 136; /* * bpf_skc_to_tcp_sock * * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp_sock *(*bpf_skc_to_tcp_sock)(void *sk) = (void *) 137; /* * bpf_skc_to_tcp_timewait_sock * * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp_timewait_sock *(*bpf_skc_to_tcp_timewait_sock)(void *sk) = (void *) 138; /* * bpf_skc_to_tcp_request_sock * * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct tcp_request_sock *(*bpf_skc_to_tcp_request_sock)(void *sk) = (void *) 139; /* * bpf_skc_to_udp6_sock * * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct udp6_sock *(*bpf_skc_to_udp6_sock)(void *sk) = (void *) 140; /* * bpf_get_task_stack * * Return a user or a kernel stack in bpf program provided buffer. * To achieve this, the helper needs *task*, which is a valid * pointer to **struct task_struct**. To store the stacktrace, the * bpf program provides *buf* with a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_USER_BUILD_ID** * Collect buildid+offset instead of ips for user stack, * only valid if **BPF_F_USER_STACK** is also specified. * * **bpf_get_task_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack=<new value> * * Returns * The non-negative copied *buf* length equal to or less than * *size* on success, or a negative error in case of failure. */ static long (*bpf_get_task_stack)(struct task_struct *task, void *buf, __u32 size, __u64 flags) = (void *) 141; /* * bpf_load_hdr_opt * * Load header option. Support reading a particular TCP header * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). * * If *flags* is 0, it will search the option from the * *skops*\ **->skb_data**. The comment in **struct bpf_sock_ops** * has details on what skb_data contains under different * *skops*\ **->op**. * * The first byte of the *searchby_res* specifies the * kind that it wants to search. * * If the searching kind is an experimental kind * (i.e. 253 or 254 according to RFC6994). It also * needs to specify the "magic" which is either * 2 bytes or 4 bytes. It then also needs to * specify the size of the magic by using * the 2nd byte which is "kind-length" of a TCP * header option and the "kind-length" also * includes the first 2 bytes "kind" and "kind-length" * itself as a normal TCP header option also does. * * For example, to search experimental kind 254 with * 2 byte magic 0xeB9F, the searchby_res should be * [ 254, 4, 0xeB, 0x9F, 0, 0, .... 0 ]. * * To search for the standard window scale option (3), * the *searchby_res* should be [ 3, 0, 0, .... 0 ]. * Note, kind-length must be 0 for regular option. * * Searching for No-Op (0) and End-of-Option-List (1) are * not supported. * * *len* must be at least 2 bytes which is the minimal size * of a header option. * * Supported flags: * * * **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the * saved_syn packet or the just-received syn packet. * * * Returns * > 0 when found, the header option is copied to *searchby_res*. * The return value is the total length copied. On failure, a * negative error code is returned: * * **-EINVAL** if a parameter is invalid. * * **-ENOMSG** if the option is not found. * * **-ENOENT** if no syn packet is available when * **BPF_LOAD_HDR_OPT_TCP_SYN** is used. * * **-ENOSPC** if there is not enough space. Only *len* number of * bytes are copied. * * **-EFAULT** on failure to parse the header options in the * packet. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. */ static long (*bpf_load_hdr_opt)(struct bpf_sock_ops *skops, void *searchby_res, __u32 len, __u64 flags) = (void *) 142; /* * bpf_store_hdr_opt * * Store header option. The data will be copied * from buffer *from* with length *len* to the TCP header. * * The buffer *from* should have the whole option that * includes the kind, kind-length, and the actual * option data. The *len* must be at least kind-length * long. The kind-length does not have to be 4 byte * aligned. The kernel will take care of the padding * and setting the 4 bytes aligned value to th->doff. * * This helper will check for duplicated option * by searching the same option in the outgoing skb. * * This helper can only be called during * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. * * * Returns * 0 on success, or negative error in case of failure: * * **-EINVAL** If param is invalid. * * **-ENOSPC** if there is not enough space in the header. * Nothing has been written * * **-EEXIST** if the option already exists. * * **-EFAULT** on failrue to parse the existing header options. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. */ static long (*bpf_store_hdr_opt)(struct bpf_sock_ops *skops, const void *from, __u32 len, __u64 flags) = (void *) 143; /* * bpf_reserve_hdr_opt * * Reserve *len* bytes for the bpf header option. The * space will be used by **bpf_store_hdr_opt**\ () later in * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. * * If **bpf_reserve_hdr_opt**\ () is called multiple times, * the total number of bytes will be reserved. * * This helper can only be called during * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**. * * * Returns * 0 on success, or negative error in case of failure: * * **-EINVAL** if a parameter is invalid. * * **-ENOSPC** if there is not enough space in the header. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. */ static long (*bpf_reserve_hdr_opt)(struct bpf_sock_ops *skops, __u32 len, __u64 flags) = (void *) 144; /* * bpf_inode_storage_get * * Get a bpf_local_storage from an *inode*. * * Logically, it could be thought of as getting the value from * a *map* with *inode* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *inode*) except this * helper enforces the key must be an inode and the map must also * be a **BPF_MAP_TYPE_INODE_STORAGE**. * * Underneath, the value is stored locally at *inode* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf_local_storage residing at *inode*. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * * Returns * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. */ static void *(*bpf_inode_storage_get)(void *map, void *inode, void *value, __u64 flags) = (void *) 145; /* * bpf_inode_storage_delete * * Delete a bpf_local_storage from an *inode*. * * Returns * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. */ static int (*bpf_inode_storage_delete)(void *map, void *inode) = (void *) 146; /* * bpf_d_path * * Return full path for given **struct path** object, which * needs to be the kernel BTF *path* object. The path is * returned in the provided buffer *buf* of size *sz* and * is zero terminated. * * * Returns * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. */ static long (*bpf_d_path)(struct path *path, char *buf, __u32 sz) = (void *) 147; /* * bpf_copy_from_user * * Read *size* bytes from user space address *user_ptr* and store * the data in *dst*. This is a wrapper of **copy_from_user**\ (). * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_copy_from_user)(void *dst, __u32 size, const void *user_ptr) = (void *) 148; /* * bpf_snprintf_btf * * Use BTF to store a string representation of *ptr*->ptr in *str*, * using *ptr*->type_id. This value should specify the type * that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1) * can be used to look up vmlinux BTF type ids. Traversing the * data structure using BTF, the type information and values are * stored in the first *str_size* - 1 bytes of *str*. Safe copy of * the pointer data is carried out to avoid kernel crashes during * operation. Smaller types can use string space on the stack; * larger programs can use map data to store the string * representation. * * The string can be subsequently shared with userspace via * bpf_perf_event_output() or ring buffer interfaces. * bpf_trace_printk() is to be avoided as it places too small * a limit on string size to be useful. * * *flags* is a combination of * * **BTF_F_COMPACT** * no formatting around type information * **BTF_F_NONAME** * no struct/union member names/types * **BTF_F_PTR_RAW** * show raw (unobfuscated) pointer values; * equivalent to printk specifier %px. * **BTF_F_ZERO** * show zero-valued struct/union members; they * are not displayed by default * * * Returns * The number of bytes that were written (or would have been * written if output had to be truncated due to string size), * or a negative error in cases of failure. */ static long (*bpf_snprintf_btf)(char *str, __u32 str_size, struct btf_ptr *ptr, __u32 btf_ptr_size, __u64 flags) = (void *) 149; /* * bpf_seq_printf_btf * * Use BTF to write to seq_write a string representation of * *ptr*->ptr, using *ptr*->type_id as per bpf_snprintf_btf(). * *flags* are identical to those used for bpf_snprintf_btf. * * Returns * 0 on success or a negative error in case of failure. */ static long (*bpf_seq_printf_btf)(struct seq_file *m, struct btf_ptr *ptr, __u32 ptr_size, __u64 flags) = (void *) 150; /* * bpf_skb_cgroup_classid * * See **bpf_get_cgroup_classid**\ () for the main description. * This helper differs from **bpf_get_cgroup_classid**\ () in that * the cgroup v1 net_cls class is retrieved only from the *skb*'s * associated socket instead of the current process. * * Returns * The id is returned or 0 in case the id could not be retrieved. */ static __u64 (*bpf_skb_cgroup_classid)(struct __sk_buff *skb) = (void *) 151; /* * bpf_redirect_neigh * * Redirect the packet to another net device of index *ifindex* * and fill in L2 addresses from neighboring subsystem. This helper * is somewhat similar to **bpf_redirect**\ (), except that it * populates L2 addresses as well, meaning, internally, the helper * relies on the neighbor lookup for the L2 address of the nexthop. * * The helper will perform a FIB lookup based on the skb's * networking header to get the address of the next hop, unless * this is supplied by the caller in the *params* argument. The * *plen* argument indicates the len of *params* and should be set * to 0 if *params* is NULL. * * The *flags* argument is reserved and must be 0. The helper is * currently only supported for tc BPF program types, and enabled * for IPv4 and IPv6 protocols. * * Returns * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. */ static long (*bpf_redirect_neigh)(__u32 ifindex, struct bpf_redir_neigh *params, int plen, __u64 flags) = (void *) 152; /* * bpf_per_cpu_ptr * * Take a pointer to a percpu ksym, *percpu_ptr*, and return a * pointer to the percpu kernel variable on *cpu*. A ksym is an * extern variable decorated with '__ksym'. For ksym, there is a * global var (either static or global) defined of the same name * in the kernel. The ksym is percpu if the global var is percpu. * The returned pointer points to the global percpu var on *cpu*. * * bpf_per_cpu_ptr() has the same semantic as per_cpu_ptr() in the * kernel, except that bpf_per_cpu_ptr() may return NULL. This * happens if *cpu* is larger than nr_cpu_ids. The caller of * bpf_per_cpu_ptr() must check the returned value. * * Returns * A pointer pointing to the kernel percpu variable on *cpu*, or * NULL, if *cpu* is invalid. */ static void *(*bpf_per_cpu_ptr)(const void *percpu_ptr, __u32 cpu) = (void *) 153; /* * bpf_this_cpu_ptr * * Take a pointer to a percpu ksym, *percpu_ptr*, and return a * pointer to the percpu kernel variable on this cpu. See the * description of 'ksym' in **bpf_per_cpu_ptr**\ (). * * bpf_this_cpu_ptr() has the same semantic as this_cpu_ptr() in * the kernel. Different from **bpf_per_cpu_ptr**\ (), it would * never return NULL. * * Returns * A pointer pointing to the kernel percpu variable on this cpu. */ static void *(*bpf_this_cpu_ptr)(const void *percpu_ptr) = (void *) 154; /* * bpf_redirect_peer * * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_redirect**\ (), except * that the redirection happens to the *ifindex*' peer device and * the netns switch takes place from ingress to ingress without * going through the CPU's backlog queue. * * The *flags* argument is reserved and must be 0. The helper is * currently only supported for tc BPF program types at the ingress * hook and for veth device types. The peer device must reside in a * different network namespace. * * Returns * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. */ static long (*bpf_redirect_peer)(__u32 ifindex, __u64 flags) = (void *) 155; /* * bpf_task_storage_get * * Get a bpf_local_storage from the *task*. * * Logically, it could be thought of as getting the value from * a *map* with *task* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this * helper enforces the key must be an task_struct and the map must also * be a **BPF_MAP_TYPE_TASK_STORAGE**. * * Underneath, the value is stored locally at *task* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf_local_storage residing at *task*. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * * Returns * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. */ static void *(*bpf_task_storage_get)(void *map, struct task_struct *task, void *value, __u64 flags) = (void *) 156; /* * bpf_task_storage_delete * * Delete a bpf_local_storage from a *task*. * * Returns * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. */ static long (*bpf_task_storage_delete)(void *map, struct task_struct *task) = (void *) 157; /* * bpf_get_current_task_btf * * Return a BTF pointer to the "current" task. * This pointer can also be used in helpers that accept an * *ARG_PTR_TO_BTF_ID* of type *task_struct*. * * Returns * Pointer to the current task. */ static struct task_struct *(*bpf_get_current_task_btf)(void) = (void *) 158; /* * bpf_bprm_opts_set * * Set or clear certain options on *bprm*: * * **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit * which sets the **AT_SECURE** auxv for glibc. The bit * is cleared if the flag is not specified. * * Returns * **-EINVAL** if invalid *flags* are passed, zero otherwise. */ static long (*bpf_bprm_opts_set)(struct linux_binprm *bprm, __u64 flags) = (void *) 159; /* * bpf_ktime_get_coarse_ns * * Return a coarse-grained version of the time elapsed since * system boot, in nanoseconds. Does not include time the system * was suspended. * * See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**) * * Returns * Current *ktime*. */ static __u64 (*bpf_ktime_get_coarse_ns)(void) = (void *) 160; /* * bpf_ima_inode_hash * * Returns the stored IMA hash of the *inode* (if it's avaialable). * If the hash is larger than *size*, then only *size* * bytes will be copied to *dst* * * Returns * The **hash_algo** is returned on success, * **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if * invalid arguments are passed. */ static long (*bpf_ima_inode_hash)(struct inode *inode, void *dst, __u32 size) = (void *) 161; /* * bpf_sock_from_file * * If the given file represents a socket, returns the associated * socket. * * Returns * A pointer to a struct socket on success or NULL if the file is * not a socket. */ static struct socket *(*bpf_sock_from_file)(struct file *file) = (void *) 162; /* * bpf_check_mtu * * Check packet size against exceeding MTU of net device (based * on *ifindex*). This helper will likely be used in combination * with helpers that adjust/change the packet size. * * The argument *len_diff* can be used for querying with a planned * size change. This allows to check MTU prior to changing packet * ctx. Providing an *len_diff* adjustment that is larger than the * actual packet size (resulting in negative packet size) will in * principle not exceed the MTU, why it is not considered a * failure. Other BPF-helpers are needed for performing the * planned size change, why the responsability for catch a negative * packet size belong in those helpers. * * Specifying *ifindex* zero means the MTU check is performed * against the current net device. This is practical if this isn't * used prior to redirect. * * On input *mtu_len* must be a valid pointer, else verifier will * reject BPF program. If the value *mtu_len* is initialized to * zero then the ctx packet size is use. When value *mtu_len* is * provided as input this specify the L3 length that the MTU check * is done against. Remember XDP and TC length operate at L2, but * this value is L3 as this correlate to MTU and IP-header tot_len * values which are L3 (similar behavior as bpf_fib_lookup). * * The Linux kernel route table can configure MTUs on a more * specific per route level, which is not provided by this helper. * For route level MTU checks use the **bpf_fib_lookup**\ () * helper. * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** for tc cls_act programs. * * The *flags* argument can be a combination of one or more of the * following values: * * **BPF_MTU_CHK_SEGS** * This flag will only works for *ctx* **struct sk_buff**. * If packet context contains extra packet segment buffers * (often knows as GSO skb), then MTU check is harder to * check at this point, because in transmit path it is * possible for the skb packet to get re-segmented * (depending on net device features). This could still be * a MTU violation, so this flag enables performing MTU * check against segments, with a different violation * return code to tell it apart. Check cannot use len_diff. * * On return *mtu_len* pointer contains the MTU value of the net * device. Remember the net device configured MTU is the L3 size, * which is returned here and XDP and TC length operate at L2. * Helper take this into account for you, but remember when using * MTU value in your BPF-code. * * * Returns * * 0 on success, and populate MTU value in *mtu_len* pointer. * * * < 0 if any input argument is invalid (*mtu_len* not updated) * * MTU violations return positive values, but also populate MTU * value in *mtu_len* pointer, as this can be needed for * implementing PMTU handing: * * * **BPF_MTU_CHK_RET_FRAG_NEEDED** * * **BPF_MTU_CHK_RET_SEGS_TOOBIG** */ static long (*bpf_check_mtu)(void *ctx, __u32 ifindex, __u32 *mtu_len, __s32 len_diff, __u64 flags) = (void *) 163; /* * bpf_for_each_map_elem * * For each element in **map**, call **callback_fn** function with * **map**, **callback_ctx** and other map-specific parameters. * The **callback_fn** should be a static function and * the **callback_ctx** should be a pointer to the stack. * The **flags** is used to control certain aspects of the helper. * Currently, the **flags** must be 0. * * The following are a list of supported map types and their * respective expected callback signatures: * * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH, * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY * * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx); * * For per_cpu maps, the map_value is the value on the cpu where the * bpf_prog is running. * * If **callback_fn** return 0, the helper will continue to the next * element. If return value is 1, the helper will skip the rest of * elements and return. Other return values are not used now. * * * Returns * The number of traversed map elements for success, **-EINVAL** for * invalid **flags**. */ static long (*bpf_for_each_map_elem)(void *map, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 164; /* * bpf_snprintf * * Outputs a string into the **str** buffer of size **str_size** * based on a format string stored in a read-only map pointed by * **fmt**. * * Each format specifier in **fmt** corresponds to one u64 element * in the **data** array. For strings and pointers where pointees * are accessed, only the pointer values are stored in the *data* * array. The *data_len* is the size of *data* in bytes - must be * a multiple of 8. * * Formats **%s** and **%p{i,I}{4,6}** require to read kernel * memory. Reading kernel memory may fail due to either invalid * address or valid address but requiring a major memory fault. If * reading kernel memory fails, the string for **%s** will be an * empty string, and the ip address for **%p{i,I}{4,6}** will be 0. * Not returning error to bpf program is consistent with what * **bpf_trace_printk**\ () does for now. * * * Returns * The strictly positive length of the formatted string, including * the trailing zero character. If the return value is greater than * **str_size**, **str** contains a truncated string, guaranteed to * be zero-terminated except when **str_size** is 0. * * Or **-EBUSY** if the per-CPU memory copy buffer is busy. */ static long (*bpf_snprintf)(char *str, __u32 str_size, const char *fmt, __u64 *data, __u32 data_len) = (void *) 165; /* * bpf_sys_bpf * * Execute bpf syscall with given arguments. * * Returns * A syscall result. */ static long (*bpf_sys_bpf)(__u32 cmd, void *attr, __u32 attr_size) = (void *) 166; /* * bpf_btf_find_by_name_kind * * Find BTF type with given name and kind in vmlinux BTF or in module's BTFs. * * Returns * Returns btf_id and btf_obj_fd in lower and upper 32 bits. */ static long (*bpf_btf_find_by_name_kind)(char *name, int name_sz, __u32 kind, int flags) = (void *) 167; /* * bpf_sys_close * * Execute close syscall for given FD. * * Returns * A syscall result. */ static long (*bpf_sys_close)(__u32 fd) = (void *) 168; /* * bpf_timer_init * * Initialize the timer. * First 4 bits of *flags* specify clockid. * Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed. * All other bits of *flags* are reserved. * The verifier will reject the program if *timer* is not from * the same *map*. * * Returns * 0 on success. * **-EBUSY** if *timer* is already initialized. * **-EINVAL** if invalid *flags* are passed. * **-EPERM** if *timer* is in a map that doesn't have any user references. * The user space should either hold a file descriptor to a map with timers * or pin such map in bpffs. When map is unpinned or file descriptor is * closed all timers in the map will be cancelled and freed. */ static long (*bpf_timer_init)(struct bpf_timer *timer, void *map, __u64 flags) = (void *) 169; /* * bpf_timer_set_callback * * Configure the timer to call *callback_fn* static function. * * Returns * 0 on success. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. * **-EPERM** if *timer* is in a map that doesn't have any user references. * The user space should either hold a file descriptor to a map with timers * or pin such map in bpffs. When map is unpinned or file descriptor is * closed all timers in the map will be cancelled and freed. */ static long (*bpf_timer_set_callback)(struct bpf_timer *timer, void *callback_fn) = (void *) 170; /* * bpf_timer_start * * Set timer expiration N nanoseconds from the current time. The * configured callback will be invoked in soft irq context on some cpu * and will not repeat unless another bpf_timer_start() is made. * In such case the next invocation can migrate to a different cpu. * Since struct bpf_timer is a field inside map element the map * owns the timer. The bpf_timer_set_callback() will increment refcnt * of BPF program to make sure that callback_fn code stays valid. * When user space reference to a map reaches zero all timers * in a map are cancelled and corresponding program's refcnts are * decremented. This is done to make sure that Ctrl-C of a user * process doesn't leave any timers running. If map is pinned in * bpffs the callback_fn can re-arm itself indefinitely. * bpf_map_update/delete_elem() helpers and user space sys_bpf commands * cancel and free the timer in the given map element. * The map can contain timers that invoke callback_fn-s from different * programs. The same callback_fn can serve different timers from * different maps if key/value layout matches across maps. * Every bpf_timer_set_callback() can have different callback_fn. * * * Returns * 0 on success. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier * or invalid *flags* are passed. */ static long (*bpf_timer_start)(struct bpf_timer *timer, __u64 nsecs, __u64 flags) = (void *) 171; /* * bpf_timer_cancel * * Cancel the timer and wait for callback_fn to finish if it was running. * * Returns * 0 if the timer was not active. * 1 if the timer was active. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. * **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its * own timer which would have led to a deadlock otherwise. */ static long (*bpf_timer_cancel)(struct bpf_timer *timer) = (void *) 172; /* * bpf_get_func_ip * * Get address of the traced function (for tracing and kprobe programs). * * Returns * Address of the traced function. */ static __u64 (*bpf_get_func_ip)(void *ctx) = (void *) 173; /* * bpf_get_attach_cookie * * Get bpf_cookie value provided (optionally) during the program * attachment. It might be different for each individual * attachment, even if BPF program itself is the same. * Expects BPF program context *ctx* as a first argument. * * Supported for the following program types: * - kprobe/uprobe; * - tracepoint; * - perf_event. * * Returns * Value specified by user at BPF link creation/attachment time * or 0, if it was not specified. */ static __u64 (*bpf_get_attach_cookie)(void *ctx) = (void *) 174; /* * bpf_task_pt_regs * * Get the struct pt_regs associated with **task**. * * Returns * A pointer to struct pt_regs. */ static long (*bpf_task_pt_regs)(struct task_struct *task) = (void *) 175; /* * bpf_get_branch_snapshot * * Get branch trace from hardware engines like Intel LBR. The * hardware engine is stopped shortly after the helper is * called. Therefore, the user need to filter branch entries * based on the actual use case. To capture branch trace * before the trigger point of the BPF program, the helper * should be called at the beginning of the BPF program. * * The data is stored as struct perf_branch_entry into output * buffer *entries*. *size* is the size of *entries* in bytes. * *flags* is reserved for now and must be zero. * * * Returns * On success, number of bytes written to *buf*. On error, a * negative value. * * **-EINVAL** if *flags* is not zero. * * **-ENOENT** if architecture does not support branch records. */ static long (*bpf_get_branch_snapshot)(void *entries, __u32 size, __u64 flags) = (void *) 176; /* * bpf_trace_vprintk * * Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64 * to format and can handle more format args as a result. * * Arguments are to be used as in **bpf_seq_printf**\ () helper. * * Returns * The number of bytes written to the buffer, or a negative error * in case of failure. */ static long (*bpf_trace_vprintk)(const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 177; /* * bpf_skc_to_unix_sock * * Dynamically cast a *sk* pointer to a *unix_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct unix_sock *(*bpf_skc_to_unix_sock)(void *sk) = (void *) 178; /* * bpf_kallsyms_lookup_name * * Get the address of a kernel symbol, returned in *res*. *res* is * set to 0 if the symbol is not found. * * Returns * On success, zero. On error, a negative value. * * **-EINVAL** if *flags* is not zero. * * **-EINVAL** if string *name* is not the same size as *name_sz*. * * **-ENOENT** if symbol is not found. * * **-EPERM** if caller does not have permission to obtain kernel address. */ static long (*bpf_kallsyms_lookup_name)(const char *name, int name_sz, int flags, __u64 *res) = (void *) 179; /* * bpf_find_vma * * Find vma of *task* that contains *addr*, call *callback_fn* * function with *task*, *vma*, and *callback_ctx*. * The *callback_fn* should be a static function and * the *callback_ctx* should be a pointer to the stack. * The *flags* is used to control certain aspects of the helper. * Currently, the *flags* must be 0. * * The expected callback signature is * * long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx); * * * Returns * 0 on success. * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. * **-EBUSY** if failed to try lock mmap_lock. * **-EINVAL** for invalid **flags**. */ static long (*bpf_find_vma)(struct task_struct *task, __u64 addr, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 180; /* * bpf_loop * * For **nr_loops**, call **callback_fn** function * with **callback_ctx** as the context parameter. * The **callback_fn** should be a static function and * the **callback_ctx** should be a pointer to the stack. * The **flags** is used to control certain aspects of the helper. * Currently, the **flags** must be 0. Currently, nr_loops is * limited to 1 << 23 (~8 million) loops. * * long (\*callback_fn)(u32 index, void \*ctx); * * where **index** is the current index in the loop. The index * is zero-indexed. * * If **callback_fn** returns 0, the helper will continue to the next * loop. If return value is 1, the helper will skip the rest of * the loops and return. Other return values are not used now, * and will be rejected by the verifier. * * * Returns * The number of loops performed, **-EINVAL** for invalid **flags**, * **-E2BIG** if **nr_loops** exceeds the maximum number of loops. */ static long (*bpf_loop)(__u32 nr_loops, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 181; /* * bpf_strncmp * * Do strncmp() between **s1** and **s2**. **s1** doesn't need * to be null-terminated and **s1_sz** is the maximum storage * size of **s1**. **s2** must be a read-only string. * * Returns * An integer less than, equal to, or greater than zero * if the first **s1_sz** bytes of **s1** is found to be * less than, to match, or be greater than **s2**. */ static long (*bpf_strncmp)(const char *s1, __u32 s1_sz, const char *s2) = (void *) 182; /* * bpf_get_func_arg * * Get **n**-th argument (zero based) of the traced function (for tracing programs) * returned in **value**. * * * Returns * 0 on success. * **-EINVAL** if n >= arguments count of traced function. */ static long (*bpf_get_func_arg)(void *ctx, __u32 n, __u64 *value) = (void *) 183; /* * bpf_get_func_ret * * Get return value of the traced function (for tracing programs) * in **value**. * * * Returns * 0 on success. * **-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN. */ static long (*bpf_get_func_ret)(void *ctx, __u64 *value) = (void *) 184; /* * bpf_get_func_arg_cnt * * Get number of arguments of the traced function (for tracing programs). * * * Returns * The number of arguments of the traced function. */ static long (*bpf_get_func_arg_cnt)(void *ctx) = (void *) 185; /* * bpf_get_retval * * Get the syscall's return value that will be returned to userspace. * * This helper is currently supported by cgroup programs only. * * Returns * The syscall's return value. */ static int (*bpf_get_retval)(void) = (void *) 186; /* * bpf_set_retval * * Set the syscall's return value that will be returned to userspace. * * This helper is currently supported by cgroup programs only. * * Returns * 0 on success, or a negative error in case of failure. */ static int (*bpf_set_retval)(int retval) = (void *) 187; /* * bpf_xdp_get_buff_len * * Get the total size of a given xdp buff (linear and paged area) * * Returns * The total size of a given xdp buffer. */ static __u64 (*bpf_xdp_get_buff_len)(struct xdp_md *xdp_md) = (void *) 188; /* * bpf_xdp_load_bytes * * This helper is provided as an easy way to load data from a * xdp buffer. It can be used to load *len* bytes from *offset* from * the frame associated to *xdp_md*, into the buffer pointed by * *buf*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_load_bytes)(struct xdp_md *xdp_md, __u32 offset, void *buf, __u32 len) = (void *) 189; /* * bpf_xdp_store_bytes * * Store *len* bytes from buffer *buf* into the frame * associated to *xdp_md*, at *offset*. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_xdp_store_bytes)(struct xdp_md *xdp_md, __u32 offset, void *buf, __u32 len) = (void *) 190; /* * bpf_copy_from_user_task * * Read *size* bytes from user space address *user_ptr* in *tsk*'s * address space, and stores the data in *dst*. *flags* is not * used yet and is provided for future extensibility. This helper * can only be used by sleepable programs. * * Returns * 0 on success, or a negative error in case of failure. On error * *dst* buffer is zeroed out. */ static long (*bpf_copy_from_user_task)(void *dst, __u32 size, const void *user_ptr, struct task_struct *tsk, __u64 flags) = (void *) 191; /* * bpf_skb_set_tstamp * * Change the __sk_buff->tstamp_type to *tstamp_type* * and set *tstamp* to the __sk_buff->tstamp together. * * If there is no need to change the __sk_buff->tstamp_type, * the tstamp value can be directly written to __sk_buff->tstamp * instead. * * BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that * will be kept during bpf_redirect_*(). A non zero * *tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO * *tstamp_type*. * * A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used * with a zero *tstamp*. * * Only IPv4 and IPv6 skb->protocol are supported. * * This function is most useful when it needs to set a * mono delivery time to __sk_buff->tstamp and then * bpf_redirect_*() to the egress of an iface. For example, * changing the (rcv) timestamp in __sk_buff->tstamp at * ingress to a mono delivery time and then bpf_redirect_*() * to sch_fq@phy-dev. * * Returns * 0 on success. * **-EINVAL** for invalid input * **-EOPNOTSUPP** for unsupported protocol */ static long (*bpf_skb_set_tstamp)(struct __sk_buff *skb, __u64 tstamp, __u32 tstamp_type) = (void *) 192; /* * bpf_ima_file_hash * * Returns a calculated IMA hash of the *file*. * If the hash is larger than *size*, then only *size* * bytes will be copied to *dst* * * Returns * The **hash_algo** is returned on success, * **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if * invalid arguments are passed. */ static long (*bpf_ima_file_hash)(struct file *file, void *dst, __u32 size) = (void *) 193; /* * bpf_kptr_xchg * * Exchange kptr at pointer *map_value* with *ptr*, and return the * old value. *ptr* can be NULL, otherwise it must be a referenced * pointer which will be released when this helper is called. * * Returns * The old value of kptr (which can be NULL). The returned pointer * if not NULL, is a reference which must be released using its * corresponding release function, or moved into a BPF map before * program exit. */ static void *(*bpf_kptr_xchg)(void *map_value, void *ptr) = (void *) 194; /* * bpf_map_lookup_percpu_elem * * Perform a lookup in *percpu map* for an entry associated to * *key* on *cpu*. * * Returns * Map value associated to *key* on *cpu*, or **NULL** if no entry * was found or *cpu* is invalid. */ static void *(*bpf_map_lookup_percpu_elem)(void *map, const void *key, __u32 cpu) = (void *) 195; /* * bpf_skc_to_mptcp_sock * * Dynamically cast a *sk* pointer to a *mptcp_sock* pointer. * * Returns * *sk* if casting is valid, or **NULL** otherwise. */ static struct mptcp_sock *(*bpf_skc_to_mptcp_sock)(void *sk) = (void *) 196; /* * bpf_dynptr_from_mem * * Get a dynptr to local memory *data*. * * *data* must be a ptr to a map value. * The maximum *size* supported is DYNPTR_MAX_SIZE. * *flags* is currently unused. * * Returns * 0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE, * -EINVAL if flags is not 0. */ static long (*bpf_dynptr_from_mem)(void *data, __u32 size, __u64 flags, struct bpf_dynptr *ptr) = (void *) 197; /* * bpf_ringbuf_reserve_dynptr * * Reserve *size* bytes of payload in a ring buffer *ringbuf* * through the dynptr interface. *flags* must be 0. * * Please note that a corresponding bpf_ringbuf_submit_dynptr or * bpf_ringbuf_discard_dynptr must be called on *ptr*, even if the * reservation fails. This is enforced by the verifier. * * Returns * 0 on success, or a negative error in case of failure. */ static long (*bpf_ringbuf_reserve_dynptr)(void *ringbuf, __u32 size, __u64 flags, struct bpf_dynptr *ptr) = (void *) 198; /* * bpf_ringbuf_submit_dynptr * * Submit reserved ring buffer sample, pointed to by *data*, * through the dynptr interface. This is a no-op if the dynptr is * invalid/null. * * For more information on *flags*, please see * 'bpf_ringbuf_submit'. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_submit_dynptr)(struct bpf_dynptr *ptr, __u64 flags) = (void *) 199; /* * bpf_ringbuf_discard_dynptr * * Discard reserved ring buffer sample through the dynptr * interface. This is a no-op if the dynptr is invalid/null. * * For more information on *flags*, please see * 'bpf_ringbuf_discard'. * * Returns * Nothing. Always succeeds. */ static void (*bpf_ringbuf_discard_dynptr)(struct bpf_dynptr *ptr, __u64 flags) = (void *) 200; /* * bpf_dynptr_read * * Read *len* bytes from *src* into *dst*, starting from *offset* * into *src*. * *flags* is currently unused. * * Returns * 0 on success, -E2BIG if *offset* + *len* exceeds the length * of *src*'s data, -EINVAL if *src* is an invalid dynptr or if * *flags* is not 0. */ static long (*bpf_dynptr_read)(void *dst, __u32 len, struct bpf_dynptr *src, __u32 offset, __u64 flags) = (void *) 201; /* * bpf_dynptr_write * * Write *len* bytes from *src* into *dst*, starting from *offset* * into *dst*. * *flags* is currently unused. * * Returns * 0 on success, -E2BIG if *offset* + *len* exceeds the length * of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst* * is a read-only dynptr or if *flags* is not 0. */ static long (*bpf_dynptr_write)(struct bpf_dynptr *dst, __u32 offset, void *src, __u32 len, __u64 flags) = (void *) 202; /* * bpf_dynptr_data * * Get a pointer to the underlying dynptr data. * * *len* must be a statically known value. The returned data slice * is invalidated whenever the dynptr is invalidated. * * Returns * Pointer to the underlying dynptr data, NULL if the dynptr is * read-only, if the dynptr is invalid, or if the offset and length * is out of bounds. */ static void *(*bpf_dynptr_data)(struct bpf_dynptr *ptr, __u32 offset, __u32 len) = (void *) 203; /* * bpf_tcp_raw_gen_syncookie_ipv4 * * Try to issue a SYN cookie for the packet with corresponding * IPv4/TCP headers, *iph* and *th*, without depending on a * listening socket. * * *iph* points to the IPv4 header. * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** if *th_len* is invalid. */ static __s64 (*bpf_tcp_raw_gen_syncookie_ipv4)(struct iphdr *iph, struct tcphdr *th, __u32 th_len) = (void *) 204; /* * bpf_tcp_raw_gen_syncookie_ipv6 * * Try to issue a SYN cookie for the packet with corresponding * IPv6/TCP headers, *iph* and *th*, without depending on a * listening socket. * * *iph* points to the IPv6 header. * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * * Returns * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** if *th_len* is invalid. * * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. */ static __s64 (*bpf_tcp_raw_gen_syncookie_ipv6)(struct ipv6hdr *iph, struct tcphdr *th, __u32 th_len) = (void *) 205; /* * bpf_tcp_raw_check_syncookie_ipv4 * * Check whether *iph* and *th* contain a valid SYN cookie ACK * without depending on a listening socket. * * *iph* points to the IPv4 header. * * *th* points to the TCP header. * * Returns * 0 if *iph* and *th* are a valid SYN cookie ACK. * * On failure, the returned value is one of the following: * * **-EACCES** if the SYN cookie is not valid. */ static long (*bpf_tcp_raw_check_syncookie_ipv4)(struct iphdr *iph, struct tcphdr *th) = (void *) 206; /* * bpf_tcp_raw_check_syncookie_ipv6 * * Check whether *iph* and *th* contain a valid SYN cookie ACK * without depending on a listening socket. * * *iph* points to the IPv6 header. * * *th* points to the TCP header. * * Returns * 0 if *iph* and *th* are a valid SYN cookie ACK. * * On failure, the returned value is one of the following: * * **-EACCES** if the SYN cookie is not valid. * * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. */ static long (*bpf_tcp_raw_check_syncookie_ipv6)(struct ipv6hdr *iph, struct tcphdr *th) = (void *) 207; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/bpf_headers/bpf_helpers.h������������������������������������������������0000664�0000000�0000000�00000023732�15003540030�0022545�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_HELPERS__ #define __BPF_HELPERS__ /* * Note that bpf programs need to include either * vmlinux.h (auto-generated from BTF) or linux/types.h * in advance since bpf_helper_defs.h uses such types * as __u64. */ #include "bpf_helper_defs.h" #define __uint(name, val) int (*name)[val] #define __type(name, val) typeof(val) *name #define __array(name, val) typeof(val) *name[] /* * Helper macro to place programs, maps, license in * different sections in elf_bpf file. Section names * are interpreted by libbpf depending on the context (BPF programs, BPF maps, * extern variables, etc). * To allow use of SEC() with externs (e.g., for extern .maps declarations), * make sure __attribute__((unused)) doesn't trigger compilation warning. */ #if __GNUC__ && !__clang__ /* * Pragma macros are broken on GCC * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90400 */ #define SEC(name) __attribute__((section(name), used)) #else #define SEC(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ __attribute__((section(name), used)) \ _Pragma("GCC diagnostic pop") \ #endif /* Avoid 'linux/stddef.h' definition of '__always_inline'. */ #undef __always_inline #define __always_inline inline __attribute__((always_inline)) #ifndef __noinline #define __noinline __attribute__((noinline)) #endif #ifndef __weak #define __weak __attribute__((weak)) #endif /* * Use __hidden attribute to mark a non-static BPF subprogram effectively * static for BPF verifier's verification algorithm purposes, allowing more * extensive and permissive BPF verification process, taking into account * subprogram's caller context. */ #define __hidden __attribute__((visibility("hidden"))) /* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include * any system-level headers (such as stddef.h, linux/version.h, etc), and * commonly-used macros like NULL and KERNEL_VERSION aren't available through * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define * them on their own. So as a convenience, provide such definitions here. */ #ifndef NULL #define NULL ((void *)0) #endif #ifndef KERNEL_VERSION #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) #endif /* * Helper macros to manipulate data structures */ #ifndef offsetof #define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) #endif #ifndef container_of #define container_of(ptr, type, member) \ ({ \ void *__mptr = (void *)(ptr); \ ((type *)(__mptr - offsetof(type, member))); \ }) #endif /* * Compiler (optimization) barrier. */ #ifndef barrier #define barrier() asm volatile("" ::: "memory") #endif /* Variable-specific compiler (optimization) barrier. It's a no-op which makes * compiler believe that there is some black box modification of a given * variable and thus prevents compiler from making extra assumption about its * value and potential simplifications and optimizations on this variable. * * E.g., compiler might often delay or even omit 32-bit to 64-bit casting of * a variable, making some code patterns unverifiable. Putting barrier_var() * in place will ensure that cast is performed before the barrier_var() * invocation, because compiler has to pessimistically assume that embedded * asm section might perform some extra operations on that variable. * * This is a variable-specific variant of more global barrier(). */ #ifndef barrier_var #define barrier_var(var) asm volatile("" : "=r"(var) : "0"(var)) #endif /* * Helper macro to throw a compilation error if __bpf_unreachable() gets * built into the resulting code. This works given BPF back end does not * implement __builtin_trap(). This is useful to assert that certain paths * of the program code are never used and hence eliminated by the compiler. * * For example, consider a switch statement that covers known cases used by * the program. __bpf_unreachable() can then reside in the default case. If * the program gets extended such that a case is not covered in the switch * statement, then it will throw a build error due to the default case not * being compiled out. */ #ifndef __bpf_unreachable # define __bpf_unreachable() __builtin_trap() #endif /* * Helper function to perform a tail call with a constant/immediate map slot. */ #if __clang_major__ >= 8 && defined(__bpf__) static __always_inline void bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) { if (!__builtin_constant_p(slot)) __bpf_unreachable(); /* * Provide a hard guarantee that LLVM won't optimize setting r2 (map * pointer) and r3 (constant map index) from _different paths_ ending * up at the _same_ call insn as otherwise we won't be able to use the * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key * tracking for prog array pokes") for details on verifier tracking. * * Note on clobber list: we need to stay in-line with BPF calling * convention, so even if we don't end up using r0, r4, r5, we need * to mark them as clobber so that LLVM doesn't end up using them * before / after the call. */ asm volatile("r1 = %[ctx]\n\t" "r2 = %[map]\n\t" "r3 = %[slot]\n\t" "call 12" :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) : "r0", "r1", "r2", "r3", "r4", "r5"); } #endif /* * Helper structure used by eBPF C program * to describe BPF map attributes to libbpf loader */ struct bpf_map_defold { unsigned int type; unsigned int key_size; unsigned int value_size; unsigned int max_entries; unsigned int map_flags; } __attribute__((deprecated("use BTF-defined maps in .maps section"))); enum libbpf_pin_type { LIBBPF_PIN_NONE, /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ LIBBPF_PIN_BY_NAME, }; enum libbpf_tristate { TRI_NO = 0, TRI_YES = 1, TRI_MODULE = 2, }; #define __kconfig __attribute__((section(".kconfig"))) #define __ksym __attribute__((section(".ksyms"))) #define __kptr __attribute__((btf_type_tag("kptr"))) #define __kptr_ref __attribute__((btf_type_tag("kptr_ref"))) #ifndef ___bpf_concat #define ___bpf_concat(a, b) a ## b #endif #ifndef ___bpf_apply #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) #endif #ifndef ___bpf_nth #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N #endif #ifndef ___bpf_narg #define ___bpf_narg(...) \ ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #endif #define ___bpf_fill0(arr, p, x) do {} while (0) #define ___bpf_fill1(arr, p, x) arr[p] = x #define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args) #define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args) #define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args) #define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args) #define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args) #define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args) #define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args) #define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args) #define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args) #define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args) #define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args) #define ___bpf_fill(arr, args...) \ ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args) /* * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values * in a structure. */ #define BPF_SEQ_PRINTF(seq, fmt, args...) \ ({ \ static const char ___fmt[] = fmt; \ unsigned long long ___param[___bpf_narg(args)]; \ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ ___bpf_fill(___param, args); \ _Pragma("GCC diagnostic pop") \ \ bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \ ___param, sizeof(___param)); \ }) /* * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of * an array of u64. */ #define BPF_SNPRINTF(out, out_size, fmt, args...) \ ({ \ static const char ___fmt[] = fmt; \ unsigned long long ___param[___bpf_narg(args)]; \ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ ___bpf_fill(___param, args); \ _Pragma("GCC diagnostic pop") \ \ bpf_snprintf(out, out_size, ___fmt, \ ___param, sizeof(___param)); \ }) #ifdef BPF_NO_GLOBAL_DATA #define BPF_PRINTK_FMT_MOD #else #define BPF_PRINTK_FMT_MOD static const #endif #define __bpf_printk(fmt, ...) \ ({ \ BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \ bpf_trace_printk(____fmt, sizeof(____fmt), \ ##__VA_ARGS__); \ }) /* * __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments * instead of an array of u64. */ #define __bpf_vprintk(fmt, args...) \ ({ \ static const char ___fmt[] = fmt; \ unsigned long long ___param[___bpf_narg(args)]; \ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ ___bpf_fill(___param, args); \ _Pragma("GCC diagnostic pop") \ \ bpf_trace_vprintk(___fmt, sizeof(___fmt), \ ___param, sizeof(___param)); \ }) /* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args * Otherwise use __bpf_vprintk */ #define ___bpf_pick_printk(...) \ ___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ __bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\ __bpf_printk /*1*/, __bpf_printk /*0*/) /* Helper macro to print out debug messages */ #define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args) #endif ��������������������������������������opensnitch-1.6.9/ebpf_prog/bpf_headers/bpf_tracing.h������������������������������������������������0000664�0000000�0000000�00000053242�15003540030�0022531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_TRACING_H__ #define __BPF_TRACING_H__ #include "bpf_helpers.h" /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ #if defined(__TARGET_ARCH_x86) #define bpf_target_x86 #define bpf_target_defined #elif defined(__TARGET_ARCH_s390) #define bpf_target_s390 #define bpf_target_defined #elif defined(__TARGET_ARCH_arm) #define bpf_target_arm #define bpf_target_defined #elif defined(__TARGET_ARCH_arm64) #define bpf_target_arm64 #define bpf_target_defined #elif defined(__TARGET_ARCH_mips) #define bpf_target_mips #define bpf_target_defined #elif defined(__TARGET_ARCH_powerpc) #define bpf_target_powerpc #define bpf_target_defined #elif defined(__TARGET_ARCH_sparc) #define bpf_target_sparc #define bpf_target_defined #elif defined(__TARGET_ARCH_riscv) #define bpf_target_riscv #define bpf_target_defined #elif defined(__TARGET_ARCH_arc) #define bpf_target_arc #define bpf_target_defined #else /* Fall back to what the compiler says */ #if defined(__x86_64__) #define bpf_target_x86 #define bpf_target_defined #elif defined(__s390__) #define bpf_target_s390 #define bpf_target_defined #elif defined(__arm__) #define bpf_target_arm #define bpf_target_defined #elif defined(__aarch64__) #define bpf_target_arm64 #define bpf_target_defined #elif defined(__mips__) #define bpf_target_mips #define bpf_target_defined #elif defined(__powerpc__) #define bpf_target_powerpc #define bpf_target_defined #elif defined(__sparc__) #define bpf_target_sparc #define bpf_target_defined #elif defined(__riscv) && __riscv_xlen == 64 #define bpf_target_riscv #define bpf_target_defined #elif defined(__arc__) #define bpf_target_arc #define bpf_target_defined #endif /* no compiler target */ #endif #ifndef __BPF_TARGET_MISSING #define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\"" #endif #if defined(bpf_target_x86) #if defined(__KERNEL__) || defined(__VMLINUX_H__) #define __PT_PARM1_REG di #define __PT_PARM2_REG si #define __PT_PARM3_REG dx #define __PT_PARM4_REG cx #define __PT_PARM5_REG r8 #define __PT_RET_REG sp #define __PT_FP_REG bp #define __PT_RC_REG ax #define __PT_SP_REG sp #define __PT_IP_REG ip /* syscall uses r10 for PARM4 */ #define PT_REGS_PARM4_SYSCALL(x) ((x)->r10) #define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(x, r10) #else #ifdef __i386__ #define __PT_PARM1_REG eax #define __PT_PARM2_REG edx #define __PT_PARM3_REG ecx /* i386 kernel is built with -mregparm=3 */ #define __PT_PARM4_REG __unsupported__ #define __PT_PARM5_REG __unsupported__ #define __PT_RET_REG esp #define __PT_FP_REG ebp #define __PT_RC_REG eax #define __PT_SP_REG esp #define __PT_IP_REG eip #else /* __i386__ */ #define __PT_PARM1_REG rdi #define __PT_PARM2_REG rsi #define __PT_PARM3_REG rdx #define __PT_PARM4_REG rcx #define __PT_PARM5_REG r8 #define __PT_RET_REG rsp #define __PT_FP_REG rbp #define __PT_RC_REG rax #define __PT_SP_REG rsp #define __PT_IP_REG rip /* syscall uses r10 for PARM4 */ #define PT_REGS_PARM4_SYSCALL(x) ((x)->r10) #define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(x, r10) #endif /* __i386__ */ #endif /* __KERNEL__ || __VMLINUX_H__ */ #elif defined(bpf_target_s390) struct pt_regs___s390 { unsigned long orig_gpr2; }; /* s390 provides user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const user_pt_regs *)(x)) #define __PT_PARM1_REG gprs[2] #define __PT_PARM2_REG gprs[3] #define __PT_PARM3_REG gprs[4] #define __PT_PARM4_REG gprs[5] #define __PT_PARM5_REG gprs[6] #define __PT_RET_REG grps[14] #define __PT_FP_REG gprs[11] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG gprs[2] #define __PT_SP_REG gprs[15] #define __PT_IP_REG psw.addr #define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x) #define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ((const struct pt_regs___s390 *)(x), orig_gpr2) #elif defined(bpf_target_arm) #define __PT_PARM1_REG uregs[0] #define __PT_PARM2_REG uregs[1] #define __PT_PARM3_REG uregs[2] #define __PT_PARM4_REG uregs[3] #define __PT_PARM5_REG uregs[4] #define __PT_RET_REG uregs[14] #define __PT_FP_REG uregs[11] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG uregs[0] #define __PT_SP_REG uregs[13] #define __PT_IP_REG uregs[12] #elif defined(bpf_target_arm64) struct pt_regs___arm64 { unsigned long orig_x0; }; /* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x)) #define __PT_PARM1_REG regs[0] #define __PT_PARM2_REG regs[1] #define __PT_PARM3_REG regs[2] #define __PT_PARM4_REG regs[3] #define __PT_PARM5_REG regs[4] #define __PT_RET_REG regs[30] #define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG regs[0] #define __PT_SP_REG sp #define __PT_IP_REG pc #define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x) #define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ((const struct pt_regs___arm64 *)(x), orig_x0) #elif defined(bpf_target_mips) #define __PT_PARM1_REG regs[4] #define __PT_PARM2_REG regs[5] #define __PT_PARM3_REG regs[6] #define __PT_PARM4_REG regs[7] #define __PT_PARM5_REG regs[8] #define __PT_RET_REG regs[31] #define __PT_FP_REG regs[30] /* Works only with CONFIG_FRAME_POINTER */ #define __PT_RC_REG regs[2] #define __PT_SP_REG regs[29] #define __PT_IP_REG cp0_epc #elif defined(bpf_target_powerpc) #define __PT_PARM1_REG gpr[3] #define __PT_PARM2_REG gpr[4] #define __PT_PARM3_REG gpr[5] #define __PT_PARM4_REG gpr[6] #define __PT_PARM5_REG gpr[7] #define __PT_RET_REG regs[31] #define __PT_FP_REG __unsupported__ #define __PT_RC_REG gpr[3] #define __PT_SP_REG sp #define __PT_IP_REG nip /* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ctx #elif defined(bpf_target_sparc) #define __PT_PARM1_REG u_regs[UREG_I0] #define __PT_PARM2_REG u_regs[UREG_I1] #define __PT_PARM3_REG u_regs[UREG_I2] #define __PT_PARM4_REG u_regs[UREG_I3] #define __PT_PARM5_REG u_regs[UREG_I4] #define __PT_RET_REG u_regs[UREG_I7] #define __PT_FP_REG __unsupported__ #define __PT_RC_REG u_regs[UREG_I0] #define __PT_SP_REG u_regs[UREG_FP] /* Should this also be a bpf_target check for the sparc case? */ #if defined(__arch64__) #define __PT_IP_REG tpc #else #define __PT_IP_REG pc #endif #elif defined(bpf_target_riscv) #define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) #define __PT_PARM1_REG a0 #define __PT_PARM2_REG a1 #define __PT_PARM3_REG a2 #define __PT_PARM4_REG a3 #define __PT_PARM5_REG a4 #define __PT_RET_REG ra #define __PT_FP_REG s0 #define __PT_RC_REG a0 #define __PT_SP_REG sp #define __PT_IP_REG pc /* riscv does not select ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ctx #elif defined(bpf_target_arc) /* arc provides struct user_pt_regs instead of struct pt_regs to userspace */ #define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) #define __PT_PARM1_REG scratch.r0 #define __PT_PARM2_REG scratch.r1 #define __PT_PARM3_REG scratch.r2 #define __PT_PARM4_REG scratch.r3 #define __PT_PARM5_REG scratch.r4 #define __PT_RET_REG scratch.blink #define __PT_FP_REG __unsupported__ #define __PT_RC_REG scratch.r0 #define __PT_SP_REG scratch.sp #define __PT_IP_REG scratch.ret /* arc does not select ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ctx #endif #if defined(bpf_target_defined) struct pt_regs; /* allow some architecutres to override `struct pt_regs` */ #ifndef __PT_REGS_CAST #define __PT_REGS_CAST(x) (x) #endif #define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG) #define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG) #define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG) #define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG) #define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG) #define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG) #define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG) #define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG) #define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) #define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_REG) #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_REG) #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_REG) #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_REG) #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_REG) #define PT_REGS_RET_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RET_REG) #define PT_REGS_FP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_FP_REG) #define PT_REGS_RC_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RC_REG) #define PT_REGS_SP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_SP_REG) #define PT_REGS_IP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_IP_REG) #if defined(bpf_target_powerpc) #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP #elif defined(bpf_target_sparc) #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP #else #define BPF_KPROBE_READ_RET_IP(ip, ctx) \ ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \ ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) #endif #ifndef PT_REGS_PARM1_SYSCALL #define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1(x) #endif #define PT_REGS_PARM2_SYSCALL(x) PT_REGS_PARM2(x) #define PT_REGS_PARM3_SYSCALL(x) PT_REGS_PARM3(x) #ifndef PT_REGS_PARM4_SYSCALL #define PT_REGS_PARM4_SYSCALL(x) PT_REGS_PARM4(x) #endif #define PT_REGS_PARM5_SYSCALL(x) PT_REGS_PARM5(x) #ifndef PT_REGS_PARM1_CORE_SYSCALL #define PT_REGS_PARM1_CORE_SYSCALL(x) PT_REGS_PARM1_CORE(x) #endif #define PT_REGS_PARM2_CORE_SYSCALL(x) PT_REGS_PARM2_CORE(x) #define PT_REGS_PARM3_CORE_SYSCALL(x) PT_REGS_PARM3_CORE(x) #ifndef PT_REGS_PARM4_CORE_SYSCALL #define PT_REGS_PARM4_CORE_SYSCALL(x) PT_REGS_PARM4_CORE(x) #endif #define PT_REGS_PARM5_CORE_SYSCALL(x) PT_REGS_PARM5_CORE(x) #else /* defined(bpf_target_defined) */ #define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM1_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM2_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM3_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM4_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #define PT_REGS_PARM5_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) #endif /* defined(bpf_target_defined) */ /* * When invoked from a syscall handler kprobe, returns a pointer to a * struct pt_regs containing syscall arguments and suitable for passing to * PT_REGS_PARMn_SYSCALL() and PT_REGS_PARMn_CORE_SYSCALL(). */ #ifndef PT_REGS_SYSCALL_REGS /* By default, assume that the arch selects ARCH_HAS_SYSCALL_WRAPPER. */ #define PT_REGS_SYSCALL_REGS(ctx) ((struct pt_regs *)PT_REGS_PARM1(ctx)) #endif #ifndef ___bpf_concat #define ___bpf_concat(a, b) a ## b #endif #ifndef ___bpf_apply #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) #endif #ifndef ___bpf_nth #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N #endif #ifndef ___bpf_narg #define ___bpf_narg(...) ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #endif #define ___bpf_ctx_cast0() ctx #define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0] #define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1] #define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2] #define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3] #define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4] #define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5] #define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6] #define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7] #define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8] #define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9] #define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10] #define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11] #define ___bpf_ctx_cast(args...) ___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args) /* * BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and * similar kinds of BPF programs, that accept input arguments as a single * pointer to untyped u64 array, where each u64 can actually be a typed * pointer or integer of different size. Instead of requring user to write * manual casts and work with array elements by index, BPF_PROG macro * allows user to declare a list of named and typed input arguments in the * same syntax as for normal C function. All the casting is hidden and * performed transparently, while user code can just assume working with * function arguments of specified type and name. * * Original raw context argument is preserved as well as 'ctx' argument. * This is useful when using BPF helpers that expect original context * as one of the parameters (e.g., for bpf_perf_event_output()). */ #define BPF_PROG(name, args...) \ name(unsigned long long *ctx); \ static __attribute__((always_inline)) typeof(name(0)) \ ____##name(unsigned long long *ctx, ##args); \ typeof(name(0)) name(unsigned long long *ctx) \ { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ return ____##name(___bpf_ctx_cast(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __attribute__((always_inline)) typeof(name(0)) \ ____##name(unsigned long long *ctx, ##args) struct pt_regs; #define ___bpf_kprobe_args0() ctx #define ___bpf_kprobe_args1(x) ___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx) #define ___bpf_kprobe_args2(x, args...) ___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx) #define ___bpf_kprobe_args3(x, args...) ___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx) #define ___bpf_kprobe_args4(x, args...) ___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx) #define ___bpf_kprobe_args5(x, args...) ___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx) #define ___bpf_kprobe_args(args...) ___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args) /* * BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for * tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific * low-level way of getting kprobe input arguments from struct pt_regs, and * provides a familiar typed and named function arguments syntax and * semantics of accessing kprobe input paremeters. * * Original struct pt_regs* context is preserved as 'ctx' argument. This might * be necessary when using BPF helpers like bpf_perf_event_output(). */ #define BPF_KPROBE(name, args...) \ name(struct pt_regs *ctx); \ static __attribute__((always_inline)) typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args); \ typeof(name(0)) name(struct pt_regs *ctx) \ { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ return ____##name(___bpf_kprobe_args(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __attribute__((always_inline)) typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args) #define ___bpf_kretprobe_args0() ctx #define ___bpf_kretprobe_args1(x) ___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx) #define ___bpf_kretprobe_args(args...) ___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args) /* * BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional * return value (in addition to `struct pt_regs *ctx`), but no input * arguments, because they will be clobbered by the time probed function * returns. */ #define BPF_KRETPROBE(name, args...) \ name(struct pt_regs *ctx); \ static __attribute__((always_inline)) typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args); \ typeof(name(0)) name(struct pt_regs *ctx) \ { \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ return ____##name(___bpf_kretprobe_args(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args) /* If kernel has CONFIG_ARCH_HAS_SYSCALL_WRAPPER, read pt_regs directly */ #define ___bpf_syscall_args0() ctx #define ___bpf_syscall_args1(x) ___bpf_syscall_args0(), (void *)PT_REGS_PARM1_SYSCALL(regs) #define ___bpf_syscall_args2(x, args...) ___bpf_syscall_args1(args), (void *)PT_REGS_PARM2_SYSCALL(regs) #define ___bpf_syscall_args3(x, args...) ___bpf_syscall_args2(args), (void *)PT_REGS_PARM3_SYSCALL(regs) #define ___bpf_syscall_args4(x, args...) ___bpf_syscall_args3(args), (void *)PT_REGS_PARM4_SYSCALL(regs) #define ___bpf_syscall_args5(x, args...) ___bpf_syscall_args4(args), (void *)PT_REGS_PARM5_SYSCALL(regs) #define ___bpf_syscall_args(args...) ___bpf_apply(___bpf_syscall_args, ___bpf_narg(args))(args) /* If kernel doesn't have CONFIG_ARCH_HAS_SYSCALL_WRAPPER, we have to BPF_CORE_READ from pt_regs */ #define ___bpf_syswrap_args0() ctx #define ___bpf_syswrap_args1(x) ___bpf_syswrap_args0(), (void *)PT_REGS_PARM1_CORE_SYSCALL(regs) #define ___bpf_syswrap_args2(x, args...) ___bpf_syswrap_args1(args), (void *)PT_REGS_PARM2_CORE_SYSCALL(regs) #define ___bpf_syswrap_args3(x, args...) ___bpf_syswrap_args2(args), (void *)PT_REGS_PARM3_CORE_SYSCALL(regs) #define ___bpf_syswrap_args4(x, args...) ___bpf_syswrap_args3(args), (void *)PT_REGS_PARM4_CORE_SYSCALL(regs) #define ___bpf_syswrap_args5(x, args...) ___bpf_syswrap_args4(args), (void *)PT_REGS_PARM5_CORE_SYSCALL(regs) #define ___bpf_syswrap_args(args...) ___bpf_apply(___bpf_syswrap_args, ___bpf_narg(args))(args) /* * BPF_KSYSCALL is a variant of BPF_KPROBE, which is intended for * tracing syscall functions, like __x64_sys_close. It hides the underlying * platform-specific low-level way of getting syscall input arguments from * struct pt_regs, and provides a familiar typed and named function arguments * syntax and semantics of accessing syscall input parameters. * * Original struct pt_regs * context is preserved as 'ctx' argument. This might * be necessary when using BPF helpers like bpf_perf_event_output(). * * At the moment BPF_KSYSCALL does not transparently handle all the calling * convention quirks for the following syscalls: * * - mmap(): __ARCH_WANT_SYS_OLD_MMAP. * - clone(): CONFIG_CLONE_BACKWARDS, CONFIG_CLONE_BACKWARDS2 and * CONFIG_CLONE_BACKWARDS3. * - socket-related syscalls: __ARCH_WANT_SYS_SOCKETCALL. * - compat syscalls. * * This may or may not change in the future. User needs to take extra measures * to handle such quirks explicitly, if necessary. * * This macro relies on BPF CO-RE support and virtual __kconfig externs. */ #define BPF_KSYSCALL(name, args...) \ name(struct pt_regs *ctx); \ extern _Bool LINUX_HAS_SYSCALL_WRAPPER __kconfig; \ static __attribute__((always_inline)) typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args); \ typeof(name(0)) name(struct pt_regs *ctx) \ { \ struct pt_regs *regs = LINUX_HAS_SYSCALL_WRAPPER \ ? (struct pt_regs *)PT_REGS_PARM1(ctx) \ : ctx; \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ if (LINUX_HAS_SYSCALL_WRAPPER) \ return ____##name(___bpf_syswrap_args(args)); \ else \ return ____##name(___bpf_syscall_args(args)); \ _Pragma("GCC diagnostic pop") \ } \ static __attribute__((always_inline)) typeof(name(0)) \ ____##name(struct pt_regs *ctx, ##args) #define BPF_KPROBE_SYSCALL BPF_KSYSCALL #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/common.h�����������������������������������������������������������������0000664�0000000�0000000�00000004212�15003540030�0017272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef OPENSNITCH_COMMON_H #define OPENSNITCH_COMMON_H #include "common_defs.h" //https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/limits.h#L13 #ifndef MAX_PATH_LEN #define MAX_PATH_LEN 4096 #endif //https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/binfmts.h#L16 #define MAX_CMDLINE_LEN 4096 // max args that I've been able to use before hitting the error: // "dereference of modified ctx ptr disallowed" #define MAX_ARGS 20 #define MAX_ARG_SIZE 256 // flags to indicate if we were able to read all the cmdline arguments, // or if one of the arguments is >= MAX_ARG_SIZE, or there more than MAX_ARGS #define COMPLETE_ARGS 0 #define INCOMPLETE_ARGS 1 #ifndef TASK_COMM_LEN #define TASK_COMM_LEN 16 #endif #define BUF_SIZE_MAP_NS 256 #define GLOBAL_MAP_NS "256" enum events_type { EVENT_NONE = 0, EVENT_EXEC, EVENT_EXECVEAT, EVENT_FORK, EVENT_SCHED_EXIT, }; struct trace_ev_common { short common_type; char common_flags; char common_preempt_count; int common_pid; }; struct trace_sys_enter_execve { struct trace_ev_common ext; int __syscall_nr; char *filename; const char *const *argv; const char *const *envp; }; struct trace_sys_enter_execveat { struct trace_ev_common ext; int __syscall_nr; char *filename; const char *const *argv; const char *const *envp; int flags; }; struct trace_sys_exit_execve { struct trace_ev_common ext; int __syscall_nr; long ret; }; struct data_t { u64 type; u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel) u32 uid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel) u32 ppid; u32 ret_code; u8 args_count; u8 args_partial; char filename[MAX_PATH_LEN]; char args[MAX_ARGS][MAX_ARG_SIZE]; char comm[TASK_COMM_LEN]; u16 pad1; u32 pad2; }; //----------------------------------------------------------------------------- // maps struct bpf_map_def SEC("maps/heapstore") heapstore = { .type = BPF_MAP_TYPE_PERCPU_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(struct data_t), .max_entries = 1 }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/common_defs.h������������������������������������������������������������0000664�0000000�0000000�00000001626�15003540030�0020301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef OPENSNITCH_COMMON_DEFS_H #define OPENSNITCH_COMMON_DEFS_H #include <linux/sched.h> #include <linux/ptrace.h> #include <uapi/linux/bpf.h> #include "bpf_headers/bpf_helpers.h" #include "bpf_headers/bpf_tracing.h" //#include <bpf/bpf_core_read.h> #define BUF_SIZE_MAP_NS 256 #define MAPSIZE 12000 // even though we only need 32 bits of pid, on x86_32 ebpf verifier complained when pid type was set to u32 typedef u64 pid_size_t; typedef u64 uid_size_t; //-------------------------------map definitions // which github.com/iovisor/gobpf/elf expects typedef struct bpf_map_def { unsigned int type; unsigned int key_size; unsigned int value_size; unsigned int max_entries; unsigned int map_flags; unsigned int pinning; char namespace[BUF_SIZE_MAP_NS]; } bpf_map_def; enum bpf_pin_type { PIN_NONE = 0, PIN_OBJECT_NS, PIN_GLOBAL_NS, PIN_CUSTOM_NS, }; //----------------------------------- #endif ����������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/opensnitch-dns.c���������������������������������������������������������0000664�0000000�0000000�00000016276�15003540030�0020746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2022 calesanz // 2023-2024 Gustavo Iñiguez Goya // // This file is part of OpenSnitch. // // OpenSnitch 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. // // OpenSnitch 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 for more details. // // You should have received a copy of the GNU General Public License // along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. */ #define KBUILD_MODNAME "opensnitch-dns" #include <linux/in.h> #include <linux/in6.h> #include <linux/ptrace.h> #include <linux/sched.h> #include <net/sock.h> #include <uapi/linux/bpf.h> #include <uapi/linux/tcp.h> #include "common_defs.h" #include "bpf_headers/bpf_helpers.h" #include "bpf_headers/bpf_tracing.h" //----------------------------------- // random values #define MAX_ALIASES 5 #define MAX_IPS 30 struct nameLookupEvent { u32 addr_type; u8 ip[16]; char host[252]; } __attribute__((packed)); struct hostent { char *h_name; /* Official name of host. */ char **h_aliases; /* Alias list. */ int h_addrtype; /* Host address type. */ int h_length; /* Length of address. */ char **h_addr_list; /* List of addresses from name server. */ #ifdef __USE_MISC #define h_addr h_addr_list[0] /* Address, for backward compatibility.*/ #endif }; struct addrinfo { int ai_flags; /* Input flags. */ int ai_family; /* Protocol family for socket. */ int ai_socktype; /* Socket type. */ int ai_protocol; /* Protocol for socket. */ size_t ai_addrlen; /* Length of socket address. */ struct sockaddr *ai_addr; /* Socket address for socket. */ char *ai_canonname; /* Canonical name for service location. */ struct addrinfo *ai_next; /* Pointer to next in list. */ }; struct addrinfo_args_cache { struct addrinfo **addrinfo_ptr; char node[256]; }; // define temporary array for data struct bpf_map_def SEC("maps/addrinfo_args_hash") addrinfo_args_hash = { .type = BPF_MAP_TYPE_HASH, .max_entries = 256, // max entries at any time .key_size = sizeof(u32), .value_size = sizeof(struct addrinfo_args_cache), }; // BPF output events struct bpf_map_def SEC("maps/events") events = { .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u32), .max_entries = 256, // max cpus }; /** * Hooks gethostbyname calls and emits multiple nameLookupEvent events. * It supports at most MAX_IPS many addresses. */ SEC("uretprobe/gethostbyname") int uretprobe__gethostbyname(struct pt_regs *ctx) { // bpf_tracing_prinkt("Called gethostbyname %d\n",1); struct nameLookupEvent data = {0}; if (!PT_REGS_RC(ctx)) return 0; struct hostent *host = (struct hostent *)PT_REGS_RC(ctx); char * hostnameptr = {0}; bpf_probe_read(&hostnameptr, sizeof(hostnameptr), &host->h_name); bpf_probe_read_str(&data.host, sizeof(data.host), hostnameptr); char **ips = {0}; bpf_probe_read(&ips, sizeof(ips), &host->h_addr_list); #pragma clang loop unroll(full) for (int i = 0; i < MAX_IPS; i++) { char *ip={0}; bpf_probe_read(&ip, sizeof(ip), &ips[i]); if (ip == NULL) { return 0; } bpf_probe_read_user(&data.addr_type, sizeof(data.addr_type), &host->h_addrtype); if (data.addr_type == AF_INET) { // Only copy the 4 relevant bytes bpf_probe_read_user(&data.ip, 4, ip); } else { bpf_probe_read_user(&data.ip, sizeof(data.ip), ip); } bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); // char **alias = host->h_aliases; char **aliases = {0}; bpf_probe_read(&aliases, sizeof(aliases), &host->h_aliases); #pragma clang loop unroll(full) for (int j = 0; j < MAX_ALIASES; j++) { char *alias = {0}; bpf_probe_read(&alias, sizeof(alias), &aliases[j]); if (alias == NULL) { return 0; } bpf_probe_read_user(&data.host, sizeof(data.host), alias); bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); } } return 0; } // capture getaddrinfo call and store the relevant arguments to a hash. SEC("uprobe/getaddrinfo") int addrinfo(struct pt_regs *ctx) { struct addrinfo_args_cache addrinfo_args = {0}; if (!PT_REGS_PARM1(ctx)) return 0; if (!PT_REGS_PARM4(ctx)) return 0; u64 pid_tgid = bpf_get_current_pid_tgid(); u32 tid = (u32)pid_tgid; addrinfo_args.addrinfo_ptr = (struct addrinfo **)PT_REGS_PARM4(ctx); bpf_probe_read_user_str(&addrinfo_args.node, sizeof(addrinfo_args.node), (char *)PT_REGS_PARM1(ctx)); bpf_map_update_elem(&addrinfo_args_hash, &tid, &addrinfo_args, 0 /* flags */); return 0; } SEC("uretprobe/getaddrinfo") int ret_addrinfo(struct pt_regs *ctx) { struct nameLookupEvent data = {0}; struct addrinfo_args_cache *addrinfo_args = {0}; u64 pid_tgid = bpf_get_current_pid_tgid(); u32 tid = (u32)pid_tgid; addrinfo_args = bpf_map_lookup_elem(&addrinfo_args_hash, &tid); if (addrinfo_args == 0) { return 0; // missed start } struct addrinfo **res_p={0}; bpf_probe_read(&res_p, sizeof(res_p), &addrinfo_args->addrinfo_ptr); #pragma clang loop unroll(full) for (int i = 0; i < MAX_IPS; i++) { struct addrinfo *res={0}; bpf_probe_read(&res, sizeof(res), res_p); if (res == NULL) { goto out; } bpf_probe_read(&data.addr_type, sizeof(data.addr_type), &res->ai_family); if (data.addr_type == AF_INET) { struct sockaddr_in *ipv4={0}; bpf_probe_read(&ipv4, sizeof(ipv4), &res->ai_addr); // Only copy the 4 relevant bytes bpf_probe_read_user(&data.ip, 4, &ipv4->sin_addr); } else if(data.addr_type == AF_INET6) { struct sockaddr_in6 *ipv6={0}; bpf_probe_read(&ipv6, sizeof(ipv6), &res->ai_addr); bpf_probe_read_user(&data.ip, sizeof(data.ip), &ipv6->sin6_addr); } else { goto out; } bpf_probe_read_kernel_str(&data.host, sizeof(data.host), &addrinfo_args->node); bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); struct addrinfo * next={0}; bpf_probe_read(&next, sizeof(next), &res->ai_next); if (next == NULL){ goto out; } res_p = &next; } out: bpf_map_delete_elem(&addrinfo_args_hash, &tid); return 0; } char _license[] SEC("license") = "GPL"; u32 _version SEC("version") = 0xFFFFFFFE; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/opensnitch-procs.c�������������������������������������������������������0000664�0000000�0000000�00000016276�15003540030�0021310�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define KBUILD_MODNAME "opensnitch-procs" #include "common.h" struct bpf_map_def SEC("maps/proc-events") events = { // Since kernel 4.4 .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u32), .max_entries = 256, // max cpus }; struct bpf_map_def SEC("maps/execMap") execMap = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(u32), .value_size = sizeof(struct data_t), .max_entries = 256, }; static __always_inline void new_event(struct data_t* data) { // initializing variables with __builtin_memset() is required // for compatibility with bpf on kernel 4.4 struct task_struct *task; struct task_struct *parent; __builtin_memset(&task, 0, sizeof(task)); __builtin_memset(&parent, 0, sizeof(parent)); task = (struct task_struct *)bpf_get_current_task(); bpf_probe_read(&parent, sizeof(parent), &task->real_parent); data->pid = bpf_get_current_pid_tgid() >> 32; #if !defined(__arm__) && !defined(__i386__) // on i686 -> invalid read from stack bpf_probe_read(&data->ppid, sizeof(u32), &parent->tgid); #endif data->uid = bpf_get_current_uid_gid() & 0xffffffff; bpf_get_current_comm(&data->comm, sizeof(data->comm)); }; /* * send to userspace the result of the execve* call. */ static __always_inline void __handle_exit_execve(struct trace_sys_exit_execve *ctx) { u64 pid_tgid = bpf_get_current_pid_tgid(); struct data_t *proc = bpf_map_lookup_elem(&execMap, &pid_tgid); // don't delete the pid from execMap here, delegate it to sched_process_exit if (proc == NULL) { return; } if (ctx->ret != 0) { return; } proc->ret_code = ctx->ret; bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, proc, sizeof(*proc)); } // https://0xax.gitbooks.io/linux-insides/content/SysCall/linux-syscall-4.html // bprm_execve REGS_PARM3 // https://elixir.bootlin.com/linux/latest/source/fs/exec.c#L1796 SEC("tracepoint/sched/sched_process_exit") int tracepoint__sched_sched_process_exit(struct pt_regs *ctx) { u64 pid_tgid = bpf_get_current_pid_tgid(); struct data_t *proc = bpf_map_lookup_elem(&execMap, &pid_tgid); // if the pid is not in execMap cache (because it's not of a pid we've // previously intercepted), do not send the event to userspace, because // we won't do anything with it and it consumes CPU cycles (too much in some // scenarios). if (proc == NULL) { return 0; } int zero = 0; struct data_t *data = bpf_map_lookup_elem(&heapstore, &zero); if (!data){ return 0; } new_event(data); data->type = EVENT_SCHED_EXIT; bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); bpf_map_delete_elem(&execMap, &pid_tgid); return 0; }; SEC("tracepoint/syscalls/sys_exit_execve") int tracepoint__syscalls_sys_exit_execve(struct trace_sys_exit_execve *ctx) { __handle_exit_execve(ctx); return 0; }; SEC("tracepoint/syscalls/sys_exit_execveat") int tracepoint__syscalls_sys_exit_execveat(struct trace_sys_exit_execve *ctx) { __handle_exit_execve(ctx); return 0; }; SEC("tracepoint/syscalls/sys_enter_execve") int tracepoint__syscalls_sys_enter_execve(struct trace_sys_enter_execve* ctx) { int zero = 0; struct data_t *data = {0}; data = (struct data_t *)bpf_map_lookup_elem(&heapstore, &zero); if (!data){ return 0; } new_event(data); data->type = EVENT_EXEC; // bpf_probe_read_user* helpers were introduced in kernel 5.5 // Since the args can be overwritten anyway, maybe we could get them from // mm_struct instead for a wider kernel version support range? bpf_probe_read_user_str(&data->filename, sizeof(data->filename), (const char *)ctx->filename); const char *argp={0}; data->args_count = 0; data->args_partial = INCOMPLETE_ARGS; // FIXME: on i386 arch, the following code fails with permission denied. #if !defined(__arm__) && !defined(__i386__) #pragma unroll for (int i = 0; i < MAX_ARGS; i++) { bpf_probe_read_user(&argp, sizeof(argp), &ctx->argv[i]); if (!argp){ data->args_partial = COMPLETE_ARGS; break; } if (bpf_probe_read_user_str(&data->args[i], MAX_ARG_SIZE, argp) >= MAX_ARG_SIZE){ break; } data->args_count++; } #endif // FIXME: on aarch64 we fail to save the event to execMap, so send it to userspace here. #if defined(__aarch64__) bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); #else // in case of failure adding the item to the map, send it directly u64 pid_tgid = bpf_get_current_pid_tgid(); if (bpf_map_update_elem(&execMap, &pid_tgid, data, BPF_ANY) != 0) { // With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe? // BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP) // Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); } #endif return 0; }; SEC("tracepoint/syscalls/sys_enter_execveat") int tracepoint__syscalls_sys_enter_execveat(struct trace_sys_enter_execveat* ctx) { int zero = 0; struct data_t *data = {0}; data = (struct data_t *)bpf_map_lookup_elem(&heapstore, &zero); if (!data){ return 0; } new_event((void *)data); data->type = EVENT_EXECVEAT; // bpf_probe_read_user* helpers were introduced in kernel 5.5 // Since the args can be overwritten anyway, maybe we could get them from // mm_struct instead for a wider kernel version support range? bpf_probe_read_user_str(&data->filename, sizeof(data->filename), (const char *)ctx->filename); const char *argp={0}; data->args_count = 0; data->args_partial = INCOMPLETE_ARGS; // FIXME: on i386 arch, the following code fails with permission denied. #if !defined(__arm__) && !defined(__i386__) #pragma unroll for (int i = 0; i < MAX_ARGS; i++) { bpf_probe_read_user(&argp, sizeof(argp), &ctx->argv[i]); if (!argp){ data->args_partial = COMPLETE_ARGS; break; } if (bpf_probe_read_user_str(&data->args[i], MAX_ARG_SIZE, argp) >= MAX_ARG_SIZE){ break; } data->args_count++; } #endif // FIXME: on aarch64 we fail to save the event to execMap, so send it to userspace here. #if defined(__aarch64__) bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); #else // in case of failure adding the item to the map, send it directly u64 pid_tgid = bpf_get_current_pid_tgid(); if (bpf_map_update_elem(&execMap, &pid_tgid, data, BPF_ANY) != 0) { // With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe? // BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP) // Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); } #endif return 0; }; char _license[] SEC("license") = "GPL"; // this number will be interpreted by the elf loader // to set the current running kernel version u32 _version SEC("version") = 0xFFFFFFFE; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ebpf_prog/opensnitch.c�������������������������������������������������������������0000664�0000000�0000000�00000043703�15003540030�0020157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define KBUILD_MODNAME "dummy" #include "common_defs.h" #include <uapi/linux/tcp.h> #include <net/sock.h> #include <net/udp_tunnel.h> #include <net/inet_sock.h> struct tcp_key_t { u16 sport; u32 daddr; u16 dport; u32 saddr; }__attribute__((packed)); struct tcp_value_t { pid_size_t pid; uid_size_t uid; char comm[TASK_COMM_LEN]; }__attribute__((packed)); // not using unsigned __int128 because it is not supported on x86_32 struct ipV6 { u64 part1; u64 part2; }__attribute__((packed)); struct tcpv6_key_t { u16 sport; struct ipV6 daddr; u16 dport; struct ipV6 saddr; }__attribute__((packed)); struct tcpv6_value_t{ pid_size_t pid; uid_size_t uid; char comm[TASK_COMM_LEN]; }__attribute__((packed)); struct udp_key_t { u16 sport; u32 daddr; u16 dport; u32 saddr; } __attribute__((packed)); struct udp_value_t{ pid_size_t pid; uid_size_t uid; char comm[TASK_COMM_LEN]; }__attribute__((packed)); struct udpv6_key_t { u16 sport; struct ipV6 daddr; u16 dport; struct ipV6 saddr; }__attribute__((packed)); struct udpv6_value_t{ pid_size_t pid; uid_size_t uid; char comm[TASK_COMM_LEN]; }__attribute__((packed)); // on x86_32 "struct sock" is arranged differently from x86_64 (at least on Debian kernels). // We hardcode offsets of IP addresses. struct sock_on_x86_32_t { u8 data_we_dont_care_about[40]; struct ipV6 daddr; struct ipV6 saddr; }; // Add +1,+2,+3 etc. to map size helps to easier distinguish maps in bpftool's output struct bpf_map_def SEC("maps/tcpMap") tcpMap = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(struct tcp_key_t), .value_size = sizeof(struct tcp_value_t), .max_entries = MAPSIZE+1, }; struct bpf_map_def SEC("maps/tcpv6Map") tcpv6Map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(struct tcpv6_key_t), .value_size = sizeof(struct tcpv6_value_t), .max_entries = MAPSIZE+2, }; struct bpf_map_def SEC("maps/udpMap") udpMap = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(struct udp_key_t), .value_size = sizeof(struct udp_value_t), .max_entries = MAPSIZE+3, }; struct bpf_map_def SEC("maps/udpv6Map") udpv6Map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(struct udpv6_key_t), .value_size = sizeof(struct udpv6_value_t), .max_entries = MAPSIZE+4, }; // for TCP the IP-tuple can be copied from "struct sock" only upon return from tcp_connect(). // We stash the socket here to look it up upon return. struct bpf_map_def SEC("maps/tcpsock") tcpsock = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(u64), // using u64 instead of sizeof(struct sock *) // to avoid pointer size related quirks on x86_32 .value_size = sizeof(u64), .max_entries = 300, }; struct bpf_map_def SEC("maps/tcpv6sock") tcpv6sock = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(u64), .value_size = sizeof(u64), .max_entries = 300, }; struct bpf_map_def SEC("maps/icmpsock") icmpsock = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(u64), .value_size = sizeof(u64), .max_entries = 300, }; // initializing variables with __builtin_memset() is required // for compatibility with bpf on kernel 4.4 SEC("kprobe/tcp_v4_connect") int kprobe__tcp_v4_connect(struct pt_regs *ctx) { #if defined(__i386__) // On x86_32 platforms I couldn't get function arguments using PT_REGS_PARM1 // that's why we are accessing registers directly struct sock *sk = (struct sock *)((ctx)->ax); #else struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); #endif u64 skp = (u64)sk; u64 pid_tgid = bpf_get_current_pid_tgid(); bpf_map_update_elem(&tcpsock, &pid_tgid, &skp, BPF_ANY); return 0; }; SEC("kretprobe/tcp_v4_connect") int kretprobe__tcp_v4_connect(struct pt_regs *ctx) { u64 pid_tgid = bpf_get_current_pid_tgid(); u64 *skp = bpf_map_lookup_elem(&tcpsock, &pid_tgid); if (skp == NULL) {return 0;} struct sock *sk; __builtin_memset(&sk, 0, sizeof(sk)); sk = (struct sock *)*skp; struct tcp_key_t tcp_key; __builtin_memset(&tcp_key, 0, sizeof(tcp_key)); bpf_probe_read(&tcp_key.dport, sizeof(tcp_key.dport), &sk->__sk_common.skc_dport); bpf_probe_read(&tcp_key.sport, sizeof(tcp_key.sport), &sk->__sk_common.skc_num); bpf_probe_read(&tcp_key.daddr, sizeof(tcp_key.daddr), &sk->__sk_common.skc_daddr); bpf_probe_read(&tcp_key.saddr, sizeof(tcp_key.saddr), &sk->__sk_common.skc_rcv_saddr); struct tcp_value_t tcp_value={0}; __builtin_memset(&tcp_value, 0, sizeof(tcp_value)); tcp_value.pid = pid_tgid >> 32; tcp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; bpf_get_current_comm(&tcp_value.comm, sizeof(tcp_value.comm)); bpf_map_update_elem(&tcpMap, &tcp_key, &tcp_value, BPF_ANY); bpf_map_delete_elem(&tcpsock, &pid_tgid); return 0; }; SEC("kprobe/tcp_v6_connect") int kprobe__tcp_v6_connect(struct pt_regs *ctx) { #if defined(__i386__) struct sock *sk = (struct sock *)((ctx)->ax); #else struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); #endif u64 skp = (u64)sk; u64 pid_tgid = bpf_get_current_pid_tgid(); bpf_map_update_elem(&tcpv6sock, &pid_tgid, &skp, BPF_ANY); return 0; }; SEC("kretprobe/tcp_v6_connect") int kretprobe__tcp_v6_connect(struct pt_regs *ctx) { u64 pid_tgid = bpf_get_current_pid_tgid(); u64 *skp = bpf_map_lookup_elem(&tcpv6sock, &pid_tgid); if (skp == NULL) {return 0;} struct sock *sk; __builtin_memset(&sk, 0, sizeof(sk)); sk = (struct sock *)*skp; struct tcpv6_key_t tcpv6_key; __builtin_memset(&tcpv6_key, 0, sizeof(tcpv6_key)); bpf_probe_read(&tcpv6_key.dport, sizeof(tcpv6_key.dport), &sk->__sk_common.skc_dport); bpf_probe_read(&tcpv6_key.sport, sizeof(tcpv6_key.sport), &sk->__sk_common.skc_num); #if defined(__i386__) struct sock_on_x86_32_t sock; __builtin_memset(&sock, 0, sizeof(sock)); bpf_probe_read(&sock, sizeof(sock), *(&sk)); tcpv6_key.daddr = sock.daddr; tcpv6_key.saddr = sock.saddr; #else bpf_probe_read(&tcpv6_key.daddr, sizeof(tcpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); bpf_probe_read(&tcpv6_key.saddr, sizeof(tcpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); #endif struct tcpv6_value_t tcpv6_value={0}; __builtin_memset(&tcpv6_value, 0, sizeof(tcpv6_value)); tcpv6_value.pid = pid_tgid >> 32; tcpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff; bpf_get_current_comm(&tcpv6_value.comm, sizeof(tcpv6_value.comm)); bpf_map_update_elem(&tcpv6Map, &tcpv6_key, &tcpv6_value, BPF_ANY); bpf_map_delete_elem(&tcpv6sock, &pid_tgid); return 0; }; SEC("kprobe/udp_sendmsg") int kprobe__udp_sendmsg(struct pt_regs *ctx) { #if defined(__i386__) struct sock *sk = (struct sock *)((ctx)->ax); struct msghdr *msg = (struct msghdr *)((ctx)->dx); #else struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx); #endif u64 msg_name; //pointer __builtin_memset(&msg_name, 0, sizeof(msg_name)); bpf_probe_read(&msg_name, sizeof(msg_name), &msg->msg_name); struct sockaddr_in * usin = (struct sockaddr_in *)msg_name; struct udp_key_t udp_key; __builtin_memset(&udp_key, 0, sizeof(udp_key)); bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &usin->sin_port); if (udp_key.dport != 0){ //likely bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &usin->sin_addr.s_addr); } else { //very rarely dport can be found in skc_dport bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &sk->__sk_common.skc_dport); bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &sk->__sk_common.skc_daddr); } bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sk->__sk_common.skc_num); bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &sk->__sk_common.skc_rcv_saddr); // TODO: armhf #if !defined(__arm__) // extract from the ancillary message the source IP. if (udp_key.saddr == 0){ u64 cmsg=0; bpf_probe_read(&cmsg, sizeof(cmsg), &msg->msg_control); struct in_pktinfo *inpkt = (struct in_pktinfo *)CMSG_DATA(cmsg); bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &inpkt->ipi_spec_dst.s_addr); } #endif u32 zero_key = 0; __builtin_memset(&zero_key, 0, sizeof(zero_key)); struct udp_value_t *lookedupValue = bpf_map_lookup_elem(&udpMap, &udp_key); u64 pid = bpf_get_current_pid_tgid() >> 32; if (lookedupValue == NULL || lookedupValue->pid != pid) { struct udp_value_t udp_value={0}; __builtin_memset(&udp_value, 0, sizeof(udp_value)); udp_value.pid = pid; udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; bpf_get_current_comm(&udp_value.comm, sizeof(udp_value.comm)); bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); } //else nothing to do return 0; }; SEC("kprobe/udpv6_sendmsg") int kprobe__udpv6_sendmsg(struct pt_regs *ctx) { #if defined(__i386__) struct sock *sk = (struct sock *)((ctx)->ax); struct msghdr *msg = (struct msghdr *)((ctx)->dx); #else struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx); #endif u64 msg_name; //a pointer __builtin_memset(&msg_name, 0, sizeof(msg_name)); bpf_probe_read(&msg_name, sizeof(msg_name), &msg->msg_name); struct udpv6_key_t udpv6_key; __builtin_memset(&udpv6_key, 0, sizeof(udpv6_key)); bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sk->__sk_common.skc_dport); if (udpv6_key.dport != 0){ //likely bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); } else { struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)msg_name; bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sin6->sin6_port); bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sin6->sin6_addr.in6_u.u6_addr32); } bpf_probe_read(&udpv6_key.sport, sizeof(udpv6_key.sport), &sk->__sk_common.skc_num); bpf_probe_read(&udpv6_key.saddr, sizeof(udpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); if (udpv6_key.saddr.part1 == 0){ u64 cmsg=0; bpf_probe_read(&cmsg, sizeof(cmsg), &msg->msg_control); struct in6_pktinfo *inpkt = (struct in6_pktinfo *)CMSG_DATA(cmsg); bpf_probe_read(&udpv6_key.saddr, sizeof(udpv6_key.saddr), &inpkt->ipi6_addr.s6_addr32); } #if defined(__i386__) struct sock_on_x86_32_t sock; __builtin_memset(&sock, 0, sizeof(sock)); bpf_probe_read(&sock, sizeof(sock), *(&sk)); udpv6_key.daddr = sock.daddr; udpv6_key.saddr = sock.saddr; #endif struct udpv6_value_t *lookedupValue = bpf_map_lookup_elem(&udpv6Map, &udpv6_key); u64 pid = bpf_get_current_pid_tgid() >> 32; if ( lookedupValue == NULL || lookedupValue->pid != pid) { struct udpv6_value_t udpv6_value={0}; __builtin_memset(&udpv6_value, 0, sizeof(udpv6_value)); bpf_get_current_comm(&udpv6_value.comm, sizeof(udpv6_value.comm)); udpv6_value.pid = pid; udpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff; bpf_map_update_elem(&udpv6Map, &udpv6_key, &udpv6_value, BPF_ANY); } //else nothing to do return 0; }; // TODO: armhf #if !defined(__arm__) SEC("kprobe/inet_dgram_connect") int kprobe__inet_dgram_connect(struct pt_regs *ctx) { #if defined(__i386__) struct socket *skt = (struct socket *)PT_REGS_PARM1(ctx); struct sockaddr *saddr = (struct sockaddr *)PT_REGS_PARM2(ctx); #else struct socket *skt = (struct socket *)PT_REGS_PARM1(ctx); struct sockaddr *saddr = (struct sockaddr *)PT_REGS_PARM2(ctx); #endif u64 pid_tgid = bpf_get_current_pid_tgid(); u64 skp = (u64)skt; u64 sa = (u64)saddr; bpf_map_update_elem(&tcpsock, &pid_tgid, &skp, BPF_ANY); bpf_map_update_elem(&icmpsock, &pid_tgid, &sa, BPF_ANY); return 0; } SEC("kretprobe/inet_dgram_connect") int kretprobe__inet_dgram_connect(int retval) { u64 pid_tgid = bpf_get_current_pid_tgid(); u64 *skp = bpf_map_lookup_elem(&tcpsock, &pid_tgid); if (skp == NULL) { goto out; } u64 *sap = bpf_map_lookup_elem(&icmpsock, &pid_tgid); if (sap == NULL) { goto out; } struct sock *sk; struct socket *skt; __builtin_memset(&sk, 0, sizeof(sk)); __builtin_memset(&skt, 0, sizeof(skt)); skt = (struct socket *)*skp; bpf_probe_read(&sk, sizeof(sk), &skt->sk); u8 proto = 0; u8 type = 0; u8 fam = 0; bpf_probe_read(&proto, sizeof(proto), &sk->sk_protocol); bpf_probe_read(&type, sizeof(type), &sk->sk_type); bpf_probe_read(&fam, sizeof(type), &sk->sk_family); struct udp_value_t udp_value={0}; __builtin_memset(&udp_value, 0, sizeof(udp_value)); udp_value.pid = pid_tgid >> 32; udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; bpf_get_current_comm(&udp_value.comm, sizeof(udp_value.comm)); if (fam == AF_INET){ struct sockaddr_in *ska; struct udp_key_t udp_key; __builtin_memset(&ska, 0, sizeof(ska)); __builtin_memset(&udp_key, 0, sizeof(udp_key)); ska = (struct sockaddr_in *)*sap; bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &ska->sin_addr.s_addr); bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &ska->sin_port); if (udp_key.dport == 0){ bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &sk->__sk_common.skc_dport); bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &sk->__sk_common.skc_daddr); } bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sk->__sk_common.skc_num); bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &sk->__sk_common.skc_rcv_saddr); udp_key.sport = (udp_key.sport >> 8) | ((udp_key.sport << 8) & 0xff00); // There're several reasons for these fields to be empty: // - saddr may be empty if sk_state is 7 (CLOSE) // - <insert more here> if (udp_key.dport == 0 || udp_key.daddr == 0){ goto out; } if (proto == IPPROTO_UDP){ bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); } } else if (fam == AF_INET6){ struct sockaddr_in6 *ska; struct udpv6_key_t udpv6_key; __builtin_memset(&ska, 0, sizeof(ska)); __builtin_memset(&udpv6_key, 0, sizeof(udpv6_key)); ska = (struct sockaddr_in6 *)*sap; bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sk->__sk_common.skc_dport); if (udpv6_key.dport != 0){ //likely bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); } else { bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &ska->sin6_port); bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &ska->sin6_addr.in6_u.u6_addr32); } bpf_probe_read(&udpv6_key.sport, sizeof(udpv6_key.sport), &sk->__sk_common.skc_num); bpf_probe_read(&udpv6_key.saddr, sizeof(udpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); #if defined(__i386__) struct sock_on_x86_32_t sock; __builtin_memset(&sock, 0, sizeof(sock)); bpf_probe_read(&sock, sizeof(sock), *(&sk)); udpv6_key.daddr = sock.daddr; udpv6_key.saddr = sock.saddr; #endif if (udpv6_key.dport == 0){ goto out; } if (proto == IPPROTO_UDP){ bpf_map_update_elem(&udpv6Map, &udpv6_key, &udp_value, BPF_ANY); } } //if (proto == IPPROTO_UDP && type == SOCK_DGRAM && udp_key.dport == 1025){ // udp_key.dport = 0; // udp_key.sport = 0; // bpf_map_update_elem(&icmpMap, &udp_key, &udp_value, BPF_ANY); //} //else if (proto == IPPROTO_UDP && type == SOCK_DGRAM && udp_key.dport != 1025){ // bpf_map_update_elem(&icmpMap, &udp_key, &udp_value, BPF_ANY); //} else if (proto == IPPROTO_TCP && type == SOCK_RAW){ // sport always 6 and dport 0 // bpf_map_update_elem(&tcpMap, &udp_key, &udp_value, BPF_ANY); //} return 0; out: bpf_map_delete_elem(&tcpsock, &pid_tgid); bpf_map_delete_elem(&icmpsock, &pid_tgid); return 0; }; #endif // TODO: for 32bits #if !defined(__arm__) && !defined(__i386__) SEC("kprobe/iptunnel_xmit") int kprobe__iptunnel_xmit(struct pt_regs *ctx) { struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM3(ctx); u32 src = (u32)PT_REGS_PARM4(ctx); u32 dst = (u32)PT_REGS_PARM5(ctx); u16 sport = 0; unsigned char *head; u16 pkt_hdr; __builtin_memset(&head, 0, sizeof(head)); __builtin_memset(&pkt_hdr, 0, sizeof(pkt_hdr)); bpf_probe_read(&head, sizeof(head), &skb->head); bpf_probe_read(&pkt_hdr, sizeof(pkt_hdr), &skb->transport_header); struct udphdr *udph; __builtin_memset(&udph, 0, sizeof(udph)); udph = (struct udphdr *)(head + pkt_hdr); bpf_probe_read(&sport, sizeof(sport), &udph->source); sport = (sport >> 8) | ((sport << 8) & 0xff00); struct udp_key_t udp_key; struct udp_value_t udp_value; __builtin_memset(&udp_key, 0, sizeof(udp_key)); __builtin_memset(&udp_value, 0, sizeof(udp_value)); bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sport); bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &udph->dest); bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &src); bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &dst); struct udp_value_t *lookedupValue = bpf_map_lookup_elem(&udpMap, &udp_key); u64 pid = bpf_get_current_pid_tgid() >> 32; if ( lookedupValue == NULL || lookedupValue->pid != pid) { bpf_get_current_comm(&udp_value.comm, sizeof(udp_value.comm)); udp_value.pid = pid; udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); } return 0; }; #endif char _license[] SEC("license") = "GPL"; // this number will be interpreted by the elf loader // to set the current running kernel version u32 _version SEC("version") = 0xFFFFFFFE; �������������������������������������������������������������opensnitch-1.6.9/proto/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015032�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/proto/.gitignore�������������������������������������������������������������������0000664�0000000�0000000�00000000006�15003540030�0017016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.pyc ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/proto/Makefile���������������������������������������������������������������������0000664�0000000�0000000�00000001104�15003540030�0016466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������all: ../daemon/ui/protocol/ui.pb.go ../ui/opensnitch/ui_pb2.py ../daemon/ui/protocol/ui.pb.go: ui.proto protoc -I. ui.proto --go_out=../daemon/ui/protocol/ --go-grpc_out=../daemon/ui/protocol/ --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative ../ui/opensnitch/ui_pb2.py: ui.proto python3 -m grpc_tools.protoc -I. --python_out=../ui/opensnitch/ --grpc_python_out=../ui/opensnitch/ ui.proto clean: @rm -rf ../daemon/ui/protocol/ui.pb.go @rm -rf ../daemon/ui/protocol/ui_grpc.pb.go @rm -rf ../ui/opensnitch/ui_pb2.py @rm -rf ../ui/opensnitch/ui_pb2_grpc.py ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/proto/ui.proto���������������������������������������������������������������������0000664�0000000�0000000�00000013201�15003540030�0016531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������syntax = "proto3"; package protocol; option go_package = "github.com/evilsocket/opensnitch/daemon/ui/protocol"; service UI { rpc Ping(PingRequest) returns (PingReply) {} rpc AskRule (Connection) returns (Rule) {} rpc Subscribe (ClientConfig) returns (ClientConfig) {} rpc Notifications (stream NotificationReply) returns (stream Notification) {} rpc PostAlert(Alert) returns (MsgResponse) {} } /** - Send error messages (kernel not compatible, etc) - Send warnings (eBPF modules failed loading, etc) - Send kernel events: new execs, bytes recv/sent, ... - Alert of events defined by the user: alert when a rule matches */ message Alert { enum Priority { LOW = 0; MEDIUM = 1; HIGH = 2; } enum Type { ERROR = 0; WARNING = 1; INFO = 2; } enum Action { NONE = 0; SHOW_ALERT = 1; SAVE_TO_DB = 2; } // What caused the alert enum What { GENERIC = 0; PROC_MONITOR = 1; FIREWALL = 2; CONNECTION = 3; RULE = 4; NETLINK = 5; // bind, exec, etc KERNEL_EVENT = 6; } uint64 id = 1; Type type = 2; // TODO: group of actions: SHOW_ALERT | SAVE_TO_DB Action action = 3; Priority priority = 4; What what = 5; // https://developers.google.com/protocol-buffers/docs/reference/go-generated#oneof oneof data { // errors, messages, etc string text = 6; // proc events: send/recv bytes, etc Process proc = 8; // conn events: bind, listen, etc Connection conn = 9; Rule rule = 10; FwRule fwrule = 11; } } message MsgResponse { uint64 id = 1; } message Event { string time = 1; Connection connection = 2; Rule rule = 3; int64 unixnano = 4; } message Statistics { string daemon_version = 1; uint64 rules = 2; uint64 uptime = 3; uint64 dns_responses = 4; uint64 connections = 5; uint64 ignored = 6; uint64 accepted = 7; uint64 dropped = 8; uint64 rule_hits = 9; uint64 rule_misses = 10; map<string, uint64> by_proto = 11; map<string, uint64> by_address = 12; map<string, uint64> by_host = 13; map<string, uint64> by_port = 14; map<string, uint64> by_uid = 15; map<string, uint64> by_executable = 16; repeated Event events = 17; } message PingRequest { uint64 id = 1; Statistics stats = 2; } message PingReply { uint64 id = 1; } message Process { uint64 pid = 1; uint64 ppid = 2; uint64 uid = 3; string comm = 4; string path = 5; repeated string args = 6; map<string, string> env = 7; string cwd = 8; uint64 io_reads = 9; uint64 io_writes = 10; uint64 net_reads = 11; uint64 net_writes = 12; } message Connection { string protocol = 1; string src_ip = 2; uint32 src_port = 3; string dst_ip = 4; string dst_host = 5; uint32 dst_port = 6; uint32 user_id = 7; uint32 process_id = 8; string process_path = 9; string process_cwd = 10; repeated string process_args = 11; map<string, string> process_env = 12; } message Operator { string type = 1; string operand = 2; string data = 3; bool sensitive = 4; repeated Operator list = 5; } message Rule { int64 created = 1; string name = 2; string description = 3; bool enabled = 4; bool precedence = 5; bool nolog = 6; string action = 7; string duration = 8; Operator operator = 9; } enum Action { NONE = 0; ENABLE_INTERCEPTION = 1; DISABLE_INTERCEPTION = 2; ENABLE_FIREWALL = 3; DISABLE_FIREWALL = 4; RELOAD_FW_RULES = 5; CHANGE_CONFIG = 6; ENABLE_RULE = 7; DISABLE_RULE = 8; DELETE_RULE = 9; CHANGE_RULE = 10; LOG_LEVEL = 11; STOP = 12; MONITOR_PROCESS = 13; STOP_MONITOR_PROCESS = 14; } message StatementValues { string Key = 1; string Value = 2; } message Statement { string Op = 1; string Name = 2; repeated StatementValues Values = 3; } message Expressions { Statement Statement = 1; } message FwRule { // DEPRECATED: for backward compatibility with iptables string Table = 1; string Chain = 2; string UUID = 3; bool Enabled = 4; uint64 Position = 5; string Description = 6; string Parameters = 7; repeated Expressions Expressions = 8; string Target = 9; string TargetParameters = 10; } message FwChain { string Name = 1; string Table = 2; string Family = 3; string Priority = 4; string Type = 5; string Hook = 6; string Policy = 7; repeated FwRule Rules = 8; } message FwChains { // DEPRECATED: backward compatibility with iptables FwRule Rule = 1; repeated FwChain Chains = 2; } message SysFirewall { bool Enabled = 1; uint32 Version = 2; repeated FwChains SystemRules = 3; } // client configuration sent on Subscribe() message ClientConfig { uint64 id = 1; string name = 2; string version = 3; bool isFirewallRunning = 4; // daemon configuration as json string string config = 5; uint32 logLevel = 6; repeated Rule rules = 7; SysFirewall systemFirewall = 8; } // notification sent to the clients (daemons) message Notification { uint64 id = 1; string clientName = 2; string serverName = 3; // CHANGE_CONFIG: 2, data: {"default_timeout": 1, ...} Action type = 4; string data = 5; repeated Rule rules = 6; SysFirewall sysFirewall = 7; } // notification reply sent to the server (GUI) message NotificationReply { uint64 id = 1; NotificationReplyCode code = 2; string data = 3; } enum NotificationReplyCode { OK = 0; ERROR = 1; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/release.sh�������������������������������������������������������������������������0000775�0000000�0000000�00000001243�15003540030�0015646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # nothing to see here, just a utility i use to create new releases ^_^ CURRENT_VERSION=$(cat daemon/core/version.go | grep Version | cut -d '"' -f 2) TO_UPDATE=( daemon/core/version.go ui/version.py ) echo -n "Current version is $CURRENT_VERSION, select new version: " read NEW_VERSION echo "Creating version $NEW_VERSION ...\n" for file in "${TO_UPDATE[@]}" do echo "Patching $file ..." sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" $file git add $file done git commit -m "Releasing v$NEW_VERSION" git push git tag -a v$NEW_VERSION -m "Release v$NEW_VERSION" git push origin v$NEW_VERSION echo echo "All done, v$NEW_VERSION released ^_^" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/screenshots/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016227�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/screenshots/opensnitch-ui-general-tab-deny.png�������������������������������������0000664�0000000�0000000�00000325323�15003540030�0024646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR��€��G���Á®8ï���sBIT|dˆ���tEXtSoftware�mate-screenshotÈ–ðJ�� �IDATxœìÝyXTeûÀñï þƒ *ˆŠ+îZîjæ’KšÖke¥¯–fšVšù˲̲E}µÌ\3-—r/5·ÜrßBPPPP‘]¶™9¿?hN ›ˆ(æÜŸëš 8˳3ÃsŸç9g4ä3jÔ(%ÿ2!„B!„ÿ>óæÍÓäý[gúåµ×^SêׯOãÆqtt|ð%B!„BQfRSS VΞ=Ëܹs5�È þ „»»{ù–P!„B!D™JNNfåÊ•Ì;W£¨_¿¾B!„Bñruu¥^½z�X5JéÞ½;666å\,!„B!„÷ƒ»»;¶¶¶ÿ§äž?!„B!Ä¿‚Á` ++‹ììl Š¢ ÕjÑjµØØØ`gg‡V«•üòqqqò<F!„B!VŠ¢‘‘Aff&F£Q]¹A“ÑhD¯×“™™‰­­-h4šâ’´Èü$�B!„B<ÔE!--ììlE¡B… T¨P'''t:z½ž´´4nܸABB‚D999•*Hz”ó“�P!„BñÐR…Û·o“““ƒµµ5µjÕÂÁÁA]o4Ñjµ¸¸¸àââ‚··7.\ ''‡ôôtï*HzÔó+»ÉªB!„BQƲ³³ÉÉÉA§ÓQ·n]ììì0E¾ìíí©S§VVVèõz²³³%¿<î:�T…œœRSS¹yó&7oÞ$55•œœuŽªBˆò KlR6òiü¨)‹c+ç‡âßGQôz=z½žêÕ«£Ñh0 w|iµZªW¯Ž^¯W¤"ùåºë) z½žôôtœ©U«�111¤¦¦âè舵µõÝ&)„åKŸHèï›Ø}æ2ÑWoatñ¡Š=Ú<Ù•FàgZNçöþʶ?ùz#‰,3žÞÔïЛ>M+ÝñŠ’|€ÿ·‚PM]ž{ÿUZ¹‚!;“ƒ:;t%™¢?ÎÂqßr¢bޞ܋*Åeª$±÷«wYnÄ¡þsLÙ u{=§Oà룞<ùÎ$žô»céᆲeénê %8V¥­Oáû<¶Å%Xi!ÄÃÁh4b0pqqÁÖÖV}8JIØÙÙáââBff&ƒîΡÏÒߢE‹¨Q£íÚµS·Ý»w/ 6¬ÔùA)F�333qrr¢~ýú¸¹¹áææFýúõqrr"33ón“Bˆr¥¤‡³þ‹˜»þ a×<ý«âšss‡7±à£™üx:™’ôßc2'¾ÿœ¹k÷s!AÁÝ??…[—Î}»dßÓª±ñÄ·²+®>~xÚj@Ifÿ¼ ¼1áìKºŸµP¸ý×~9•f6²tW£L¬¬e $Ǫ´õ)b¿Çö~§!„ ÓìC777³°k¶ìêÕ«8pÀl™››999÷œŸé•‘‘ÁçŸNFFF¡ëË*¿:uê°hÑ"vìØÁ``÷îÝjPx/ùA)F�³²²ð÷÷G¯×›-÷òò"<<ggç»MR!ÊIý¼”ß.ÝÆ£ÉxíùÇñ²1 [¿eÁæóìù~-µ¦¾HcÇûÛaV®íg˱[àÓ7ß~ ÿ¿}&ÙØ•ìj}mú¾õ1}ÕDïOY ÐhВ±u›hUûjÛ? |ËI™«»•ÿØ–WBQrrrÔûâ 7h:vì«V­b„ x{{ÏÌ™3©Y³&Íš5SŠbkk‹^¯¿«‡¤äÏÏÄ`00gÎBCC™={6¯¿þ:VVVfÛ”U~mÚ´ÁÚÚš¹sçráÂþüóOFŒAÛ¶mÉÈȸ§üJõPÓwPä_&„ÿ&JúöMû†ôøÞ¦ÙžZ'jöB·sÓYwñ$ûO¥Ò¨Eß[BR—ç¾¹›½¡×Èq  i÷<ÕÂ@I»ÄîŸa_h4·ôNT®×–¾ý;SÃYû÷Ãï¸ÚêÚ±ëTÉÚ Ôí0ˆÁ]k`—–BªÀ€Þ €uî¹Fg‡­©ÀwHÃÉp–åoÏã ®-¯ðñÿ{—•áà<?Nz•Ÿ*tfüÿ Ð ÈŽçÄ–_Øv2’¸Tk*øÒªßP:úþÝ6™Ñøþ BÏ\!UW‰º1¸su ƒ­‚hóx2û÷ígͶÇy«O5¬ü·#Ù>N)O%ÍWÖ©m‰øì~¹âN—7ÿþÕ­ ã‹ß_Æ©ú¼3šŽ´(釘?i¡Uúóã:SAÏñMëØvò"ñ©V¸W©G›§úÒ±ºýqŽ[ÈÅÇ^` ãQ6Œ$Õ§£G¹›—5ãë>›Íïqn<þß·x®¦ªît¬Ô©±Û> 9”í[vr<<šëÉYX»úP«å“ô}¢>žÚ¢÷ TòÛ¤Ë&öð~Ù~‚Ë7R18TÀÛ·)}^lMü‚’¦QÌyPÍXxúÞ$èð…­Vkö]x&ãÆã«¯¾â“O>á…^`ùòå2vìXÒÓÓÕí4 F£±ÄRQù ,X@tt4S¦LaΜ9Ì™3‡‘#GšM½,«üÒÓÓiÞ¼9:ubÇŽ´k׎֭[“––f¶ÿÝæ¥˜jkkKll¬z³¢é‹­­íBˆ‡„!æ"—³AW=˜Îù>85iÖ¼:ZEOÌåXr?’sˆüu);®9á_³2V7ÃØµd.ëÎe>–ßþ÷«$àT·-ºpóÈÏÌ_vˆ[êõ1#×ö®dÃy…JÕ|pHåøú%l ÏÆÊ7ˆZÎŒWgöÿÍdÁOÛ9v1‰œ£xE§aÎŽª-:RÏSêuìNµqÓ�Ê öΟɷÛN“`]u}±MËD±ÿç*¦’x–?/‚—¿7¶iÑ[·˜ÍE<eLcCõîOÓÊÓÈÕ«ÙoªpžÂÛ>E”U[‰†|±R‰¸pÐ_ çBJ&Y9‘äD†q)GKÕFÁxj®³çë™,ÜJŠS êÕp$5â k¾üŠMQzµ\Iû–²pW4xzáîãEż‡_Iç¯uËØyMƒOççPÿŸà(Á±*¦í¹AĹ›XyѸQ ®9±ß²%»ãQŠÝÏœ>r#ß,ÝIx¶Úv¦uý*¹·+yÅE§_ø) „÷‹V«E£Ñm6õ199™1cÆÈœ9s¨V­o¾ù&©©©fÛegg£ÑhÐjKö–Ÿ^¯çÛo¿åòåË|ôÑGøûûóÁÃÂ… Õ±”U~¦×¶mÛØ¹s';vdß¾}lÞ¼¹À6w›”bÐÎÎŽÔÔTÎ;G¥J•�¸~ý:ÙÙÙ2ýSñ¯¢d¤‘X98Rð–(-N®.è4™™õÏRï^}ë)ªéÌãƒïÿâÐî„d†²=Ê€W×WýT5tJ“'ñý¹?9“òír÷×x´gÄäÔ²†ëÛ?eÚº(ÂB¯¡ÔnÄ áý0,ß̉ë—9±ë2'v¯Ç9 C†  ‘ç?ìE¥a¬‘§ø;üïLðá]ü•Tú]zÑáï'´è#v±-<›šxsL'*å½òwŒ¤©Ð‘‘“Ÿ¦¦µ‘k¿~Âôõ1„Ÿ‹ÇX³J¡W5öAôîߌ3 ŽðëšC4ÙÂl}æÙߊnŸÔÇh_DY•†ñÝMì…HÒºV"%2’Tgw\Ó“¸táúÇêp%<‚tmº4¨€!b5ÛÂ2°­=˜ñ£Ûâ©É!zógÌÜÍ{Âè>äïÙ×fÀ„ÿÒÁ4ì«?þ÷ #ÉÇdÅþlúðb¯Øç¿6àt§cUtÛãÑžW§·ÿç¼¾O§­ãÒ™0’;¶/z?³I6 Ù7âIRÀÁ§wlE€‡í?Aj‰Ò�}dQçÂí‹Å¤/„V«ÅÊÊŠÛ·oãääd¶.))‰ñãÇ3{ölÆŒCbbb§a¦§§£ÓéÐjµ%š±XT~mÚ´aذaèt:nß¾N§ãÃ?$<<EQÔ|Ë*¿ƒòÃ?0bÄÚµkGƒ ˜;w.Š¢Ð¶mÛR×J�êt:œÉÌÌäÊ•+@³³s‰Ÿ<#„­=¶(d¤¦p[³®BæíÛpvt@Cî” µ¶V�Üê7Äßê,áñqDÇÇ“¥‰ûm£Ë“ŒUÙyïͶ²Fg•›‘‡W%¬5—ÉÎÉBAƒc`^y¯ 7.œäÈá?ùóx87.îfér/üÆ´§ÂÓ(®²¦_Œ$G]!IÑRµ^}*Z±¹ZO-½½Ði¢s¿î§È ´8÷¥wƒ¿X~f¿œªCcuk…¤¸¶YYAãL#ßMD_>ÏÅ캤\ˆÇ1x ®­bÃ…p¢sÜ9> ­okTRH:E²¢Å¿n]Ü5�Öø5nDå­Wˆ#Ùè‘›°“/Õ*|«’z‚5?^'IãË“ƒºàWèC`Kp¬4f›ça$1l7Û÷'üÊM’RÓÈ0‚6;›ìüV‘—»ÚѬB‡Nýħ§×áä×=zÑ¥¡¶%J£¸óàéK$(„xÀILLÄÞ¾àô¤¤$ÆÇÍ›7 €’’’̾T½´ù5lØììl²²r/  ¬­­iذ¡Ù”Ó²ÊïÊ•+¼öÚk4nܘ7nШQ#^{í5Nž<i6U´4ùÝuĦÑh°¶¶ÆÚÚZFü„ÿjV>UñÓi»tŒS·ZÑ6Ï(J gO] ±¥j@e´œ/˜€V›;f¥ÅÚÚÐàñø ŒèTùŸQ2­îžÚ£/ÀßO³©’Z;*=F Çéü+_ÎØÀ¥‹a\ÎiO…‚µÂÒ(–ýßO 3‹ßGíçk4%ýѺñØ€'9t~5ÇÖmÂÁëŸ�ÐÚæíSd!|nèÃæM‘\ˆ¼HÊkj¶kB°û6m‰äB¤;áqàÓ½!> ×ÿþ§hÌÛà:Vyª¥*$;?œ’¹~ñGEÐ¥JvEU¾¸cUă[s"~æ«ÿýNr•vôØÖòaE·@áY»5aè{5húOŽž:ÉÇÙ¼ Šô7Þc`;ï§ó ØôkÊ×= !EQpqqáÊ•+ܾ};;ó¹èF£‘¸¸¸B÷ÍÌÌ$==ªU«šM¥É/55µÀ¶YYYj@XÖù1‚ÌÌL’““HII¡aÆ´lÙ’7n”:?(Å=€Bñ¨Ð¸6䱺öΖ÷aêgsíàOl<“îÍhüÏ=`Æô[$f)€BúùP¢ üªá_­nZ…¤KWȩ䇟ß߯Êp,É'­’Dìå2óÄ-ZkëÜi;ìKõim…••È!'ÇT .•*b­1ríÌiâKþÿ¢D4Û2°k5¬²7ÔtÏ·ªUqÓ×>…•5·¼•5ÂG›HøÎýDèý ´Ç»N]*(1œúõ8—/‚ƒ½Ñ ÁÝÛ [‘˜S'¹a�P¸u.”«F îU«âr§v´ò¢ýàÚ¹¾w%ëÃ3 Š%:V…ÕG!!â<7õZª4¡upMüÜíò×EµC>F­ Õš„ÐÿÅ7ÕÍ­’HLLÆ¥q‡ó Øô…âÁQFCÅŠ‰-ô^¹Â^ÙÙÙÄÄÄP©R%4Í]}1ûÃ_\\IIIfy$%%wOùA)Ÿ*„ Í>KxüRžYͧSÿÀ¿Z4IÑ\ŠMFïX'†>Em;Ô{㔄},˜qƒúU!úL8éV~ôèRG?oºÔ:ÈOá;™?+•6Mü°M‹åÍÖ¿>wzv†áÒï|óÙï$9VÀ»’;öÆDb¢orÛhKõN¨eýOJ^?|*»£9w™ß—}OZÝ:´èÞ ß:­iéy‚½—ÖóŬhšV·#åj&µ½H;Ï»mÄütøvHÇ£³Ø~Õ€òwüaØŽ.A‡ŠnŸ¢Êªw#yoasè9´Õž¢¦³­C]j»ngOx$ZŸî4ôÍìlêu¡S•lŽ\ÏŸ]¢¶g N]$Û¡.]ÛWʤ;ÖÀʧ#Ït?ɧë/ñǪõ4|{ µó –èXQX}šâ^±"všh"·~Dz„@”‹ò—!ÏÕØ¢Ú!_õVóá÷1øÕ­NE›T.¹†ÑªþþÎh5š¥a_ÌyÐ*©˜ôïúœBˆ{“““ƒ‹‹ z½ž+W®àíí]ìÃ'³²²ˆ‹‹ÃÃãTßUþ¨ç'ŸãB‹¦ñhÂso¿Å ]›èžÃµˆHn+P»u?ÆL~ƒžAŽf”·@j¹&pîÌô•ñä¨Qô¨¢FN䥆TÌ cÏæMì8ÞEz †L×ú„tkN w-)q—¸—“oº<7–‘]«PºIwVtÊS|!ö(ûåb‚ìë0`ìHz6©Š}Âöí;AT¦CæÝF˜E°ñ牭ñÔæ½™ïNíSDY´>7òF£h¨P«fî;uÔ rDƒ–Š âg:Hº*<1ê5ú6¯†Ý­sœ:ŸŠsý® Ÿ8‚¶…Ρ-ŒßÎCèî¯Ãx}/+ ##Ï…Õ’«Âê£àФ/Ïu¬…§1–SGÎRµ;Ý‚ò^(¦þ)9VžTóÈàÒñ½ü¾ç Ž5iÿÜpzèJ˜Åœ9wH_!¼ÌÌL<==ñööæÚµk\»v””²²²Ðëõdee‘’’ÂÕ«W¹ví>>>xzz’]Ĭ-8?ͨQ£”Q£F•ª Ba1ôÇY8î[NúôaòÛÝñ¹‡a(ä6GÁ%E/½»4J_¦ÒoõoQ–µ)÷–)£™L¹WP!rÙÙÙ¡ÓéHLL$))‰ŒŒ ôz=:{{{ÜÜÜððð ''ç®GÆ,!¿yóæÉP!„¸[÷Ú.¸á)—OIÓ(©’íýhE�eY›ro™2*@1Bˆ‡Bff&Z­ggg<<<°²²Rï3}?yzzz‰¿Áó“�P!„Bñ¯a4ÉÌÌ,“7KÌOîB!„B !#€BQº&¼<{~y—B!„âžÈ B!„BX �…B!„ÂBH�(„B!„B@!„B!„°E>Æ××÷A–C!„B!D‰-t¹Œ� !„B!„…�P!„B!,„€B!„Ba!$�B!„B ! B!„BX �…B!„ÂBH�(„B!„B@!„B!„°� !„B!„…�P!„B!,„®¬Šˆˆ 55•„„„2IÏÓÓWWWªW¯^&é !„B!„¥+“�0""FCûöíË"9ÕñãljŒŒ$00°LÓB!„BKT&S@SSSiܸqY$e¦I“&¤¤¤”yºB!„Ba‰Êd0!!EQÔ¿9ó?øˆL}ú]¥c§sd`ãI<Õà ³´…B!„BÜ»2»0o�¸êø|;à"Vš¢“oÓ©+ûvn3[fPô¼²¦:}ê-«b !„B!„ø[™€ðO˜mÈÄÙÑÏ>ÿ¤ÐíÞ÷�NÖe2Õt4MYO!„B!,Z™Ž�š7k+[Œ¼5aR‘ÛŸ:ògezCNî¾yF…B!„B”24é<‘ÁK+mÈ,°nÚàUlKüˆ}¿ž.°ÎÆÊŽþÁïG‘„B!„ÂâÝ—À~ &ЯÁ„Û \æbö÷OC §Œ� !„B!DÙ»/`^W’B™·$¶:‡ëÆþÒ'[þÓôjUlYVEB!„BQˆ2ù@ø'�Ìÿš»o8íšu¥SËžx©ÛxѯÃKÔ¨S•O~XäþB!„B!ÊÆ}T# 9ɲwàÙöÿÜßצe®+gˆË%=['ÁžB!„BÜg÷å!0y=Ûd_îï�®\ÀMç À¥ÌƒÄFÝ$îR oµ_}¿‹!„B!„ï¾N=|ô�Û<ÃõŸë¶;…#O•t†Œô,NýIØî"–Uá…ï°ÿЙ*„B!„÷Ñ}ZÁ³"S&½Ã›™¯£( 6Ö6XYéÈÑg“S5‡œ®ÙdÌ&;'‡Ó§OHÀ'„B!„÷Q™�BÁQÀS§O‘œœÄ­[ ܼqÎFƒµ-Y™Y\¼IVfVh‰‹‹—Ñ?!„B!„¸îëà•è+äès0$&%ƹ°P233±¶¶¡Cûö�XÛØ_` …B!„¢ìÜׇÀÄÇ]ÇÆÆ[\]Ý ä±ÇZ¹­B!„Bˆû美�ÆÇÇ3⿯ÜUÅý-„B!„¢ôîk�8ÿßÜuÅý-„B!„¢ôÊ$�twwÇh4¢Ñh0e‘$Z­£Ñˆ»»{™¤'„B!„–®LžêêêJhh¨–ÅËh4Š««kYQ!„B!,^™Œ�Ö¨Qƒ .püøqË"IÜÝÝquu¥Fe’žB!„BXº2»°fÍše•”B!„Bˆû L¿^!„B!ÄÃK@!„B!„°� !„B!„…�P!„B!,„€B!„Ba!Êì) ¤¦¦’PVI>²<==quu¥zõêå]!„B!„)“�0""­VKÇŽË"9‹pìØ1"## ,ï¢!„B!,D™€©©©üÝ¥¦M›²k×.¢££¹}û6iiiå]¤ûÊÉÉ '''|}}-¦Î&–\÷MÚº|XZ»[Z}MòÖ[!Ä¿W™€2í³tpss£zõêh44Myé¾PEQˆ‰‰!<<›G¾Î&–\÷MÚº|XZ»[Z}MòÖ;&&??¿ò.’BˆR*³{�Eéøùù¡Ñh0å]”ûJ«ÕâççGxx¸ÚYzÔëlbÉuФ­Ë‡¥µ»¥Õ×ÄTï‹/–wQ„B܃2 �E)«¤,Š¥tŒF#Z­–¬¬,‹©³‰%×ýA“¶.–Öî–V_S½-aº«B<Êäk ÊÙ£<e(?S]-©Î&–\÷MÚº|XZ»[Z}M,­¾Bñ(’�P!„B!,„€B!„Ba!$�B!„B QîO5 lÙ²…½{÷²oß>  6ä‰'ž GØÙÙ•w‹4mÚ4bbbX°`AyE!„B!î¨\G�oݺŋ/¾ÈŒ3¨]»6ï¿ÿ>ï½÷5bãÆètåŸ>tV®\‰———úzâ‰'˜>}:QQQw•NHH?ýôÓ}*eÙZ¹r%íÚµ+tݤI“˜4iR™äc0HNNF¯×—Ize!ÿñ2d‹-’§ðÝGŠ¢0xð`¼¼¼8þ|‰ö™8q"Ÿ|òI‘ëÏ;‡——ÑÑÑeUÌGÆý<ÇÆ÷ô† ðòò*ôÉ¡cÆŒaÖ¬YåP*!„–¤\#¬O>ù[[[¶mÛ†££cyå_ÅÏϯ¿þEQHMMeݺu 4ˆŸþŸò.Þ¿ÖÕ«WiÖ¬üñµjÕ*ïâ¨LÇÛh4ÃW_}Å¡C‡˜7ož\$¹"##Ù¹s'�ûöí{¨Î…GÕý:ÇÖ÷´BQžÊ­÷xöìY>ýôS.\¸P¢à/::šÙ³g³}ûvêÕ«ÇèÑ£yì±Ç�ؽ{7sçÎåÉ'ŸdÍš5ÄÇÇóÌ3Ï0räH5m£ÑÈš5kX¹r%qqqôïߟ#FàììÌ7ðòòbûöí¬Y³†ƒ²xñblmmY¹r%ûöí#11‘=z0f̼½½ïkÛ܉££#Í›7WÿnÙ²%õë×çÀôïß¿K&ǻeË–Ô®]›N:ñ /ЪU«r.Ý£g÷îÝôíÛ—fÍšñÓO?ñüóÏceeUÞÅz¤É9.„B<8åöEðáááøøøàïïÇ}oܸA§N˜0a 22’îÝ»³wï^4h€¢(¬[·Ž€€�ÆÏíÛ·y÷ÝwÑëõ¼õÖ[�ÌŸ?ŸmÛ¶1vìX¬­­Y¸p!o¿ý6sæÌQó aîܹtêÔ þüóOyóÍ7¹}û6?þø##GŽdíÚµêw!)Šr×u/kŽŽŽT¬X‘œœ�~þùg¾øâ öîÝ«nóÑG‘™™É´iÓ MÃh4²qãFÖ­[Çõë×éÙ³'Ï?ÿ<NNN@î¨Èâŋٿ?~~~4oÞœÿþ÷¿ØØØÜÿ ÞEQصkkÖ¬!<<œ¶mÛòÊ+¯àëë €^¯gݺuüòË/ÄÄÄкukú÷ïOåÊ•iÖ¬�mÛ¶`ÿþýÔ¨Qã¡«{:uðòò"**ŠV­Z1þ|RSSñõõeõêÕÔ­[—>úˆììl~üñG¶nÝJZZÝ»wgèСê1…ÜãúÝwß±oß>ªV­Ê³Ï>ËO<À±cÇX¶l'Ož¤]»vŒ1???�Y¾|9;vìÀ`0ЦM†Н¯o±íõ°Ÿgz½ž+V0vìXªW¯ÎäÉ“ §nݺfÛ>}šÅ‹säÈZ´hÁåË—ñôôT×gff²lÙ2¶lÙ@½zõÌö/ê˜ÝKûìß¿Ÿ‰2E}�� �IDAT+VpòäIš4iB§Nèׯ�ééé,[¶Œ­[·âääÄÓO?MïÞ½ÕÀ¶¸}ËCþsrÿg,Y²„аaCžþyš6m @BBuëÖeÍš5lذ£G2eÊ |O?lõ-JißK…µÇìÙ³©S§N¡Ÿ¦Ï>!„–¡ÜF�¯\¹B@@�Zí?·!öïߟ_~ùEýûóÏ?çõ×_gÕªUtèÐW^y€-ZpæÌ¶lÙBƒ Ôíß}÷]\\\�¸yó&3gÎdâĉ$&&2qâDöïßOÆ ðññ¡fÍš¼ûî»j'hË–-tëÖMM¯[·nfûûûÓ²eKâããË}Ð$33“;wrãÆ Z·n]êt–,YÂîÝ»ùïÿ‹••ßÿ=|ð3fÌ 55•ÿüç?ôíÛ—™3g’œœÌ7h�”žžÎ±cÇ ,¿~ý:•*URÿþå—_øâ‹/xë­·<x0kÖ¬¡ÿþlÞ¼OOOV¯^Íüùó™:u*...\¾|<==YµjÏ<ó K–,Áßß___RRRʽîùéõzRRRÌÎÁY³f1hÐ ^zé%*V¬ˆ¢(¼ÿþû\»v#F••ÅW_}Å™3gøßÿþ‡V«%22’Þ½{óÆoЫW/¢¢¢Ô÷‘#Gxýõ×™:u*Ï?ÿ<{÷îeذa¬Y³¦NJNNo¿ý6Š¢‰³³óÛëa?ÏN:Ehh(-Z´ B… òǘ€ýõ½{÷fÊ”) 8Ó§O³bÅ Z¶l ä^„˜2e qqqŒ;½^ÏÂ… ä•ÿ˜AéÛçôéÓôë×Ù³góÒK/qíÚ5œÜûàÞ|óM|||˜:u*)))Ìœ9£ÑHÿþý‹Ý·¼ä?ÇÃÂÂèÙ³'ï¾û.½{÷æÐ¡Côèу͛7›/ `ÆŒ´k׎úõëúž~ë[˜{y/™ämªU«ùù'„²”[�èëëËÈÊÊÂÖրɓ'3~üx�F­n{òäI–,YÂwß}g–Æ›o¾Ydúžžž„……a0ˆŠŠ"##ƒ&MšØ.-- WWW�ÜÜÜÌÖ)ŠÂï¿ÿί¿þʹsç ++«5.;áááxyy©±råJªT©Rªô™6m›7oVG*¼¼¼hÙ²%ãÇçÖ­[DFFÒ£G#JLL =zô(tÝK/½ä‰~ø!_~ù¥zÅ¿yóæôêÕ‹ 6ðâ‹/rúôi{ì1:uê„F£1›J[£F �Õû…¢¢¢Ê½îy¥¥¥±zõjjÕªev>·k׎Ï?ÿ\½_êìÙ³¬X±‚Ç«r`` ­Zµâ¥—^¢Y³f,X°€—_~™—_~€Ç\MoΜ9Œ;V½�Ò°aCÖ­[ÇÉ“'iݺ56l`æÌ™êèŒéâCXXX‘íõo8϶oßNÏž=Õ{iû÷ïϪU«x饗Զ]¸p!cÇŽUϻǜ°°05¿þú‹eË–qêÔ)5€ñööf×®]fyå?f÷Ò>øùùÑ»woìííÍÖ=z”]»vqòäIõ©Ê™™™|óÍ7ôïß¿Ø}ËCaçø×_ÍØ±cyá…€Ü6OJJbþüùfÿV­ZEÇŽÕ¿MŸÕyßÓ[}‹rõêÕR¿—Lrò·GqŸB!,G¹€@î43S'2o‡Ö”¸»»3tèÐÓ‹ûçm𢠍÷nÛ¶­Àƒ�¼½½IJJ*4%K–°dÉÞÿ}^~ùeâââèܹsIªw_U­Z•o¿ý€¥K—rõêÕ{úGCFF:u*°.==€€�žyæ†Ê3Ï<C‡hҤɽ/*((ÈlJ«IÞ'€ÆÆÆCPPºÌÎÎŽÎ;ó×_лwoFÅÍ›7yâ‰'èСƒ:úR˜‡¡îùþnݺ1oÞ<³÷ˆ›››ÙÃ2"""hÔ¨‘Ùèh`` 5kÖäÒ¥K4iÒ„­[·òùçŸÈ/33“ß~ûß~ûÍìB @jj*VVVL™2…‰'râÄ :uêDëÖ­qpp(¶½öó,--åË—3dÈu´ÙÉɉÐÐPBCC Æh4òûï¿óÕW_™íkmm­þ~ñâE7nl6B[Xò³{iŸÇ{ ooo @ß¾}éСƒzAãâÅ‹$&&R­Z5³4+T¨�Pì¾Jqç¸Á`à×_-ðu;­[·føðádgg«ËJ2šõ0Ô·$îå½dj‡üíq·ŸB!Må�6jÔˆ—_~™÷ߟ¥K—;¥²aÆ|ðÁ8;;¥+ ___¸téÒ]p7nT;€z]y³··§Q£FêïíÚµcçÎtíÚUݦ°GŒÅÁÁ€Õ«WèU¬Xkkk¾üòK.\¸ÀüÁ”)Sðððà믿~¨¦™ŽÁ`0[nkk«Ž|<þøãüñÇ<x;w2aÂæÎKÏž= MÓÖÖ¶ÜënzB¢V«¥R¥JT®\ùŽAQvv6™™™–;99accCvv6ñññ…Þ¿jccƒ——¯¿þºz? ‰©Î/½ô={ödß¾}üðÃL:•E‹Q»ví"Ûëa?Ï>ÌÍ›7™={6³gÏ6[·gÏ‚ƒƒ1 ¤¤¤8ÇòÊÉÉ)Õ,{iŸÊ•+³~ýzNœ8ÁÞ½{éׯƒ bÒ¤I899áììÌï¿ÿn¨š.’·oÞ i÷Sqç¸^¯'11±@›[[[ãèèx×Ê»¾¦ 7iiiÎëÄÄDuJkqŸ=w:WRRR Íûn?ÿ„B<šÊ-�ÔjµL:•W^y…víÚ1vìXõÙÉ“'9sæŒ:Ūo߾̟?ŸW_}•W_}>L·nÝÔ‘Äâ8::òñÇ3|øp¬­­iܸ1QQQܼy“_|±Èý4hÀŠ+¨Y³&vvv®@»¸¸ÎÉ“'©[·n¹ÜÄðáÃùüóÏi×®vvv899c6½¶8>>>T­Z•+W®ù}{†ZµjQ«V-ž|òI6lÈ™3gîé¾Ã²æë닽½=§OŸV§ð 8ÀÓO?­nçääDHH!!!xyy±téRzöì©vþòúå]÷üO}- Nœ8Á•+W¨Zµ*�qqqœ8q‚5j`ggG«V­8sæ !!!fûjµZBBB8uêÆ +²SìååEÿþýyê©§xæ™gعs'µk×.²½7nüPŸg›6mbÈ!|ñÅfËgÍšÅÊ•+1b666tîÜ™C‡Ñ¥Ku›¼tµjÕ åòåËøûû—8ÿ{}êt:š7oNóæÍiÚ´)ƒ bĈT¯^ÔÔTÕ Gùµ¯i”ð~+î·µµ¥{÷î:tÈl:ã©S§èÖ­[±`Qïéò¬oõêÕÜÙ/yësýúu<Ș1cÌÊ_Ú÷RQŠúüBa9ÊõKÄ*W®ÌÚµkY½z5û÷ïgþüùШQ#~ÿýw‚ƒƒÜX7nä믿fÒ¤I¤§§Ó¶m[³¯;yúé§qssãûï¿gúôéÔ©S‡>}ú»Ï믿NVV'N$((ˆgžy†Ã‡«ëÈ?~< .$  t q† Æ‚ ؼy3ýû÷§~ýúØÙÙ1wî\Ú·oOTT›6m2ë°zyyñ×_qáÂj֬ɔ)S7nÖÖÖ4hЀèèhnݺÅàÁƒ‰ŽŽæ§Ÿ~¢Y³f¸¹¹qòäIìííK}ÏáýâææÆäÉ“yûí·¹}û6•+WfÛ¶m¤¥¥©÷.Y²gggüýýÉÉÉáðáÃêÔcOOOüüüظq#ƒ233ÿuϯiÓ¦ôêÕ‹qãÆñꫯ¢ÓéX´h¯¼òŠzÏÐÈ‘#>|8ÞÞÞsùòeìííéܹ3#FŒ k×®T©R…nݺ‘––ÆÑ£G9r$Z­–éÓ§Ó²eK¼¼¼HHHàÌ™3¼üòËÅž+íyvýúuV¬XÁªU« ¬ëر#3gÎäÌ™34mÚ”áÇӻwoÜÜÜhÑ¢‘‘‘¬^½š‘#GиqczöìÉo¼Ák¯½†­­-ßÿýËp/í³wï^"""¨[·.¶¶¶ìÞ½›ÆãææF… >|8'Nä­·ÞÂÛÛ[ýbú¶mÛ»ïÃbÔ¨Qj›7mÚ”°°0æÎËæÍ›‹Ý¯°÷ô¹sçʵ¾UªTaÚ´i¼öÚkL˜0ÀÀ@’““Y²d Ï=÷œ:x/異÷ù'„Âr”û·HÛÙÙñŸÿü‡ÿüç?ÅnçééÉäÉ“™<yru:t(0=èÉ'Ÿ,°ÌtÕ3¿Š+:¥ËÃÃ3f˜=UmàÀêï>>>üøãÅ–ûA`ìØ±Ìš5‹|||X±bsçÎeÆ têÔ‰gŸ}–øøxuŸñãÇ3iÒ$âãã™;w.½{÷ÆÕÕ•Õ«WóÅ_P³fMuúŸN§ÃÊÊŠ9sæE³fÍX½zµ:²ô06lîîî¬]»–„„:wîÌŠ+ÔÇè{{{³yófN:…³³3O<ñÏ?ÿ<;vÁ‚|öÙgìß¿ŸwÞy‡€€€MÝó²²²â‹/¾`Ñ¢E|öÙg¸ººÒ£G³ó7$$„o¿ý–U«V1{öl5jÄ Aƒ�¨U«;vì`Ñ¢E¼þú븹¹Ñ¾}{233±¶¶Æßߟ~øÐÐP™>}:!!!ÄÇÇÛ^ëy¶oß>¼¼¼hÑ¢Eu 4 ((ˆÝ»wÓ´iSZ¶lɺuëXºt)k×®¥K—.Œ7ŽŒŒ àŸ¶_¸p!Ÿ|ò ^^^<ýôÓ9räŽå(mû$%%qùòeV¯^Mzz:íÛ·gÁ‚ê=†S¦LaÍš5,]º”ˆˆš4iÂ!C€Ü 'Åíû0hÙ²%ëׯgÙ²e¬ZµŠV­Z±eË–;~¹{aï釡¾¯¼ò þþþlذ… ÌóÏ?ÏO<¡ŽZÞéýPܹR”â>ÿ„BXͨQ£”Q£FXaúÞ´’رcÇCùJ»uëÖѱcÇbï'z”XYYqòäI5jd1u6±äº?hÒÖåÃÒÚÝÒêkbª·é;…B<¼bcc ,›7o^ù}¼B!„BˆK{çM„B!„B< $�B!„B ñðÜåo¡ŒFã]}gß¿™éá–TgK®ûƒ&m]>,­Ý-­¾&ê»!…BÜ?�–3ƒÁ`Q�ˬ³‰%×ýA“¶.–Öî–V_!„ÿ~�–3ƒÁ€^¯/ïb<P–XgK®ûƒ&m]>,­Ý-­¾B!þýÊ$�ÔjåVÂÒ¨T©,ïb<p–XgK®ûƒ&m]>,­Ý-­¾&;vì(ï"!„EêÒ¥Ë=§!#€å¨uëÖå]!„B!Ä¿ÀÏ?ÿ\&é”ÉÐ%Ý�/„B!„ÿV2wS!„B!,„€B!„Ba!$�B!„B ! B!„BX �…B!„ÂBH�(„B!„B@!„B!„°� !„B!„…�P!„B!,„€B!„Ba!x�øá‡¢ÓéÐét¬Y³¦Àú'N¨ë‡ ö@ËöÉ'Ÿ¨yGDD<мóÚ½{7]»vÅ××GGGüýý aòäÉfÛ-_¾œiÓ¦ñÅ_”:¯?þøƒiÓ¦1mÚ4nݺu¯E(ÅÄĨǵGfë¦M›¦®[¿~ý]§}åʵýNœ8a¶îÏ?ÿTÓþî»ïî©ÿ&+V¬Pëmz9::Ò A¦M›FVVV©Ò}XÞŸBñ°;tèC† ¡Zµj888P¥JºwïΪU«0å]¼2W\ÈÑÑNÇСCË¡dÂ$o_L§Óakk‹§§'ÁÁÁŒ1‚Ó§O—w¨jÕª¡ÓéèÔ©S¹äÿÀÀ§Ÿ~Zý}íÚµÖç]–w[K±~ýzºtéÂÎ;‰'++‹˜˜víÚÅ'Ÿ|b¶­éïË/¿,u~{÷î}äÀû)::Zm¿ãÇ—wqZYYYœ;wŽiÓ¦ñú믗wq„â‘õñÇÓ¶m[~úé'bccÉÎÎæÚµkìØ±ƒçž{Ž”””ò.b™+‹þx° ÉÉÉ„††òÝwßѼys¾ùæ›ò.–ÅÐ=è ƒ‚‚æôéÓlݺ•ÌÌLìììÔõëÖ­ÀÝÝ.]º”Ižùó(Ê€¨_¿>�•+W.“¼ïÖ矀››_~ù%AAAÄÅÅqüøq~øá‡r)“(   uT188¸œKS>FÍСC9|ø0£GÆh4²|ùrfÏžMyO!)6l`Ê”)�ØÚÚ2qâDBBB0ìß¿ß";Øk×®Å`0àççWÞEkÒ¤ Ó¦M#**Š­[·²iÓ& £G&((ˆ:¹oNNZ­++«WàGP¹ÜhÙKKKã×_U—Ÿ={–óçÏðÔSOamm­®Û±c]»vÅÓÓGGG6lÈ×_¢(ê6=zô@§ÓQ­Z5öîÝK³fͰ··gíÚµ(ŠÂ§Ÿ~Jƒ puuÅÍͺuëòì³ÏríÚ5�Ö¬YCŸ>}èÓ§W¯^UÓMNNf„ Ô¬Y{{{¼¼¼èׯ_)y§Ì;—š5kâêêJçÎK<e-::€–-[òÜsÏѼyszõêÅÔ©S rZNÇÎ;ˆU‡ÔGÀ’%Khß¾=~~~888àêêÊã?βeËÔ¼ZµjÅÔ©SÕ¿k×®N§£^½z�Œ7NM7!!AÝÎÓÓNG¿~ýÔe6l M›6T¬XGGGèÕ«üñG‰êý0É;uóË/¿ä¿ÿý/•*UÂÍÍ^xääd�&MšDûöíÕýFŒ¡îGxx¸z>mÛ¶ €ýû÷«ÛÌŸ?ŸqãÆáééIÕªU™?>Š¢ðñÇãë닯¾ú*™™™få;þ<Ï=÷¾¾¾ØÛÛS½zu&L˜@ZZÚƒk¤òöö¦qãÆŒ1‚–-[¹£7oÞ ))Im¼#ƒ_}õ•ºüÔ©SÅæ‘’’Â[o½E­Zµ°··ÇÛÛ›!C†xÏ=Jç¨Bæƒ>PŸ;w.S§N¥U«V´iÓ†·Þz‹³gÏâìì¬n³}ûvBBBðððP§êüñÇäää¨Ûä~¿}ûvúöí‹‹‹ f&åí‡íܹ“–-[âääDpp°Y@QæÍ›GÓ¦MqvvVûK¦¾M^‹/¦uëÖ¸ººâììLpp0ß~ûm‰úCýû÷§OŸ>Ìš5Ë,Ͳ®ûÙ³géׯŸÚïòõõ¥C‡,X° DÇÎ’T¨PîÝ»3bÄ~ùåÞ}÷]�ŒF#Ó¦MS·ËÛ¯ž?>5jÔÀÁÁAí·‡‡‡óì³ÏR¹reìííñ÷÷gÔ¨QÄÇÇ«i”´OæÇ{ëÖ­ôèÑgggüüüøðÃÍb(Yl¹ýúõëãèèHëÖ­9vìX™·é]5j”röìÙ¯ÄÄÄ¿V¯^­èõú¿ÂÂÂ@”Áƒ«Ëß{ï=uù–-[Ôå‹/V4(vvvŠ»»»ºÝ˜1cÔíºvíª�Šƒƒƒâàà n³xñbeîܹêßnnnЇ‡‡ú÷±cǽ^¯LŸ>]]¦èõz%))I©_¿¾º<ïËÖÖVÙ³gš¿­­­š¿©¬¦m›6mZ¢¶iÓ¦(Z­V2dˆ²dÉåܹsfÛ¤¥¥Z@>|¸¢×ë•¡C‡ªeðññQ¬¬¬Ôm–,Y¢èõz¥E‹…¦¤èõze̘1ê²ëׯ«ù»ºº*€Ò»woE¯×+'OžTt:(öööŠ———z¼fÍšuWçÆýx]¾|Y­G×®]ÍÖå=çÖ®]«èõzeÿþýê2Ó¹T©R%uYÏž=½^¯Lœ8±Èãc–Î7ß|£èõzeÏž=ê2ŸûõêÕ«À²÷ß_-ïéÓ§P¬¬¬ÌÊÕ¼ysåöíÛåÞÞK—.UË4}útuyÛ¶mÕr§§§+z½^¹yó¦ºík¯½¦n;kÖ¬½?“““• ¨Ë+Uª¤ž‹Êùóçÿ稼ä%/yÝëëÚµkêgaåÊ••ìììb·_´h‘ú9˜ÿõä“O*999Š^oþÙëàà ØØØ(Z­V]¶~ýz5MS?ÌÆÆFÑétfý {{{åêÕ«ê¶/¾ø¢Y¿ÌÔwÒjµfýÊ#FZÆ–¨?dê› 2ä¾Õ=33S©\¹²(:NñññQ¬­­ÍúJ–þ*®/–žž®ö-­¬¬”¤¤$³cW¡B³ctñâEåÔ©Sj(ÿ«ZµjJ\\œ¢×—¼OWØñ®X±¢âää¤.ûôÓOÕm—069xð YP\]]ÕtÛµkwWí¸zõê»ŠÑ ‹ñF¥”Ë`5hܸ1�›7oV az(Œ§§§zSdFFãÇGQ:uêD||<ׯ_ç7Þ�`Μ9\¸pÁ,ýÛ·oÓªU+BCC‰ˆˆ }ûöêUþnݺqãÆ ®_¿N||<‹-¢B… E–uÞ¼yœ={ÈÎÏúõë±¶¶&++K-Gþü7mÚDjj* �àØ±c\¾|ùŽmóæ›o¢Õj1üðüð ԩS‡:uê°jÕ*�ìììÐëõjùúú¢×ëÑëõÌ›7€röìYÒÒÒˆŽŽæÊ•+T«V €¯¿þ€ðþûï«y‡……¡×ëù믿 ”K«-úT9pà�z½wwwâãã‰%55•7Ò¢E‹;ÖùaV¹re¢¢¢ˆeðàÁ�lÚ´‰#GŽðÑG±gÏuÛo¾ùF=ÞÞÞwL;33“­[·²xñbuÙÆùôÓO9pà�žžž€ù}±&L %%oooÂÃùzõ*?ÿü3�GŽaùòåeRï²’‘‘Áõë×Ù²e ‡ oß¾ØÚÚض¸s¬(ÿûßÿ8sæ ûYrõêUÂÂÂðððàÖ­[üßÿýðhŸ£BÿÌ ‚Ü[ŠûLÍÛ·òòòâàÁƒÄÆÆªJÛ¼y3[¶l)°_‹-¸yó&ÇG£Ñ�…?Ï!;;›7ß|“””¾úê+5Ï­[·pøðaõß;ï¼Ãõë׉‹‹£uëÖFƇ¢(=zT¶Z½zuöìÙí[·Ø¿?!!!%ê=ˆºGFFª3ÇöìÙCtt4iiiüùçŸ<ûì³E‘ËÖÖ–š5k¹÷æ…póæM&L˜@TTÇÇÓÓ“‰'ª÷³._¾œëׯóÞ{ïÅÌ™3 äS\Ÿ.¿=zKTTµk×`ÆŒdddÜUlòÁ`0�X°`QQQ 4¨Ügm•Û×@˜¦¦¤¤°}ûvÂÃà r;ˆ:]îí‰GUNrõêUúöíK·nÝØ½{7�Š¢˜uÂM¾ýö[jÕª…¿¿?U«VUïéÛ»w/C‡å“O>áèÑ£ 8__ß"ËùÛo¿`eeÅôéÓñôôäÉ'Ÿ¤k×®�?~Ülz$äNßìÞ½;†uyLLÌÛ¥gÏžìÞ½›>}ú˜Ý·xáž{î¹B§F¦S§N?~œ1cÆ0dÈFMvv¶šVY2µmbb"dêÔ©lܸ‘æÍ›óøã—i^Ú!CpwwG£Ñ0räHuùî9í^xž}öYu.{íÚµyã7hÑ¢­[·þ9orrrصk{Þ>œ®]»2gÎ5MÓûâañá‡R¹rez÷îMNN½{÷V/@”ÓÔZfÍšE×®]>|¸ú”;S{<Êç¨B�jPRGŽ!11Èý_Ô¼ys¼¼¼Ô‹f;E2¿‰'âàà@ýúõÕÏÕ¼§‰••ï¼óZ­–nݺ©ËMÛšúV�»ví¢{÷î<õÔSjÇ?::šˆˆ5`„܇۴nÝZ¶lÉ‹/¾XâúÞïºWªTIý?>jÔ(&L˜ÀÒ¥Kqqq¡ÿþ¥*§¥É?m2¯5jðÑGáëëKpp0666j¨eË– <&Mš„››ðOÿ ¯»éÓ5 ­V‹««+C†  !!óçÏßUlrðàA�5jİaÃðõõeÆŒ¥ºè]–øC`Lž~úi&Mšä^A1Eþ¦u&7nÜP #,,¬@ZùŸ^éááA•*UÌ–?žíÛ·söìYV®\©.÷òòâ÷ßW£ûüL÷*yxxààà .Ï{3ñÍ›7ÕÑ�õ÷¼#%}ü}«V­hÕªYYY:tˆ¯¿þšÕ«W°jÕª;>2Ö`0лwovìØQèúü÷”Ý-Ó• “=zðôÓO³zõj~ýõWuž¿ K–,aàÀ÷”ß½Ê{ 222ÌÖݾ}[ý½°U¬XQý=ï1Îuª4Ü‘/;;;ÒÓÓ©Q£†ºÞt¬é¼IIIQƒøøøx³9î&û“\/^¼HZZšú]”üçXQLŸÙÙÙ…^1µÇÃ~Ž !ĽÊÛ/ Çh4ÙÉ4õ`œ�ˆ�� �IDATmòï—÷‚xÞþ—‰i&üó¿µ°¾é^ë¼Ûêÿ°¼ù›f‡ä—˜˜h¶]PPP¡ÛÝ­ûQwwwwf̘ÁäÉ“9uê”Ù½ë£G¾§¯ë²YYYêà„N§+00Ó A³ yûCyûû:ooo’’’ÌŽ³ÉÝôéòÎÌû{ll¬Y?º¸Ø$++K½Ø76pqqÁÃãÐ2>(å~ЬY3 wÚÛO?ý䜼OÿÉÛèŸ}ö™:´Ÿ÷5qâD³´ííí äçíí͉'8vìß}÷cÆŒÁÖÖ–øøøbߘ¦üoݺe,äÍË?…4ï“‰îæŠÀ¥K—Ôßmmmiß¾½Ù ÄyO–¢Ò>qâ„ü½øâ‹Üºu ½^o6Y’òåýÐ6ì †­µZ-+W®$22’•+WòÞ{ïáëëKvv¶:_ž<<<ÔsâÂ… fßdu }BXÞã‘w ¯éêßÝß¼ò>äÈ$ï“1ó_ sqqQ÷éÕ«W¡ï…Í›7—º<÷ÃôéÓIKKS§Ÿ={ÖìªmaçäNß( Ó{ÏÍÍìììía:WösT!îUÅŠÕÛk®^½ÊÒ¥K lsûöm ƒY¿%o&66Vý½°ÛcJÚ¿¹Óvy;ßG-ôÿY‹-ÌÊ`z^aîæñýªûo¼All,6l`ÆŒ4oÞÈX’`–ìã?V§s¶nÝZ½x`’¿_Ÿ·?”wÚ`0˜Ÿc&wêÓ•d[__ßÇ&¶¶¶¸»»˜ÍÔëõfŸ)å:þhéKJJRï³ëׯŸÙ›¬yóæxxx�¹o"Ó“ý {öì¡OŸ>¦`fýúõìÝ»—zõêñüóÏ3}útõª):/Œiª§Á``òäÉܺu‹-[¶¨CË7.ô$+­ü?{÷ŵ7pü ,MÀ QÄ vc‰%vÑØ (ê5vÆ–W#ÖXÑhT,јXƒ5ö@°7,±%‚ JÔ…}ÿà2—•¶*‚Êïó<ó(gÎÌœÙiçÌ)C£FX¼x1GåøñãŒ5J™Ÿ¾†(í¤ŠŽŽVFDíBbZs‰»wïfú–-m�—.]Òš—þ LHHˆ2BåËNŸ>Mpp0ÖÖÖ´oßžñãÇ+…øì~Û¼b`` ÔšÞ¿ŸÁƒ³ÿ~fΜ©4C)V¬åʕ˰ìêÕ«9rä7nÜÐ]-­yfúš¬+W®è\sõ: iÔ¨�»víÒQíâÅ‹øûûóÛo¿½µí¿.ÆŽKݺuÔæ>i/(LMM•ëûÈ‘#$%%qíÚ5¥¿kN|}}Ô{È7ß|£î###™={¶rÌÞõsT!rCÚ' �Ì7ß|ÃñãÇ9rä3gÎÄÝݸ¸8ªW¯®<ÿW­ZEhh(‘‘‘ZÍ 3{iœ[š5k¦üܸqJæ?!! 6(ýæÒúåAj_Á£GKhh¨V7«üPfÞÆ¾ÇÇÇHRRŸ~ú)#FŒ wïÞÊüüÎì¿k>|ÈîÝ»Yºt)­[·fêÔ©@êËZ]^Êâíí ¤Žò¹~ýzbbbøöÛoyüø1ð¿ü{z9åéÒ›:u*×®]ãøñãJUkkkJ—.ýJe“Úµk©MAÓÊ:+W®Ôm6_äÇ( iÓõë×3ŒÜ’!ÞªU«”‘vôôô4öööSSSe™´‘~ÒFŸrrrʰŽ*£K988hºlÙ² £ÿ¤ÔÍÍ-ÓQ†ŒŒŒ4P¶‘ÙHSéGDܳgOŽ¿IÅŠ³ÑÊÜÜ\kDÐéÓ§gˆ³zõjMdd¤ÆÒÒRiè³Ï>ÓØÙÙ)ûlff¦5:ÑËëèÕ«—F­Vknܸ¡Œb•¶œ‘‘‘2šQÚÈVÁÁÁJkkk­‘ºuë–ï#O©ÕjÍ_ý¥uÌÓOzzzšuëÖ)qÓåää”!~ú£âããµF”4Ÿ|òI†õd6 hZ˜Z­Ö˜™™i�MÛ¶m•°¶mÛf8^gÏžUFʂԑ.ÓŽ5  Î÷ß:«Q@÷ï߯„×®][ ïÓ§Ö¨[úúúZ×wN£€¦¿fÌÌÌ´F÷8pà{sŽÊ$“L2åÆ4eÊ”,G¸4>Ô¨ÕjͲe˲Œãëë›éH˜i÷^µZ­ùä“O2ÜÏ3ˇ¥ýq̘1JøW_}¥„j•ÑšÝÜÜ”xÙš'«üZyÞ,·÷ýþýûZy5åoWWWMRRR¾Ÿù=¥?2›T*•fÑ¢EZËdvìÒ¦ððp­:ÓOÅ‹WFœ}•<]úãYÜô£€êZ69qâ„’o622Ò¸ººjT*•’¿.P£€¦qqqQ¾ ©ýñêÕ«—!^·nÝØ»w/M›6ÅÒÒ’˜˜¬­­iÚ´)óçÏϱ?¤¾ øôÓOqrrâñãÇR¥J-Z”mGbsss>Œ¿¿?%J”ÀÐÐ"EŠÐ¢E þøã¥V#·,\¸aÆQµjUìììP©TØÚÚÒºuk:¤ÕWrÀ€tëÖ-CSkkk¶mÛFÕªUyöìgΜaôèÑøøødØ^õêÕ™:u*Å‹ÏÐWÀÙÙ™ 6P¶lYLLLpwwgïÞ½˜››kÅsss£sçΔ.]šgÏž‘””D©R¥9r$ .ÌÅ_çõU¬X‘ãÇÓ¡CìííQ©TXZZâããî]»²ì6fÌFŒ ææætíÚUë­£‰‰ «V­ÂÍÍ-O>lîææÆ©S§èÞ½;E‹%66###j֬ɤI“ðòòzëix]õêÕSÞØ;vŒ}ûö©M?:uêD‘"E033Ãßß_ù&PNÌÌÌ8tèòÀ/^ðìÙ3ÜÝÝ2dˆòö}8G…"7Œ=šÃ‡Ó¾}{Š-Š‘‘ŽŽŽøøø°fÍ . ¤vùí·ß¨_¿>Q¶lY&OžÌÏ?ÿüF]tñÃ?°xñb<<<044äÉ“'”(Q‚Î;3mÚ4%ÞÂ… Y¶lžžž˜™™ajjŠ›››ò<¬óCYÉí}733£ÿþTªT ###¢££)V¬]ºta÷îÝÊÀ†âôõõ)\¸0nnnôéÓ‡°°0úöí«óò*Tàøñã´oß[[[¥ï`ïÞ½9vìvvv–É)O—ÞúõëiÓ¦ … ÂÎÎŽ‰'2tèPe¾®e6nܨŒ5bnnÎÖ­[3M_^Ò8p &íc™ée72æËBBBhÓ¦Mn¦Kˆ|sòäI¥9À’%Køê«¯ò9EB!„âU½JžnæÌ™Œ;HÜ%}·«wÅÖ­[3­ÐÉJú¾­i-Z”¿}�…B!„Bä)� !„B!D!’…xI5P«Õù !„Bñ^%O7jÔ(­‘÷?dR(„B!„„�…B!„¢€ B!„BR�B!„BˆB €B!„BQ@HP!„B! )� !„B!D!@!„B!„( ¤�(„B!„„�…B!„¢€x/ €ÿý7]ºtaÞ¼y9Æ}üø13fÌ AƒtíÚ•_ýU§mìß¿Ÿ¾}ûR£F &NœHTT”ÖüfÍš±dÉ’×Jÿû$üf8Í6ãöÃÛyºÝºuëF¹råèÚµ+!!!ZóW¬XJ¥Ê09sF§õŸ?•JÅ­[·ÞFò_INçZft9¯Ÿ<y¤I“hÕªÕ+¥ç—_~A¥RqãÆ %,..Ž%K–йsg*W®ÌСC¹víÚ+­WWÇŽ£zõê9r$Ë8 4iÒ„qãÆ)a/^¼àСC >///¼¼¼˜>}ºN¿§Bˆw‹Z­fÛ¶môïߟŠ+ÒµkW:”!^VÏŒ¤¤$V¯^M«V­¨X±"C‡åÒ¥KÊ|]ŸkW¯^eÔ¨QT¯^!C†hå3.\˜i^D¥R¨Ä{•|«xsaaa™“5kÖ(qÔj5?ýôvvv<zô(Ã:tÉ›å”WÍii®]»†••Û·oƒ½~3ït099™àà`<==Ù¸qcŽñãââèÒ¥ W®\aÔ¨QxyyѦMÖ¯_Ÿír¿üò Ÿþ95kÖd„ \¾|™:“[»ò^IѤäéövîÜIïÞ½iذ!óæÍÃÝÝ___8 Äyúô)7æðáÃZ“‹‹Kž¦õM½Î¹¦Ëy}âÄ |}}™:uê+¥çÎ;´k×.Cø Aƒ ¥}ûöLŸ>ÿý—Ö­[óðáÃWZvâââ˜2e õêÕ#<<<Û¸óçÏgÿþýZañññ|óÍ7”)S† &гgOBBBèÕ«III¹–N!„oߪU«X°`uêÔaöìÙ”(Q‚FqêÔ) çgÆÌ™3Y·n_|ñÓ¦MãÙ³g4oÞœ{÷îº=×®\¹‚¯¯/fff|ûí·˜››Ó¬Y3Ξ= ¤V¼œY¾|9�^^^¯œo¹#11€={öh›úõëpûömzõêE=ˆŽŽÎ°¼.y³œòª9m#ͳgÏ9r$±±±¹ù¼2U¾n=‰‰‰lذmÛ¶1vìØãïܹ“G±yóf *€±±1“&M¢eË–˜››gºñãdzdÉÚ·o¤^ÄžžžüöÛotïÞ=wwêWŵ {ïÉÓmnܸ‘Þ½{Ó«W/�7n̽{÷Ø·oÞÞÞ@j ««+µk×ÎÓ´å¦×=×t9¯7mÚ„ŸŸ„††ê”µZÍØ±céÚµ+?ýô“Ö¼iÓ¦áä䄞ž�UªT¡X±bœ:uŠO?ýôu-·oßæÚµk<x dïØ±c¬^½š–-[j…)R„%�žžžT©R…+W®P©R¥\I§Bˆ·¯uëÖtíÚSSS�5jÄ_ýÅ®]»ðôôÌö™‘À”)SرcM›6 I“&+VŒ°°0œœœtz®-^¼˜víÚ1~üxôôôðññ!::šE‹ñÃ?ðñÇóñÇkmû?þÀ××—*UªðôéÓWÊ·ŠÜ‘öÒ·^½zf˜ôèQJ—.ͺuëèÒ¥‹Ö<]óf9åU³ÛFzË—/ÇÂÂ"WöûM¼Ó5€ææælÙ²…Úµk£¯ŸsR×®]K—.]”L2¤Þ�®_¿NXXX¦Ë„††rñâE6l¨„}ôÑGtïÞ=CÍáñãÇùâ‹/¨X±"ƒ&""â5÷,{‡.Âk®ûÏï§ã²ŽÔ ¬Çã§QMU±#|‡ïFä TSU\ù犲\û¥íYux-·¤úœêÌÞ5›„g Ê2Í6cå¡• Û0ŒÊÿW™ÎË;zý†Ð롨¦ªˆ| ÀœÝsøæ—o˜þÛt¼æzÑtAS6žØˆF£Q–I|žÈ¼½óðžçM“MXv`ef•áÖÝš[š˜˜púôiž?@JJ /^ÔªÝKHH páÂ:ÿ†‰‰‰Ì›7ooo¼½½Y±b…Öü „‡‡5bá…ʤþýûÓ¯_?­ø{÷îåã?&!!èèhfΜIƒ ðòòbüøñܹs'Ç4éz®M›6aÆ)¿±.çõwß}GçΕgz»ví¢M›6jïÖ­[G\\£FʰL±bÅ´ VVVV�¹Z³æîîNPP¥J•Ê2NLL þþþÌŸ?ww÷ óÓ§R߬Aêï*„âýacc£õ Ó××ÇÅÅ…§OŸÙ?3 °²²âüùóJØÃ‡‰ŽŽÆÑÑÈù¹öèÑ#,X@«V­”xúúú´mÛ–åË—gÚ&&&†ÀÀ@úõ뇾¾þ+ç[EîHLLÄÎÎ.ÓÂ@ÇŽ7n666æéš7Ë)¯šÝ6Òœ9s†ï¾ûŽo¿ýWW××Ú×ÜòΟº^@ÉÉÉ=zggg­p�"##3]îßÿÅÑÑkkk­ð?þ˜'Nœœ¬„<y’Ï>ûŒÀÀ@RRR¨_¿>wïÞ}•ÝÑÙ‰ø|{à[Z¹µbh­¡6Õ­ð³5j+—"/P/€IÞ“øéâO, Y¨gQè"jºÔdA‹”±)C«àVÜ‹¾—å:\X€…±3|gÐ¥Rº„taÿ…Ôæx4l `ÿýŒñÃ0¯al½´•ëϯ뼯ƒ âÌ™3´k׎]»v1|øp¬­­éر£'>>ž7R®\9¼½½™8qb–ýù4 ÇgÿþýŒ3†ÿüç?\¹rE+ÎåË—qsscúôéôèу+V°lÙ2�Ú·oÏŠ+´Úoß¾þýûcffƈ#8wî“'OfêÔ©/^\§Â©.çšF£áÌ™3„‡‡£V«u>¯³»NþþûoŽ=J||¼våʾþúkfÍš…‰‰IŽi¿zõ*�eÊ”É1î«È.݆™3gÒ¤I|||²ŒÉ‘#GذaC† aáÂ…”(Q"WÓ)„"o%&&ràÀ*W®¬„eõÌ011aÁ‚Œ5Š!C†°{÷nú÷ïÏ„ ðððÈt™—ŸkiÏü´c'''�<xa»wïÆÂÂB«ð ¿¼—˜˜H\\T­Z•*M‡!ûc¢k9 §¼jNÇ=>>ž¡C‡HñâÅ_wWsÍ;ÝôUÄÆÆ‹™™™V¸J¥¢T©RYö±ŠŠŠ¢X±b .Lll¬VÍ“¿¿¿Ò_ÊËË‹+W®ðÓO?eZƒ’~ìú#NV©7žWé—7¶ÅX¥Àø(þ³NÌbdó‘è‘úF«O•>t¬•zÂz|âÁì ³ ÿ;\ÙÖËÙ5bpãÁ�x•õâèÍ£ºzˆFåqúæi–ý½ŒÛoSÔª(�E-‹²wí^ÓëîîNÿþý5j»víRkèÒªêÕ«Gݺu±²²âÑ£G¬ZµŠuëÖqðàAåæœæìÙ³,]º”Û·oS´èÓT´({÷þ/MƒÖZæùóç¬_¿???j×®Í'Ÿ|ÂÑ£G•š³~øÓ§O£V«Ù´i‹-RÚ–g×|1=]ϵµk×’œœŒ¡¡!111¯u^§×¿ºuë¦49HJJbĈ|÷Ýw”*UJkð—̤¤¤H¯^½2­…{[öîÝË´Ž[fBCCùì³Ï”¿GމF£ÉP;(„âý±~ýzôõõiÞ¼¹Nñ›5kF‡X¼x1‹/ÆÕÕ•ÀÀÀLŸ™=×Òž§/·¤Ië>ôrŸ­/^°páB† ’á-ò–‹‹ K—.ÅÁÁÄÄDvíÚEíÚµ É1¦kÞL—¼jvæÎKùòå3tgÉ/L0íM_Ë©ùÝ»w³¬’-R¤·nÝÊaLHHÀÎÎN«ß`úù†††|úé§œ<y27wC‹…é›·¶2³âò³Ë$''£2Èx¸M Mp1r!6Q÷ΨŽŽ<|šÚâZÔ5ª›UW ÚãUÌž=›={öpêÔ)lllؼy3-[¶dÖ¬Y 0�€6mÚh-ãããC:uغuk†Â\DDÕ«WW ™¥ééÓ§üöÛo„„„pãÆ .^¼¨œ#¦¦¦ôë×-[¶Ð¦MŽ=JãÆqwwGOO3f(o—|}}iРN7]Ï5CCC¥Ãëž×ééééiµ7_ºti†Öì,^¼˜ƒ²oß>âç†û÷ïãççÇ?þ˜csÎæÍ›“””Ä£Gصk:t`óæÍ´nÝ:R+„"7=z¶nݪSûÙ³gôìÙ{{{nܸALL AAAÔ¨QƒmÛ¶e? ³çZÚs2­Éiš´î!EŠÑ ?~ü8'Nœ`Æ ¯µ"÷Ô¨Qƒ5j(ûúúòâÅ s,�êš7Ó%¯š•´VJxgjˆßTäCCCêÖ­ËÍ›7µÂ>|Hbbb†*ý4DEEe¨Ú¿{÷.5kÖÌö@gÚçêmJß÷NºÔ‚è¿âi~êd5ÏRž½ÒòéEEE1zôhüýý©Zµ*ÎÎΰhÑ"† ’åHJ}ôuêÔÉ´èóçÏ•¾`Y<x0û÷ï§wïÞ,Y²„ &hÍoÞ¼96làÞ½{lÙ²…=z(û=hÐ ®^½J­Zµ ÂÃà .丯¯s®½îy•ëׯÀÚµk111A¥RQºti�J—.J¥ÒzËùÓO?áïïÏòåË34C}›æÍ›ÇõëשS§Ž2œóŒ3˜1c*•*Ã'YT*ööö|ùå—øûûgÔF!ÄûáÌ™3´oߞɓ'gÛü?½cÇŽ±mÛ6FŒ³³3•*UbΜ9|þùçÌš5K+nVÏ5[[[€ ]{îß¿¯5?ͪU«ðóóË´öHä/}}}êÔ©ÃÉ“'sÌ7ë’7{ݼ*¤Öûùùqùòe•<ÍÍ›7iÛ¶-*•ê­V&eå½.�ªÕj­Œ~÷îÝY³f qqqJØ¡C‡¨X±"U«VUÂþ7(JõêÕqvvÖjf–6úhçε¶÷âÅ åÿ†?þøƒjÕªåê>eE_OŸò&å‰yún}šÂÕÖ•³‰g¹ù¿>¯RHU«Õ@ƶÓi7å´ù))ÚM`“’’8}ú4åʕ˰Î?þ˜³gÏrýzæiЉ‰áǤK—.xzzòÉ'Ÿd‘©téÒ´lÙ’+V°nÝ:­öýÚG K—.lذgggvïÞã¾êz®½îy´sÞÖÖ–°°0­é—_~R‡A S›Ù±c=zô 88XiîšWzöì™!={öTÂ[´hArrr†óÀÈÈ(C[~!„ネˆ:tè@Ïž=ñóóÓy¹Ìòúúú”,YR«F/»çšƒƒݺucË–-ʳE£Ñ°cÇüýýµj�#""øñÇ3äEþÈ,/páš4i’cEˆ.y3]óª™100 (((CžÆÑёٳgFÙ²euÛÑ\ôÎ7 C£ÑËíÛ· ÅÆÆzôèAdd$»víÂÐÐÖ­[³råJ @÷îÝ‰ŠŠbøðá,[¶LèâäÉ“øøø°iÓ&|}})\¸0“'OfÀ€$&&âêêÊÆ±µµ¥Y³fZi™9s&ÖÖÖ8;;ÂéÓ§Y°`Ažý]ÊuaÞ©yØ¶ÇØÐ˜Íá›ólÛY©þIuÚ;´§ïƾŒ¬7c•1ËO,×yyGGGüüü ** WWWîܹêU«6lvvv<yò„¶mÛÒ¥KJ”(ÁóçÏÙ¼y3*•*CÓPH½˜Û¶mKß¾}9r$ÆÆÆÊwz µ]wµjÕX¾|9ÉÉÉ<yò„Ù³gk­COO/¾ø‚öíÛãçç‡:ØÐرc©S§E‹åÁƒ„‡‡3dÈ µßÚ§Ÿ~Ê“'O24 Õå\Óh4¯u^GDDðäÉîÞ½ËãÇ ÅÈȈŠ+²téR&MšÄ±cÇ(Q¢D†Ï#¤~Ë•+§ o}äÈ:uêDÛ¶mqrrÒz;•¾™Å›HHHàÒ¥KÊÇR/]º„‰‰ ®®®™Žòfoo ¤?,,Œÿüç?têÔ  gÁ‚yÚ\U!Ä›»{÷.]ºtáÙ³g4iÒDkôö´.Y=3<==©[·.ýû÷§OŸ>XZZrùòe¾ÿþ{æÎ èö\ó÷÷§iÓ¦XYYQ¿~}Nž<ÉÏ?ÿ¬ôùJ³yóf7nœi%@VùVœìíéÝ»7nnn”/_CCCNœ8ÁÂ… 9zô(:XÜ;w”ÃÃñ´´¤bÅŠ:åÍtÉ«f·Ì>K•vîæ×'«Þù`§N”æoáááÌ;—Y³f@¹rå°µµE¥JÝ +++¶lÙ‚ ˜0aîîî¬[·N« ¥¥%^^^ZUùÝ»w§páÂlܸ‘Õ«WÓ¬Y3fΜ™á»ƒ&88˜Û·oãããþ}û24 x›5DŠ&…¯ÿg3g:VìÈÑæÙö3c oÀ]~`Á¾LÜ?[c[ª:T…É´ÏáËôôôøöÛo©Zµ*Û¶mãܹsT¨Pž={òùçŸ©Í  į¿þÊ÷ß³³35bæÌ™ÚäCêÛ–eË–1þ|&Nœˆ££#]»våøñãÊüü‘iÓ¦1pà@¼¼¼ð÷÷ÏPôôô´û>þœ?þ˜   Î;G©R¥ T¾!‡««k–}s:×ôôô^ë¼^½z53fÌPþ®Y³&7f×®]899ááá‘éw0³²råJùùçŸùù矵æe÷¦ëUDFFR³fMåï´6ôûöíөƱdÉ’ôïߟݻw³xñb *D:u8qâDžV#„âÍýþûïÊÞ5j¤5ïÂ… fûÌfõêÕ,Y²„{÷îáééɪU«”<º<תT©ÂÞ½{Y¾|9£F¢AƒìÚµKk옘¾ÿþ{–.]ši×ìò­"÷i4¾øâ ¶oßΖ-[Ð××ÇËË‹ÐÐPå¸<y’¶mÛ*Ëøúú©#€ÚÚÚê”7Ë)¯šÓ6Þ5zÔ 80ÃŒ—GVÌNHHH¦51¢`úýÜïôú­7ÿsS§Bà»ê·ß~cøðáœ9sF§O%�üðÜ:uŠ•¥ëS�� �IDAT+W¾åÔ !„Bˆ‚dëÖ­:÷¸w/ãgÞ-Zôî×�Šwßþóû‰ŒÄÕÖ•ØÄX¦œÎ¬ú³ÞëÂÀš5kèÓ§Î…?Hí¼Þ´iÓ·˜*!„B!^ßûCï===v_ÙÍ©#§¨b]…ÿÔû¾•|ó;Yo$""‚-[¶0nܸWZ®Y³fZMT„B!„x—HP¼1owo¼Ý½ó;¹ªT©R¯Õ×­U«Vo!5B!„BäŽ÷ú3B!„B!t'@!„B!„( ¤�(„B!„„�…B!„¢€ B!„BR�B!„BˆB>‘óçÏóøñc<xßIù ØÚÚbee…››[¶ñΟ?OLL >Ì£” !Äë±±±ÁÚÚ:ÇûšB¼>ļ°®ùÑ™�³pþüyÔj5õêÕËï¤|Pþúë/.\¸€»»{¦óÏŸ?Orr2+VÌã” !Äë¹yóf¶÷5!„x}Èyáœò£�ÿ÷[îEs2âß·’†¥©X܆aŸVy+ëÏŽ�³ðøñcêÖ­‹F£Éï¤|PªT©Âü‘åü˜˜4hw Bˆ7deeÅÁƒó;B‘«>ä¼pNùÑï~ #.éAý›¼ÕtŒßxŒÙ;þbxóªou;/“`<xðAžðï‚ìšH³O!ÄûHî]Bˆ͇žÎ.?zñn4+ú5&%åíîÿäÏkñÕ’ßßê62#@!„B!„ø¯“×þͳÂïÉko§‰iv¤�˜ ]ü‹'O¸µ~="æÔ)^<}Šu•*X{{S¬m[Ì?ù$R*„B!Dîúk�s¢áÍöÿö¹½„nÏÃÛç©Òb$ÕÛNÊGOOïõø¤�˜ƒìü¿{öúå—X<}J‘¤$Ê‘úƒÆ…„ðøàA.MJ¹I“(ãïùt€ß5ùu¢ !„BˆW÷!uɦh4¯½ïG׺y .Ÿ¸S½r'~ß½€jm&fLÇk­ýÍIð5]œ2…ësæðñÓ§ØÛÛcøÕW¨ªTAßܫӧIÞ¹“âgÏrá›ox°g^;wJ!P!„Bˆ÷€&åõ €/c9<¯úÞ¬Ý~ŠæÆú8•«&³þ„ùT4È—Á¡R©2þùçÂÃÃiÖ¬·oß`÷îݨT*ž?@rr2?F­V¿Õtjþ[òyzÊ•ÿû?Ê>}Š]»v;†ñ´iĹ»sëéSTãÆaxè…'L Ò³g<=y’k‹eº®   ŒŒŒ”ÉÇLJ¯¿þšC‡e¹ý÷}ÊK?&))I+¬iÓ¦,^¼8OÓ!„Bñ¾y9÷üùs<ÈðáéW¯õêÕcÆŒDEEeˆ»ÿ~úõëG­Zµ˜8qb¦qbbb˜1cÞÞÞtëÖ_ýõÉj4R^c:³û{‹bmmÇ—mëqáüYÊù É4n~Õ®æKÀÙٙÇg˜Š)@JJJ–ËÞ½{"""Þj3;Y’Ÿ=ãd§N8%&bZ§Æ‹c`e…¾¾>·¶oçüÂ…¤<}о¡!ðIBçÇ#þúõLO>ggg8À2d… ¢qãÆŒ7Žäää|/°½ÏÀ:uê°mÛ¶<ݦB!ćàå<\||<S§N¥téÒŒ;–/¾ø‚zõêEbb¢oûöítèÐOOOÆŽË•+WèØ±#111JœØØXºuëÆÕ«W9r$uêÔ¡]»v¬_¿þȾjÁïaDgâläêÑå”)çŽF£!ôÏ#”®×ûÒ^Y.—ò­ ¨¹¹9µk×Ît^•*UسgO§H71¡¡¤ÄÄ`i`€Éœ9 §‡Þ'3{{ ÂÄÒ’/^ §§‡A@�…~þ«Û·¹·m¥2¬ÓÌÌŒZµj)·lÙ4h@:uðõõÍË]B!„"KKKöìÙ£Õ‡®zõêT¯^+W®P©R%™0a‹/¦]»v@ê ùÚµk³cǺví À®]»xôèÁÁÁ*T�ccc¦L™B‹-077ÏûLçU*.4 Ïc¡—|¯ŽÉj.s> Œ>*A¥–c²\O~õ®Ì·À섆†¢R©ˆŒŒÌ0ïîÝ»|òß‘5+T¨€J¥âÊ•+�ÄÇÇ3gÎ4h@Ë–- &99HýÖ‡J¥bÿþý 8ªU«žm:2{[ð(, £/Ð+U }gg­x¦ŽŽ˜»¸’’ò¿“F¥B¯ALŸ=ãÑdùâå°5jàççÇ¢tMGãââ˜;w. 6ä³Ï>cãÆ¨Õj4 QQQØÙÙ±mÛ6zôèA¥J•àîÝ»h4Ö®]K‰%HHHPÖwïÞ=LLL8{öì+¿5yøð!Û·oÏrzøða®Ô�6mÚ”5kÖ@¥J•øâ‹/”ãvæÌôôô8sæŒ?%%…ºuëòã?R­Z5.^¼HçÎÑÓÓcÓ¦MJ¼sçÎáç燻»;;väÏ?ÿÔÚ4i5jÔ uëÖkÕJ7mÚ”+VàïïO… 2]‡B!ÄûL—|ë³gÏ�øè£Rk½BC¹téõë×Wâ.\˜®]»²aÃ%ì§Ÿ~¢S§N˜šš*a>>>\¿~¿þú+ßó£) ))ºMñÿÞBO;fæ£(Q¦'fö8UûÏÏ}ÑhÈzÙ|ªÌ·`||<'OžÔšÒ rÙ±±±açÎ�lÙ²…Ó§OS¼xq’““éß¿?QQQ̘1ƒ!C†0oÞ<‚ƒƒµ–oÒ¤ *T`ìØ±¸ººf»­ÌN–˜cÇ0JJBÏÝ””­ÉÄÞ Ôj5ÉÉÉJ8nn1™œÌÙmËÃý{÷’˜˜ˆZ­fРADFF2mÚ4ÈüùóÙ¸q£²žØØXÖ¯_OÇŽ $**ŠAƒñâÅ 5jÄýû÷ùóÏ?•õ>|˜ àææöÊ@+++4M¦[úùoZ�3f •*UbÞ¼y+V ///"""¨P¡5"$$D‰{ñâEŽ9BãÆY³f ®®®Ìœ9“³gÏÒ A%Þ¨Y³&K–,¡L™2´hÑ‚{÷îG‹-HJJbÖ¬Y´iÓ???~øá­t-X°€Zµj±xñâ ëB!„xße•—»ÿ>G%88˜¡C‡ˆ³³3†ÿý‡ yÁ%JpòäIÔj5jµšãÇS¼xq­8vvv�Ü¿?ßó£:å‰S‰»=Äè6˜˜…÷¸4woô"9evNxúp ‘ ²]G~È·& ·oߦN:Za#FŒ`ÆŒÙ.gbbB™2e�(]º4åÊ•àèÑ£ìÝ»—[·najj @bb"sçÎ¥K—.Êò;wî¤I“&:¥1³£oaA2 ŽŽÖ*ü©ÕjŒíì(T¬jµš””¥¨~ô5`P¨P†õ¥“ò2+++ µ@AHHW¯^ÕÚ¿ùóçÓ±cGeùaÆQ½zu õML­Zµ¸{÷....ôë×={öP·n]�‚ƒƒéÞ½;zzz¯u¶hÑFï¿þª„µlÙR Ï-£G¦G�Ô«WË—/³víZ&OžL¯^½˜>}:~~~€pttÄÑÑSSSœ©P¡‚Ö:ýüü”óÂÃÃٳgó×_áääÄ–-[°²²bÚ´ièëëS¿~}ÌÌÌèÝ»7]ºtÁÒÒ€¾}ûÒ©S§L×!„Bñ¾ËªÆçŸ®ü@JJ zzzDEEQ¬X± ËYXXKBBÉÉÉÄÆÆ*µi (Y²¤ÒWPo+?š¢!Û::†¸¿;ab¢Á©´/úúѤ$¯#!¾V$î±&–%0¶é“õz4ù3 h¾Õ�º¹¹)o�Ò¦œ Ù‰ˆˆ :: eDÑvíÚe¨Uüè£Þ(ÝV5jðÂÜœ¤³g•B^ZŸ¡•VÊþ$''“œœÌ³3gH�Šü·`¦«èèhJ”(µµ5×®]#::ÌÌÌ033£S§N\½z5ËåÓö5>>€Ö­[³zõjâââ¸qã»wï¦aƯý[@êÖ²eË ÿÏMéÛ™ëééáííMXX�>>>œ?žÓ§OóâÅ ‚‚‚´nHº011ÁÙÙ™ØØX�ÂÃé_¿>úúÿ»<<<<xòä‰22mNëB!„øP5kÖŒ'OžpãÆ ~øáºté¢À,--¹uëV†ÂWBB¶¶¶J>6-,½””îÝ»‡µµõ+¥çmäG5š¬|INŽ#öfÌ-L°²)«äU“£±ø(‚âŸl½;tAƒA–ë)p5€¹Í‚… Š‘‘‘þ&Ïì­‡e•*$i4<{ôˆøà`Ì;vTâ>½s‡ØˆLþûÖ#%%…çW¯’°w/‰ffØ×®i `voW|||€Ôb .ÌÑ£G144Tâèëëk-ŸÙºÒÂ<==±±±áĉܹs‡~ýúakkûÆ'_‹-(]º4¥K—ΓÙÀÀ� �ììì:t({÷îàéÓ§Y.”ô…½§OŸføÄˆJ•z©¤ÿí³[‡B!Äû.»fŠØÙÙÑ­[7Î;ÇúõëiÙ²%ööö<xð@Ÿ"ÍÝ»w©Q£zzz¨T*êÔ©ÃÍ›7µÖÿàÁ±··å<ençG5)šÌ¿Ý§yFÂín˜2ÄÜÜ‘¤Ä‡›X‘’œÌ³§O(dnõßxw EƒF/›ZÄ|æ½Ì±¦ê^¼x¡„•*UŠØØXbbb(V¬˜2½Is¼ÌÚé®P•+óØÐ˜)Sxvã†Rã—ðÏ?<¾xQùûEl,1ÆñäÙ3qúüsû�†††²téR €F£¡dÉ’ÄÆÆ“““29::fZ�ÌlýFFFôîÝ›_ý•uëÖѺuëWîû—ÕTªT)㾪—:vì˜ÒÌ ]»v¬X±‚Í›73`À�LLL”yúúúÊ÷#uU±bEvïÞ­tj†Ô¾…...8ÿwà!„BˆÝËy¸´n/‡)ýíªV­JñâÅ Qæ?}ú”Í›7Ó¾}{%¬sçά[·ŽØØX%ì?þ |ùòT®\9ßó£YÕÚ%Üù cU<…-yðïI [–D£I!6æúz<K|”ã: ìg ^EÚ0°'OžÄÍÍ [[[œÙ²e ÉÉÉ899Q±bEüýý8p “&M¢hÑ¢œ?GGÇ7næø²jAAì«RUl,ÉÍ›c5z4f:aQ©…+WF­VóìèQCÒ­[<,T  ³h~š  ÎòøñcÎ;ǼyóX²d ¥K— |ùò 4ˆ¡C‡2nÜ8¹páZƒ›ä¤I“&üç?ÿáã?Ö*D½Ë&OžLáÂ…ñôôäÈ‘#9r„ÿû¿ÿSæ{zzbaaÁ¬Y³¸xñ¢Ö²uêÔá—_~ÁÝÝ å÷ÌN‡˜={6tèИ˜ÆŽËÌ™3•& B!„ÍÙ³g3f :t xñâræÌ~øáeÆÂ… 3~üxüýýIJJÂÅÅ…-[¶`cc£5G«V­øñÇñ÷÷§sçÎ<|ø¯¿þš… j½ÌÏ/YÐÔOÿÄÒ®"÷n…àX¼>�ÿÞ9€6˜}äHÜ“›&E“‚FÏ$Û¦^>�ß‹@OOOFŽI@@�ááᘚš²~ýzÂÂÂð÷÷çÒ¥K�LŸ>~ýú±téR:uêÄï¿ÿŽÁm;³7ÆT "ÖÜœÇÏŸ5n·Ýݹߦ Q]»r§R%îwíÊ£[·ø·P!>6 [oï,ß@ܹs‡† Ò¢E V¬XZ­fÿþýjè&OžÌW_}ÅŠ+øâ‹/Ø·o9Ö�¦ÿ;­à×§OŒŒŒr­ðmÕþtïÞ³gÏ2`À�þùç8€ƒƒƒ2ßÈȈöíÛÓ¼ysÊ–-«µì¸qã(R¤½zõbß¾}:mÏÞÞž R©<x0›7ofΜ9tüos_!„Bˆ‚"}>ÎÕÕ•>}úpêÔ)ÆÏ7ß|Cdd$¤ZµjJ¼N:±bÅ >Ì·ß~‹³³3+W®ÄÌÌL‰ciiɺuëpqqaÊ”):tˆ   š7oþNäG5d¾¬I±yyôTèëòÏí#$ÆÇ IC#Sâco’˜pCw4¨²OK>5Õ8p fàÀf¼JÓÉÚ´i“›éÊw[·nÅ××WëÛo/Küç¿úŠØ3gPi4¨ž>EИ˜ðÂØ=33ª­YC¼Kxnß¾M… 8yòd†ÂR^Ð××g÷îÝYž/[·nÕÄ¥iÓ¦´nÝšd¹NµZ——C‡UFåBˆ¼´yóæî9(„(ØtÉ ¿¯rʺø‘ýc[£NÎ|ß5ê{¨ŸlE·‘äјajâÄG¶NÜ»¹KëJ$èù ²ÊXÆJOe OÃo·qñÿ¾Ð)Ý[·nUÆÑEfŸ'[´hÑûÑô]eZ´(µwí"öìYbˆ>v ul,V^^XV­Jôó;™ZöîÝKƒ ”Oi|þüóONž<‰··w~'E!„B|�²@¿(ª"ƒ È žGòd5Oã÷+µ+ÒãGg0*6.ëåÓ¶‘Í�1o“�³¡k5±E… XT¨€ó—_fºŽwÅóçÏY±bþþþ@þ¤ímlsË–-ôíÛ{{û\_·B!DAõ&]xÞe¯2LŽ K¢o3}›)™®#§mä)�fãC;é 9zô(ÓWÝîž={rŒ“~@!„B‘;>´¼pšœö©š«êÿ~Ø>ù-55Ð×G’B5W»œ#ç²÷b!„B!„È •œmù~×R4)èa ¯Ÿ»“)šæì:C•¶y¾R˜kkkRþ[òÿ;¿æ}}}RRR°¶¶Î2ŽM¦H!r‡Ü»„š5/¬K~tDËjÌÜÊøÍv3ê­¤£š«UJØмÚ[Yv¤�˜+++.^¼H¹rå”Ï‹7“’’Â¥K—°²²Ê2޵µ5aaaT«–÷ƒB¼ŽÐÐÐl3Bñ>úPóºäGFµzwFñÏmR�ÌBùòå9wî§OŸæÑ£Gùœ‚µµ5VVV”/_>Ë8nnn\¸pƒòðáÃ<LB¼:¬­­qssËï¤!D®úPóºäG?tR�ÌF… ò; ’»»{~'A!„¢À“¼ð‡IB!„BˆB €B!„BQ@HP!„B! )� !„B!D!@!„B!„( ò|мޤB!„B¼“|||òt{ùòˆFåÇf…x%W®\¡L™2ùŒwžüNBlñP÷YˆÜ"×¶}ûöåù6¥ ¨B!„BùR¨Ñhòc³BèLOOF#çjäw¢`+ˆ÷€‚¸ÏBä¹~Þ R(„B!„„Ô� ‘yC¥ù„(Ø â=  î³¹E®Ÿü'5€B!„BQ@äK0­ä?lØ0Œ³œ¦NJtt4-[¶äÀÊr2É”ŸSJJ -[¶ÄØØ˜‹/¾Ò²ñññÄÅÅi……‡‡Ó¼ysnݺ•ïû–ÓªU«´®ãÆ3zôh:”«Ûyüø1‰‰‰ZÇeذaÌ›7/×¶QŽ—L2åöôò=à³Ï>cñâÅ®¥×™’’’ˆ‰‰ÉÓýÙ½{7ÆÆÆ<{ö,_öY&™>ôéåë§nݺŒ7Ž7n¼Òzj֬ɚ5kò}^uÊùZøÕW_qàÀ8À’%K�øé§Ÿ”°öíÛçë#Df®^½ÊÞ½{8tèÐ+-;{öl&L˜ ¦Ñ¤^>$ÎÎÎÊu<dÈ *D“&M?~|®íkƒ ؾ}»VXnß+ Êñ"·¥ÝöíÛGÇŽY²d  àÅ‹o´Þ}ûöáààðN^ƒokŸ…(ÒçÆŒÃÝ»wiÑ¢ÿüóO~'탔¯}�ÝÜÜ”0�ªT©‚«««Vü´LžEnˆŠŠbÞ¼yøùùagg—e¼¬^>„„„о}{jÖ¬ÉÚµkéÝ»7:m;³7>•+WfÇŽÊü÷ÍËû£Ñh033£fÍšJX‹-hÔ¨ÞÞÞÔ©S‡¦M›æêöÓÌ™3'CØ›®ûC;^Bä¶œîµkׯÍÍ5jЧOêÖ­›ëÛ|›Ò¶“~›ù±ÏB|(tÉ7Ô®]:D§N^i½òlÎÙ;ß0!!SSS8�ÀáÇéÒ¥ AAA|öÙg4hЀÀÀ@îß¿ÏÂ… iÞ¼9Mš4aýúõZoããã ¤Q£F´nÝšM›6‘œœœ_»%òITTãÇçâÅ‹Œ?ž¨¨¨WZþÅ‹¬ZµŠV­ZQ»vmBCC¹téR†xû÷ï§wïÞT¬X‘¯¾úгgÏ2mÚ4¦OŸÎâÅ‹155eÔ¨Q�„……ajjJdd¤²ü¥K—:t(ôéÓ‡S§N)ó<x€½½=¿üò _~ù%•+WføðáÜ»wï5•¼Q³fMüüüX´h‘–ÓuyèÐ!zöì©üŽÁÁÁ�ÔªU‹K—.ñÅ_`jjÊÏ?ÿ @Ïž=™={¶²|‹-XµjÇÇÃÃnݺ¦Ì¿té“&M¢I“&xzz2~üxîß¿Pà—¹©|ùò888póæM�™2e «V­¢qãÆ�ðüùsV¬XAëÖ­iÔ¨sçÎ%>>€={öжm[�ÌÌÌ055UÖŸÝri""" jÕª´mÛ–_ýHÍ4îÝ»—ž={R³fMFÍ;wÞ‰}Î)í�§N¢_¿~xxx0räHnß¾­Ì‹ŽŽæ»ï¾£Q£F4hЀ‰'*ûÁˆ#ðôô¤]»v|÷Ýw<þü÷[ˆÜ`nn޽½½Rƒ¾iÓ&ªV­ªg„ ʳ93)))lÞ¼™:P¿~}¾ÿþ{âââÞjºßïÌ( Ù½UK®Ñhغu+®®®ðäÉFÍ×_ÍÔ©Sùú믹{÷.ƒ ÂÒÒ___’““4hNNNL›6'Ož0uêTÔjµÎoÄû/**Š &(…¾´Âà7ß|“iM`fo‘þúë/Î;GÍš5±³³£T©Rìß¿www%ÎöíÛ™8q"cÇŽ¥{÷îœ?}}}ºuëÆ?ÿüC\\#GŽÄÌÌ,Ó7Ê/^¤aÆL™2…víÚqäÈêׯϨQ£�±±±¬_¿ž=zгgO–/_ΠAƒØ´i*UÞ^ÖY½mË,ÌÃÃyóæ‘˜˜ˆ¡¡a¶×åéÓ§ñõõeéÒ¥ôïߟ{÷îQ¸pa4 +W®¤mÛ¶ôîÝ›¦M›booŸá·L³xñb†NÛ¶mÙ·omÚ´áøñã-Z”;wî`nnΰaÃHLLdÓ¦M 2„àààöx ‘Ût¹¼xñ‚'Ožààà ÄŸ6mݺucÀ€ØÙÙ‘’’ÂèÑ£¹wïC† !))‰ï¾ûŽððpV®\‰‡‡sæÌ! €“'O¢¯¯¯¬+»åôõõ‰ˆˆÀÇLJQ£FѦMnÞ¼©,¿iÓ&f̘Á„ èÞ½;6lÀ××—ƒbcc“é~æÕ>ç”ö'NЯ_?¦M›¦t«éܹ3;vìà£>bÔ¨Q<þ\iÊ……?¦]»vtèÐÀÀ@?~LTT†††R{"Þºœ®Ÿ¤¤$öîÝKdd$uëÖÍòùžÕºÒþ^ºt)!!! 2•JEPPãÆãûï¿GOOïmìÚ{#_rjµ:CXÚ[ÿääd­ùiÿOIIA­V+ñ†Ž……�7nÜ`ûöíøùù)Ë8q‚ýû÷ãããÃñãÇ áòåËJSÓ„„.\ÈçŸþvvR¼S¢¢¢˜4i<Ð ŒŒdܸqLš4I«¨¯¯Ÿá\عs'Ÿ}öööö�´oßž5kÖЧOT*‰‰‰Œ7ŽY³fáãã¤6cHcccƒJ¥¢L™2�Zç´Z­F­VȈ#èÙ³'ZsÍܹsY³f’&???ªW¯€……uêÔáÖ­[¸¸¸äÚï–“Ì~§ääd4M¦×¹¥¥%�?&"""ÛëòÒ¥K/^œV­ZQ¨P!åÍŸZ­¦dÉ’˜˜˜P´hQ­ßR£Ñh¥G£ÑгgOÚ´i@¥J•˜;w.aaaØÙÙáíí···’>'''4hÀ?ÿüƒ££ãw¼„ÈmºÜâââØ°aeË–¥jÕª¨ÕjRRRðöö&00CCC�ÂÃà âìÙ³Ê=ÖÕÕ•jժѷo_<==)V¬�eÊ”A__µZÍÙ³gs\nþüùôë×¾}û(MÍž<y¸qãX´hõë× zõê4nܘM›6ѧO­k^__?Ï÷9«´«ÕjþïÿþáÇ+Íê+V¬È† øóÏ?©[·.[¶laîܹÊs(íßK—.Aó%ÈIF�� �IDATæÍ)_¾¼²™Ý·…ÈMY]?—.]¢P¡BJXÙ²eùùçŸqrrRž½/ç-RRR”ò •ˆ‰‰a̘1„„„P¡B�lmm©T©#GŽT®·‚꽬|9žµµµ2*X{{{®_¿ŽF£áÚµkDGGg¨å±µµ•7]„­­- .Ì6ÎËçÝËçb||<AAAtïÞ?ÿüHÍÈŸ?žsçÎQ¹reî޽˵k×(S¦LŽçyfÛU«ÕìØ±ƒ   ­ðºuëòå—_*£Ð½¼láÂ…•4æå9Ùï”Õ[:€Gáââ‚••UŽ×e­Zµptt¤U«V´k׎† RºtéLÓ]XúôQ¼xqbcc•ðƒ*шˆ õícVoßçã%DnËêpùòeå<øôÓOY¶l™R‹¯Ñh°´´D¥R)Ë^½z•ªU«bgg§„•,Y’Ò¥KsýúuªW¯ži §åªU«Æo¿ýÆüùó3\owîÜáÎ;Z÷lcccš4iÂùóç3Íwäå>g—ö¤¤$vîÜÉÎ;éß¿¿Ö¼ØØXôõõùæ›o:t(¡¡¡øøøP¯^= *„««+]»v¥cÇŽtíÚ•† âáá¡sv!^WV׋‹ «V­`åʕܻwOOÏ,k÷² Óh4ܺu‹ÄÄDêÔ©“!~\\\¶c@DÛ£œªqÍÍÍ)\¸0üñ‡òÖM—å„Hïĉ<xð€9sæ(ƒ¤9pà�•+W&!!xýÁAÔj5ÑÑÑú§bnnþÞ?œÃÃÃñññAOO/ÇëÒÉɉ]»vÆÁƒiÙ²%]ºta„ otíêëÿ¯ëóÚµkùé§Ÿ;v,=zô 22’-Zè¼®ýx ñºŠ/Ί+Ð××ÇÞÞ''§¯‡çÏŸ“”””!ÜÂÂ##£×^îùóçÜ¿?ÓûrZŸ·—¯accc¥e‚®ÞÆ>g—v###Èpßúè£�èÛ·/­ZµâðáìY³†±cDzvíZÊ•+ÇÂ… ¹rå ‡fôèÑX[[³|ùreY!ò’©©©ÒÒ§P¡BÔ¨Qƒ|}}•8¯2ú¯™™�¿üò ¥J•ÒšWÐ ðÖ�¦ùí|Vëýä“Oˆ%::š*Uªä˜!2{CõË/¿Ð½{wæÏŸ¯wæÌ™¬]»–âàà�ÀåË—•¦Jéééé)µKé·•ö¯‘‘Í›7çèÑ£4lØP‰N³fÍ”~éÓøò:ò»0ý¼ôþúë/–-[¦|P—ëÒÀÀ�OOO<==ñðð mÛ¶ 0�[[[ôõõyþüy¶5·YÝÒâìÚµ‹-Zàååü/3˜6ÿC;^B䶬îfffxzzfˆ›ÙòiJ”(AXXÿý·Ò4úßÿ%,,Œ’%KjÅ}ñâ…R(Ìi9ccc¼¼¼8}ú4Mš4ÑÚ~±bÅ055åôéÓ8::©…Á#GŽÐ±cGk�ßÖ>g—v===š6mÊéÓ§éÓ§O†ciÛ±··§}ûö´mÛ–víÚñûï¿S¶lY µ)m™2ehÑ¢åÊ•ãÌ™32b©x«tÉ7”)S†0sæLêׯ‰‰ fffܹs‡¤¤$Œ³\WÚßŽŽŽ¸¸¸pëÖ-¥ywfÛ*¨>ˆÀœ”/_žÀ˜1cpppàâÅ‹888dzRñ²ÈÈH~üñG¶lÙ’a^£F˜>}:gÏžUFa›0a)))ØÙÙqáªV­J¹råøøãY½z5ÇÇÔԔʕ+gXßàÁƒiÖ¬EŠÁÃÃK—.¨|{ð}@hh(MêÛÏŸ?ÏüùóY¼x±ÒŒ3§ëòàÁƒDDDàî±1û÷ï§jÕª)R€5j°cÇÊ–-‹¹¹9%K–|åtº»»³qãF%£¤5¿ /!ÞÕ«Wç³Ï>ÃÏÏ??? •A Òúð¤½XÛºu+%K–¤B… :-7xð`zö쉣£#•*UâæÍ›*TˆÆ3qâD†ÎÓ§O)Z´(»wï&>>ž–-[ÿ«I ¥lÙ²ZÃäÅ>g—öÒ AœiÖ¬qqqüùçŸ 4&OžL­ZµpppàáÇœ={–~ýúqûömÖ¯_O5°´´ä¯¿þÂÔÔggç\Û7!ÞDŸ>}X¼x1¿þú+íÛ·§bÅŠ˜šš2wî\¼½½¹uëÛ·o×z1âààÀùóç¹zõ*¥K—fÒ¤Iøûû£R©¨T©·oßæÑ£GtëÖ-÷ìÝðÞÕ�æô6=ýréãLš4‰àà`V®üöÎ<®¦­ã¿ÉÍXÉ©›Û"W%J‘«¥(RRט$ ½!¡·™’xÉ<¦8÷ŠH‘!CE…ŠdJ† šPç¬÷>g¿íΘ©°¿ŸÏùÐÚk?kØk={­½žõ¬]ÈËËƒŽŽœœœ~ø/� ‚iܦ.]º„nݺaÈ!|mFKK }úôÁ¹sç ££ƒ  k×®Gqq1† B0nÜ8<~üÞÞÞÐÓÓ£™’òÚëСC‡Ý»wãàÁƒ6lΞ=KíQµzÔ+€ ÿåý¿  �&&&øé§Ÿ`ll mmm$$$ W¯^´¸¢úeçÎñøñcDEE¡²²#GŽÄ®]»ÐªU+B°páB¬^½óæÍÃŒ3ð믿òå‹÷aõäêêŠ÷ïßÃÏÏêêê˜8q"ÒÓÓ©8ßÛób`øÜÓÃÝ׸ýKII!,, ‘‘‘X½z5:wî KKKØÛÛSñúõë‡mÛ¶aÛ¶mPRRBHH”••ÅÞgff†Ý»wãСCX¿~=´µµ1eÊB0kÖ,ÈÉÉ!** %%%011Á‘#G(Ï¿:::ðôô„¯¯/V¬XA­ö­2‹Ê{ïÞ½qñâEDFFÂÍÍ rrr066FMM Ú´i555ìß¿ÙÙÙøõ×_±zõj˜™™áÕ«WhÕªÖ¯_üü| <l6***ŒNbø¢Hª3ÔÔÔàãヘ™™¡[·nˆŠŠÂÆñÏ?ÿÀÄÄNNNxõêuߢE‹ð÷ߣ°°Û¶mÃøñãÑ©S'DEEaíÚµÐÐÐÀرc™6€åææFÜÜÜø.ôèÑCb!‰‰‰”—=Iâò\£30´T¤¤¤››Ëg7Î@‡©'†›Qüˆef`ø\0ý‡Ÿ””Ês¼8Øl¶Äq<s8""¢å¬�20´$DÙ¨3ü¦ž~l~Dð#–™ásÁôŸ–”ø( ßͲØ7® Íï€QÑ0õÄÀðcó#ê€±Ì Ÿ ¦ÿ4?Í2¬­­mŽd$¦U«V¨««cÚª˜zb`ø±ùuÀXf†ÏÓZÍ2LNNnŽdšLaaasgᛀ©'†›Qüˆef`ø\0ýçÿtèÐá«§Ù,À‰'6G² -†øøø¯ž&ã†á™�200000000000ü 0@†„ïr˜šš èèèÀÂÂûöíãó6TVV†àà`ÁÁÁ'Nœ ]¯¨¨ÀÖ­[1yòd 0�ÈËËãKëܹs˜5k ???5)¯¹¹¹X´htttàîîŽ[·nÑ® �æææ000@pp0JJJ$–ÿøñcØÛÛcÓ¦M|×ÄÕAcêêêpàÀtéÒoÞ¼¡d°X,¡?^¼†<þœª[ L™2 |ñ1eÊhhhÀÁÁA`œ)Ó‹/àëë L:±±±båJÂéÓ§Áb±ðþý{3|><‡Ò‹XUULLL°dÉ‘ñ–,Y‚^½zÑÂîܹƒ€€�Œ3†Òk999_2» ?(UUU¨¨¨hîl4 K–,Á‚ ¾j:æææØºu«Ð¸077G~~~“Òàr¹ÐÔÔDttô'å�8ÊÊÊPWWG —÷ϤãW¯^:®±°°ø¢yeà§%¯ Àb±¨ñzII ÆŒƒóçÏ‹½WXh¾» `ZZìììЫW/ÃÒÒعs'§¢¢ööö¸wï|}}addkkk<xŠ3wî\ܸqvvv Aaa!¬¬¬ðúõk*Î?ÿü[[[èëëÃßß¹¹¹°µµEii©DyÍÍÍ…¹¹9Úµk‡   ´k×fffT£zûö-tuuQWWOOOxzz"&&^^^bÏOáp88r䌨¨(¾ë’ÔACòóó1mÚ4899Ñ&u²²²¸rå ßoìØ±prr‚‚‚Ÿ¬'Nàáǰ³³Chh(TTT`ff†¸¸8*ΩS§0}útŒ5 ›7oFÿþýaff&²ƒIR¦ÒÒRŒ3ÁÁÁÐÓÓƒ••8 ²>>BÈg“†sçΉŒ“€àà`¾ðµk×BZZîîîX¶lJKKabb‚Ƕü10�@HH–.]ÚÜÙøêp¹\ÄÆÆÂÀÀ E¥Cù¨s×òóó‘““--­&ßÛ˜gÏžANN÷ïßÿdYME’qGCz÷îÍ7®ILL„¬¬,lll¾rîZ:’¾ã›³4¦Y¼€~I´´´’’‚Ÿþ™ +//ÇÖ­[1gΰX,œ:u oÞ¼ÁñãÇ!++ �‘‘ÁŠ+`mmöíÛ#88ÊÊÊ`±X��mmm())!%%¨®®ÆÒ¥K±}ûvØÙÙ� 1xð`ÄÆÆÂÙÙYl^·lÙ[[[øûûƒÅbÁÔÔ%%%زe ¶oߎŽ;"-- ***Ô= 055E@@�TUU…Ê®©©ÁáÇñï¿ÿ \)¤’œœ 9röööTx›6mø^@/^¼@\\œÐÉš««+U¯�0nÜ8”––âøñã;v,� ** ...˜9s&�ÀÌÌ ÏŸ?Gbb"þøãr%)Sll,¤¤¤iii˜™™¡mÛ¶X¾|9¬¬¬Ð±cG¡uÊÐrptt„££csgCbvìØXXX mÛ¶-çêիسg¬¬¬„ÆyõêÜÝÝ1uêT\¹r…vmïÞ½´¾gmm UUU\ºt jjj/†z ••…´¨t´µµ%²¢iLvv6ÔÔÔð믿6ùÞ–„$㎆téÒ]ºt¡…%&&�,--¿lf¾)äååqæÌ™æÎF“ùîV�eddh“?�èÞ½;ÊËË©úþýûáèèHM€z„àæÍ›�€ž={Ò”…œœ�àÝ»w�€7n ''£F¢âtîÜÎÎÎ8tèØ|¾yó›7o†µµ5•Ž””lmm±cÇ�mò�ŠŠŠ�€>ˆ”ß¾}{°Ùl 6 RRüY’:hˆƒƒüüüø¢ þý÷_èééÁÐÐ@ýJçèÑ£)¹ ë¨ÿrRSS%%%*¬mÛ¶ÈÈÈ ÊÉår‘_~ù…ŠOOÏ&=×¼¼<üöÛo––¦â˜ššâñãǸqã†Èrñ&ÕÐÐЀµµµX³ÙƼÿÛ·o‡……ŒŒŒJ™IíÝ»;wFee%?88ššš4³ßˆˆˆ”й¹9öïß `àÀpvvFFF�àÖ­[4ó  þùaß¾}(..FçÎ''' 0� .¤´cÇŒ9Rhú„œ9sS§NÅ Aƒ°páBP×/\¸�œ;wvvv044ÄÛ·o�ééé˜9s&455áêꊕ+WbùòåŸT¿ýöV¬Xcccœ8qBlÿDii)æÏŸ-[¶@SSS`.— L›6 ¶¶¶|×÷½ÚÚZ¼ÿòòòM΃0V­Z…€€�lÞ¼,‹fyÿþ}xxx@SS–––øçŸ��ëÖ­C`` Ö®] CCCXZZâøñãŸuõük••EM˜¸\.X,Nž<I]ôèX,rssÔ›­^½FFF000ÀÒ¥K)]µnÝ:øûûcçÎ1b<<<¦ÃãÚµk˜:u*4551oÞ<Ú¶•´´4°X,J®[·+V¬@PP `jjЍ¨(¾úNKKÄ кuk±ºY”n߸q#õþÖÔÔ¤ÕPožÎk“'OFZZ-ÅÅÅX±b† ‚ñãÇ#**ж¢innŽ;wÂÓÓ à“!ɸC‘‘‘ðòòâc2|:ßòøªªª ,‹f™“””„©S§BCCƒšˆì_›ïn(ˆëׯÃÒÒRRRàp8¸rå ßĪ[·n�„LÉ[®íÓ§�àåË—PRRâ3qìÕ«®_¿‡#2O¼½‚•O=�€fjÚ¬¬,¨««£gÏž"å8ñðQu LVcª««wwwj’õæÍÄÇÇóÉMKKCBB|}}ñèÑ#¸ººR×ÜÝÝ‘™™‰ & ..ÞÞÞPPP V !¸uëÒÓÓQWW'q™ºwïŽÔÔTÚÞÞ OÜ¡¤RRR¸yó&ÌÍÍŽ‘#GÂÚÚ—/_–¨n!X¼x1âããáííÅ‹ãøñã˜3g¸\.FŒòòrêåÉÛs™““ƒôôtJ›Í8¸oi,Y²DXX”••ahhˆ¼¼< 0�£F¢¾¦@NN’““ajj  ~Õ~íÚµ˜4i6lØ€§OŸÂÆÆ555¥ØØØ 44%%%øã?¨+@ý`é?ÿù¬¬¬àííŽ;"33FFF4h""" ¡¡+V|r]Œ9ׯ_‡‡‡–.] SSSœ>}Zâ=Œ„ÃÌÌ &&&Bã±Ùlj€ÝxÀ㺺W¯^Ell,fÏžI“&ÁÌÌì£ÊÅÀ ˆiÓ¦ÁÅŸ}û6æÏŸ þ=jdduuulݺ'ND«V­¨û–-[†ºº:ÂÊÊ NNN`³ÙÍUŒâÆÔ„I,X€Û·o# �PQQ¡Y¢¬Zµ —/_Æüùó1iÒ$‘餤¤`üøñؼy38 i¾Ž: $$ŽŽŽ°··§ b !ˆ‹‹ƒ¾¾>&J7‹ÒíãǧVIØl6nß¾MÇ$%%aèСøïÿ XZZâùóç�ê·wXZZâÝ»wX³f &L˜�lÛ¶¯<úúúغu+Ÿ ¢Æ¢ÈÎÎFTT&Ož,Q|†¦ñ=¯ÒÓÓñÇÀÔÔ{÷î…µµ5¡¨¨(²|uÜÜÜHVV߯´´Tâ_tt4©««“èwæÌò5¹qã@ÒÓÓ !„”””�óÑ»woÁÎápÈŒ3ÈÌ™3©°°°0¢§§Ç÷ĉ�)//™¯ääd€ÐÂ=zD�k×®ñÝSRRBúôéCvïÞ-RvcŒÉÆiršZ< �òúõk×ccc‰‚‚ßuAõѯ_?€� S§N%/^¼ ®q8²fÍê:�rþüyÚý>| 555M*S~~>QPP Ó§O' äÈ‘#ÄØØ˜� áááBË- GGG²zõjB!qqq�y÷îÀ¿ÓÓÓ‰¬¬,yùò%uÿ½{÷�rõêUB!VVV”¼ììl¢®®NæÎKüüü¨ü OŸ>mr^¿&fffdË–-Ôß\.—L˜0,_¾œBÈÁƒIÿþýɇ!„lذÌ;—BHQQ­N!¤¸¸˜(((“'OBÙ¾};166˜vEEQUU%çΣª««‰¶¶6•§¤¤$ýoúôé$006sæLªþ?•••äàÁƒ¤_¿~ÄÄÄ„äç狽çôéÓDWW—”––Bñõõ%¾¾¾´8Ož<!JJJäÖ­[„Bþù碦¦Æ'ëÉ“'´~Bõ#†Ï…ŸŸ™?>-ÌÍÍŒJlmmia7n$¿ÿþ;ár¹_,ŸŸ.—KôôôHtt4!¤þ=€ÄÆÆRq>|H�{÷î‘ÚÚZ"++KöîÝ+P^hh(111¡ô¤°t©×¹ ßÝ>| ÆÆÆ$88˜BHjj*@½BCC‰MîÌ™3ÉÒ¥K©¿ ¨¼"™n¥Ûyº';;›–nã¼×ÔÔYYYrâÄ B!»wï&£G&‡ŠM:uêDéDq2xˆwˆÂÏÏØØØ|3mñ{ ¥Ž¯ž>}J�ÌÌLBHý;�ILL$„Ô÷UUURUUÅw¯°>pæÌ‰çQÑÑÑMš£ šã¹¹¹‘ïzðÙ³gpqqAPP �ÔÞ¶†ËÀ@½™Â³gÏ(ˆDDD )) ~~~T˜œœž<yÂg.QUU…®]»Réäåå!''‡ú=}ú�¨/|ÕÕÕ´ûy+<“SïÞ½ƒúöíKûõâÅ šü»w—©I „`Û¶mðððà[´·.;;•••HOOGUUììì¨úX»v-NŸ>7n ??¡¡¡°´´DDDu¿´´4µŸJÒ2©¨¨àüùóàr¹ðññAJJ Ö®] €5V/^¼À¶mÛ`oo¡C‡‚Íf£ªªJ¢ú¹ÿ>tuu©UI�ÐÐÐ@Ÿ>}ðàÁ�ÀäÉ“qôèQp¹\¤¥¥aÊ”)°¶¶FTTjkk)sœfýj$! W X,FŽI™âš˜˜ ++ ™™™¨­­ÅîÝ»ùÌ.ÞߥKRõ$Š‚‚äçç£_¿~TØO?ý„±cÇòyÙmØ.9N:Zœ6mÚHPZÉûâO?ýtïÞ………bÍA 1þ|lÞ¼;w§¶¶ýõüüüÄ:kPUU‡ÃAii)°cÇøûûKTF†…Ëå‚ÍfC[[[hœ†«� ««‹ÌÌÌ&y¾nNž?ŽÔÔT‰÷åµnÝ!!!˜;w.ÜÝÝqòäI¾÷‰¼¼<mË‚¤éHKKÃÂÂBìÖ††())ÑêZØþ?QºYÝ.޶mÛBEE…2ËÏÈÈÀˆ#h–Hººº(//§ÆTâd4,“°q‡0Š‹‹†Y³f µ¬`øtZÚøJØø]FFFèÞ½;LLL†{÷î5±&¾ßíðõë×pvvÆo¿ýFÛ --áÇãÑ£G´øÅÅŨ®®æ›8p�óçÏÇ®]»hNWºu놢¢"¾c  ¯¯O)*wwwhjjR¿ððp�ÿßË÷ìÙ3Úý<3Ä®]»RaµµµðõõÅ•+W†Ÿ~ú‰ºvøðaš|ccc±uÓÔ:”ŒŒ œ:uŠf¦"ŽvíÚaРA Brr2233ñêÕ+üý÷ßðòò‚ŽŽTTTàãトˆÌ›7Oà` )eÒÒÒž={pëÖ-¬_¿žš<Š›T•””P€/^Œ£G6ÉäÝ»wÔÒ†tìØ‘šd >éééÈËËC||<FŒÁƒãþýû¸wï¾ óOA´jÕ :t�Pß¾½¼¼pöìYdff¢ººZ¬7»Ö­[Kä@…7¡jìfYFF†ÖwÃårñþý{±æÛÂ×kkk‘˜˜+++Ì›73fÌÀõëס®®.RîÆñàÁèëëSnȃƒƒ ‹…­[·âÔ©SˆŠŠ‚››güøñxüø1X,_;•’’BçÎabb‚ÀÀ@¬Y³eeeUnIxÿþ=^¾|Ù¤=}¼÷¨¤aš›œœ¨««óM˜D•ÙÝÝêß»ví‚¶¶6²³³?*Æ´iÓF¤ÎkLãÉÍÍ›7acc#Öœµ¡nþÝ.ˆ†“½êêj>}ÎËSãɱ0 4îEll,zôè!rß9çÑÇWÂÆïâèÙ³'.^¼ˆÐÐP”••á?þ€¯¯o‹ÛÏü]N�ß¾} ÈÈÈ ""222´ëÎÎÎØ·omØÅ‹¡¥¥EûúòäI899!::šo0§§§UUUœ={– «®®Æ‘#G0eÊ*,>>„ê·fÍ�õH''';vŒÚÈLÁÉ“'áååE­�r¹\#&&ÇŽƒ²²2->>>4ù¯^½’¨Ž$­ƒ¦pèÐ!8::¢oß¾|×~ŴÌÛµkG À+oÞœw]]í ˜)!‡Ƹqã¨Ubaܽ{™™™pvv†¶¶6TTTš40QWWGjj*Íåþ‹/ššJí-UVVÆäÉ“Áf³ñï¿ÿBGGòòò˜9s&N:…#GŽPÎuZ:ÛâÕ«W1xð`êo[[[ìܹ111˜;w.ßä®áq#ååå¸zõ*UOiØzöì YYYš]]].^¼(òó¼Â^ºt‰.©ëtQ}ñÚµk033âE‹àää„k×®ÁÁÁíÚµ+wÆŒÈÌ̤ýf̘A…7zzz|q6lØ�%%%dffbåÊ•�÷=iii(((|’wR†Æ°X,ڀ짟~‚±±1­_6¦¨¨ˆ¶"~çΘ››SŽZ:éé¿k0S�� �IDATé°¶¶¦ÞgRRRèß¿¿ØL%%%8::âèÑ£PQQÁéÓ§›”†“$B’““¡««û‘¥©?k­áþ?ât³8ÝÞÔó[µ´´pæÌÚû>''ªªª|ûþ…!nÜð)€ú›6m‚§§'£#¿ -q|%lü. ÒÒÒ000Àòå˱gϬ^½šæƒ %œaüÝñîÝ;xxx€Íf#&&†vÖÏìj„ عs'æÌ™gggaÁ‚ˆŒŒ¤:øåË—1yòdØÚÚBYYׯ_§ä :;vĪU«àêꊚš¨©©!** ŠŠŠ3fŒDyõöö†©©)äää`llŒ””?~ñññTœððpøûû#88•••T>:uê$p¢Õ›7o‚ËåâíÛ·ÈÏÏGZZºté555‰ê !………((( <edd S§NÐÒÒ‚ŒŒ ž?ŽuëÖÑ6ó¸~ý:F…˜˜Œ3³gÏF×®]1dÈ((( ¬¬ ;vìÀ¬Y³Ð¯_?´nÝžžžðööFQQÔÔÔPPP€Ý»wcÁ‚øùçŸA““^½z…øøxHKKKT¦ŒŒ œ={ÚÚÚÔŠLll,bbbÄ~åä9èÙ¼y3LMM‘““ƒýû÷ÃÇÇÀÿÍPSRR ©©É÷÷!C0iÒ$Ìž=‹-‚´´4ÂÃÃáéé‰RéØÚÚÂÎγfÍB§N��cÆŒÁĉ1nÜ8‘Ç´$V®\‰Ž;BOOÉÉÉHNNFhh(u]OO:tÀš5kF>þ|”——CYYû÷ Ưq[——GPPæÍ›‡êêj(++#66bÏoòðð€‘‘äåå1lØ0äææÒžñÇrçθººbܸq4/µ’лwo¾0žº†í¦{÷î´8Ož<AÛ¶m©8UUUÐÓÓßþ‰¾}û¢C‡xúô)Ä n>+¿ýövìØäädüôÓOÐÑÑÁ‚ `ooîÝ»C[[=‚¬¬,õμpá.\ˆñãÇ£°°~~~ˆ‰‰iæ’HN||<ÜÝÝiaŽŽŽ C·nÝжm[Úê¾¾¾044D÷îÝQ\\ŒŒŒ š·OIÓê½*((@EE ÈÈÈÀ–-[>ª,………HNN¦¡ÌCœn¦Û¡ªªŠ˜˜p8ôèÑC"švvvX·n,X�;;;”––béÒ¥ ‘è#�±ãAc  þ#òƒD½Ãðé|Kã+Þ;<==rrr|[ž‘››KÏž= ===ÈÉÉ¡}ûöÕ¾ßÝ0''{÷î�>»ó£GbÒ¤I——›ÍFXXüüü ©©‰#GŽÐ¼ëíܹÕÕÕ8vìŽ;F“Ã[ÆuvvFÇŽ…={ö`ìØ±X»v­Ä_+ „„„DFFbÑ¢E9r$âãã¡¡¡ ~? §§'�À××—v¯··7Ö¯_/Rþ¤I“¨¯!éé騰aBCCáãã#Q4„ç]ŒÏ[cQQñï¿ÿB[[[àꔜœ )³WOOOÄÆÆb×®]¸{÷.´µµáàà�JéCGGl6wîÜÁ€0cÆ Ê¼”Åb¡_¿~PTT¤&n’”©]»vxñâ:„nݺÁÐÐ.\ ”(~ùåœ9sëׯlj'0zôhøùùQû† ‚Å‹ÃÛÛ!!!>|8íoDFFbË–-X¹r%äää0aÂ899ÑÒ122PïÖšÇСC©gú­àää„Û·oc÷îÝ9r$’’’höùmÚ´Á¤I“гgO+{6668xð ^¿~ kkk¬[·Nà$]P[˜7opàÀ¼~ýcÆŒÁÉ“'ÅöMCCC$%%aÛ¶mØ»w/F ##£O6A›={v³ïiÓ¦ 6mÚ„S§NáÔ©Sxýú5† ‚-[¶=[“ác±±±ÁÇ1gÎ`Û¶m°´´¤Þ—«W¯†®®.¦M›FÝ£­­víÚQƒ¶˜˜J¶t^½z… .à¿ÿý/-|þüù”‡BUUULž<™òløáÃüúë¯Øµknß¾Þ½{cÓ¦M°°°hr:@ý¬#GŽ ??¦¦¦8þüGïéç™™öêÕ‹ïš8Ý,L·ËÊÊâÈ‘#X¹r%’’’(Ñà÷çŸFRR6nÜwww 8ëׯ§½#Å!ɸ£ñ{„‚ÈÈH¸»»ÓÞ] ŸŸoi|¥  €={öP&ž...´ëòòòxøð!öïßŠŠ ˜™™áÈ‘#––†´´ôGõ/ËÍ͸¹¹ñ]d@Ì#11&L8nS:-÷‡¹¹9ƹsç SWWCCCxyyQÇ{�õû6»víŠk×®Qй¹àr¹5j\]]÷ß _uëÖ!55QQQÍ•")) ...¸{÷®ÄG@´ätBCCQXXH³ÚT7 Óí ‚‰yÄSCØl¶Äqð…Ô;·üîV�¾ ÒÒÒ’’Ò¢6ÖWWWcÇŽÐÐЀœœ.^¼ˆêêj‰Íº~L„íËûVÓIHHà[Ù”–¨Ûè0@†fáØ±cpqqi6óA¼ÿUUUøÏþƒÚÚZŒ='NœxŒ WWW¾c,¾åt¢££›äA´!-Q·300Ða&€ Ÿ†ŽŒ„Ñд¨!ŠŠŠÍæ.YNNK–,Á’%Kš%}†•Ou´ÔÜHêŒä[IGÐG/Iu³0ÝÎÀÀÐrø.`````````````à‡™�200000000000ü 0@†fÈÀÀÀÀÀÀÀÀÀÀÀðƒð]N�SSSáããXXX`ß¾}¨­­¥Å)++Cpp0ŒŒŒààà€'NЮWTT`ëÖ­˜<y2 �äååñ¥uîÜ9Ìš5 ƒ†ŸŸŠŠŠš”×ÜÜ\,Z´:::pwwÇ­[·h× �sss 88%%%Ëüø1ìíí±iÓ&‘ñ–,Y"ðÀ׆”——cùòå°´´¤…ß¹s3f UW999"eEFF‚Åbñýnݺ…«W¯ ¼Æb±„[UU%0þìÙ³�µµµ¸pá¼½½a``�5ùy‰âôéÓ`±Xxÿþ½À¿4®\¹êÐc þ|(6› hjjÂÁÁ/^¤®ÛØØ}ö—.]š–¸~Ô˜éWÂÚ¿ –.]*6ßïß¿GYY-¬¸¸, ׯ_o¦\}^rssáááÑlމx´÷¥qssÃòåË›;ŸÌ’%K°`Á‚“޹¹9¶nÝ*ôzFFÌÍÍ‘ŸŸß¤ô¹\.455-ðú;wÀb±š,·¥ H¿14-i|UPP@W ¤¤cÆŒÁùóçÅÞËápPVV†ººº/M±|wÀ´´4ØÙÙ¡W¯^†¥¥%<<<°sçN*NEEìííqïÞ=øúúÂÈÈÖÖÖ8xð gîܹ¸qãììì‚ÂÂBXYYáõë×Tœþù¶¶¶Ð×ׇ¿¿?rssakk‹ÒÒR‰òš›› sss´k×AAAh×®ÌĮ̀Fõöí[èêꢮ®žžžðôôDLL ¼¼¼ÀårEÊæp88rä,ö`Ý„„‹Œsýúu˜™™! €ïÚÚµk!-- www,[¶ ¥¥¥011ÁãÇ…Ê«ªª‚™™®\¹Bû©ªª¢wïÞ|቉‰••…@y<%I»oþüù�€ÊÊJ¬\¹ð÷÷ÇŒ3€?ÿüïÞ½Yv†¦QQQU«VÁÐÐééé´k»wïÆæÍ›ahhˆ 6à—_~±±1RRR��Ë–-ã{ö¾¾¾PVVÆï¿ÿ.0=qý¨1Ó¯DµÿÆäåå!((ŠŠŠbã~ë$$$@NNN¬>ú–QTTÄ¡C‡‘‘ñIr>uÉè¸o.—‹ØØX|3éB>ªççç#''ZZZŸœ‡–È ß>’ê÷gÏžANN÷ïßÿÂ9Ïww „––RRRhçÏ”——cëÖ­˜3gX,N:…7oÞàøñã••�ÈÈÈ`ÅŠ°¶¶Fûöí eee°X,�€¶¶6”””’’ TWWcéҥؾ};ììì��†††<x0bccáìì,6¯[¶l­­-üýýÁb±`jjŠ’’lÙ²Û·oGÇŽ‘––ꘚš" �ªªªBe×ÔÔàðáÃø÷ßEº´õêÜÝÝ1uêT\¹rEh¼¨¨(xzz"//iii´k{÷î¥ê �¬­­¡ªªŠK—.AMMM ¼êêj¨©© }yuéÒ…öwbb"�]}á pôôô0`À�¾ërrr8þ<-Ÿzzz8p îÝ»'trÁÐtòóó‘——‡Ë—/ÃÈȈvmüøñ˜:u*u¾”‰‰ ÒÓÓ‡!C†@[[›ŸËåÂßßžžžBÏâ×ó1ýJTûo̰páBtïÞ]d<†oyyy,X°�»víâkŸMáõëׯܹsñÛo¿5ù~FÇ} ++Kà3j©éhkk#!!¡É÷eggCMM ¿þúë'çá[E^^gΜiîl4™ïnPFF†ïðÑîÝ»£¼¼œš¡ïß¿ŽŽŽÔä¨7‘xðànÞ¼ �èÙ³'íE*''àÿ/á7n ''£F¢âtîÜÎÎÎ8tèØ|¾yó›7o†µµ5•Ž””lmm±cÇ�m €ZUøðáƒHùíÛ·›Íưaà %%ø1ó×Ó¦Mƒ­­­Hyë֭Ô)Sжm[¾k ë ¨7Ezÿþ=äååÔ¯öÙÙÙÑVX+++›t¸vdd$¼¼¼¨g›››‹Ñ£GSÏ‹÷u\Pþ„å“wOçÎ%ÊoRíàà� X[[ó™‹ãýû÷ؾ};,,,`dd„ÐÐPTTT�¨ŸHwîÜ•••Tüàà`hjjÒÌ#""0qâÄ&¥û5éß¿?öîÝ uuu¾kŠŠŠ´Ã…¥¤¤ ªªŠêêj²RSS‘˜˜(´}JÚÓÔ~%ªý7¤¨¨¡¡¡°··�Ìš5‹ö1ˆËåÂÔÔS¦L¡Ý7qâDlܸÝ»w§Y+@SS“f¾UVV‹…¤¤¤&Å¿ví_~/\¸@õMKKK :7nDMM �ÀÅÅ…21ä_~ùl6ãÆ�´jÕŠ¯={èÛ·/<==ñìÙ3Úõììl¸»»cÀ€˜6mÍd´¸¸;wÆñãÇ1eÊ¡2#N&‹Å¹sçàêêŠR«z/^¼À_ý…bâĉˆˆˆ€¦¦&u¯­­-¶lÙ"ÒªAíڵÇлwoøúú6YÖ×ÐqŸ .— ‹…“'ORa=‹ÅBnn.–žžŽY³f¡oß¾˜={6îÝ»G]㵸¸8899aÀ€X¸p! ¿jYšJVV5)’¤JJJ°zõjÁÀÀ�K—.EAA€z½ãïï;wbĈððð˜�Ü¿^^^8p ¬¬¬°zõjšyܵk×0uêThjjbÞ¼y´--iii`±XTÝ®[·+V¬@PP `jjЍ¨(¾UŽ´´4L˜0­[ׯ%ÔÔÔ`Ó¦M1bFŒÈÈHZ|Iä&%%aêÔ©ÐÐÐàOÕÖÖb÷îݰ²²Â€àãモ¢"};**Šf¦Ô·K###ìÛ·Olû:}ú´Hý&qú¨k<x+V¬À Aƒ¨q‘¨±Áßÿ#FPïÉÚÚZØÙÙÁÕÕ{÷îE÷îÝiïÐçÏŸó•½¥ó-¯xæùçΣµもüòË/��MMM>}øµùî&€‚¸~ý:,--!%%‡ƒ+W®ð �»uë�B_.¼åÚ>}ú��^¾| %%%(((ÐâõêÕ ×¯_‡Ã™'Þ¾ %%%Zx=�€fjÚ¬¬,¨««£gÏž"å:ñãÁf³qÿþ}xxxˆUnâdUWWãêÕ«ˆÅìÙ³1iÒ$˜™™¨ïØ©©©´%ïÊÊJ=z1büüü„îÈÎÎFTT&OžL…½yóñññÔóâMÌyæ¿S¦L›ÍæÛûYXXˆääd>|îîˆ :¤8¤¤¤póæM˜››#<<#GŽ„µµ5m›(!X¼x1âããáííÅ‹ãøñã˜3g¸\.FŒòòrJi×ÕÕáÀÈÉÉ¡™R²Ùl±öæF\{áQ]]¤¤$ 4HàõÀÅÅEèJòÇö£ÆˆëW’–'++ ÕÕÕÔ µµ5öïßO½`=z„ÄÄD>|˜j»¯^½Â±cÇ`jjŠ™3g"66–’ÇûÐtôèQj”žž555èëë7)¾°:ŽŽŽÆ… àåå…¿ÿþ»w¿?€úþIÛGvâÄ ¸ººbøðáØ¼y3� 33·oߦÉMMM…““¶nÝŠW¯^aΜ9Ô¾‡ììl 6 ššš‡ºº:ôõõi“Ôòòr<xS§N(£1’ÈêWµ´´àçç555”——c̘1àp8Ø´ilmm±fÍÚ=½zõ‚¬¬ì' ¨deeެ¬,TTT W¯^X±b5ØÇ×Ðq_“[·nÁÈÈ¿ÿþ;¶mÛ†~ýú!))‰§¼¼k׮ŤI“°aÃ<}ú666´uKãÆ´I‘8,X€Û·o# �PQQ¡}]µj._¾ŒùóçcÒ¤IÓ)++øqãйsglݺ®®®øùçŸ!##CÅOIIÁøñã±yófp8Šl{áááèСBBBàèè{{{Ú�—‚¸¸8èëëS{{{ãܹsXºt)þúë/ƒ[QrÓÓÓñÇÀÔÔ{÷î…µµ5Í”ÞÏÏÇŽƒ››Ö®] yyyÚņ}ÛÔÔ£F¢¬‡� ''ÉÉÉ055 º}ééé‰ÔoÂ¥OyL:\.Ë—/‡®®®Ø±ÁâÅ‹ñöí[ìØ±�pøðaܽ{—ÚÇþòåK¤¦¦Rò/\¸€Q£F}ñUèÏÉ÷4¾ÖŽ©•B6›Û·oK4–ÿb¸¹¹‘¬¬,¾_ii©Ä¿èèhRWW'ÑïÌ™3äkrãÆ €¤§§B)))!�æ£wïÞ$""‚/œÃá3f™3gRaaaaDOO/î‰'�R^^.2_ÉÉÉ�)(( …?zôˆ� ×®]ã»§¤¤„ôéÓ‡ìÞ½[¤ìÆ“7Òž<yB”””È­[·!„üóÏ?DMMM¬¬Õ«W ¾ð'Ož�Ô/$$„ÔÔÔP×+**‡Ã¡þ>v쉉‰!çÏŸ'ÑÑÑdìØ±DMM¯>!ÄÏÏØØØ.—K oXÇOŸ>%»wï&gΜ!ñññdõêÕ¤S§NdíÚµ´{bcciùŒ‰‰á“ÛÉêÕ« !„ÄÅÅ�äÝ»wÿNOO'²²²äåË—Ôý÷îÝ#�ÈÕ«W !„XYYQò²³³‰ºº:™;w.ñóó#„’ŸŸO�§OŸ~tž¿/_¾$�È¥K—„ƉŒŒ$½{÷Ø_?~L�ääd¡÷L?jLSú•°öÏã¿ÿý/QSS£ÚÔ›7oˆ¬¬,ILL$„røðaâââBz÷îMbcc !„œ:uŠ 2„p8rùòe€j#óçÏ'>>>�yòä !„²bÅ BirüÆ$%%�¤¬¬Œ ãÉ|òä ©®®&êêê䨱c„BŠ‹‹ �rûömBÈÿûSþ]TTÄW÷�yôè!„3fZ^,X@lmm%–ÑIe6Öý»ví"£F"µµµTØž={H¿~ýhñFÅ'ÿSÈÊÊ"óæÍ#�ÈÖ­[ÅÆo.÷1p8€jã„òðáC€Ü»wBÈÌ™3I`` í¾™3gRºŽ÷¼xº‘úö§  @Nž<ùJÑt¸\.ÑÓÓ#ÑÑÑ„ñõP[[KdeeÉÞ½{Ê %&&&äÇ"ÓÉÊÊ"�Hff¦@9fff´±Í‡ˆ±±1 &„’ššJÓ#¡¡¡ÄÎÎŽ&cæÌ™déÒ¥Ôß´çÉëŸÏŸ?§âðÂxºHœÜƒUUURUUÅW†;wîÔõ„ïÛ$ýû÷§êoÆ dîܹ´{Dµ/AúMâô)!õÏ" €vŸ$cƒ .�äøñãDAA\¾|™Š;oÞ<²hÑ"êo ràÀ‰òÜ’i©ã«§OŸÒú[ee%@½çEµcÞX9;;›~æÌ‰çQÑÑÑMš£ šã¹¹¹‘ïzðÙ³gpqqAPPõõ»}ûö�@[êMž={&ÐqCDD’’’àççG…ÉÉÉáÉ“'|&UUUèÚµ+•N^^rrr¨ßÓ§O€úÂרô÷e“grÊãÝ»wðññAß¾}i+a/^¼ É¿{÷®Øz©­­Å_ý??¿Ï¶y[UU¥¥¥HHHÀŽ;h_½Ú·oO[E±±±­­-Fމ‰'âСC‘‘ÁñãÇir‹‹‹†Y³fñ­R6üJÚ³gOL›6 æææ033Ã_ý…7bÑ¢E´å}KKKÔÖÖâå˗صk&NœˆþùGâr¾xñÛ¶mƒ½½=† 6›ªª*‰î½ÿ>tuu©Õf�ÐÐÐ@Ÿ>}ðàÁ�ÀäÉ“qôèQp¹\¤¥¥aÊ”)°¶¶FTTjkk)“›fýjô™HNN†‡‡ÂÃÚ³Ùl >C† *£©ý¨1ÂúÕÇRZZ ]]]ª­ÊËËcúôé”§ÓÄÄDXYYÁÉɉZíà™ŠHIIAWWjjj¸q㪫«qèÐ!Ì›7úúúHKKC]]¢¢¢¨/ØM/Œ†}‹gúøôéSüôÓOpuuELL €úgfff†þýû7©^x&ˆ•••¨««Ã¿ÿþË·ŸÎØØ§OŸêÕ­¡ŒÆ4Ef§Nhqnß¾ cccÚŠ Õ›>}úàÍ›7¢Š @¸ÎoŒ¢¢"eQ"‰Ü¯¥ã¾\.qqqÐÑÑ¡…·iÓ†/nö٥KRú²¥ñüùs¤¦¦J¼òÒºuk„„„`îܹpwwÇÉ“'ùÞ'òòò––™Žºº:¦OŸkkk¬X±W¯^i…$-- ܸqCâ²)))ÑÚYãýyyyÐÓÓ£í}nÕªU“ä¡{÷î011AXXÍ$øÞ½{ÐÒÒ‚²²²PYû¶‰‰ ²²²™™I™66ïkjû*++£õ¾iaú”GcË1IÆ#FŒÀÒ¥Kaccƒ… ÂÐЊ;qâDìÚµ oß¾ÅÇqêÔ)˜˜˜-CK¥¥¯$ÕåÕŽ[ßíðõë×pvvÆo¿ýFs“,--áÇãÑ£G´øÅÅŨ®®æ3%;pà�æÏŸ]»vÑœCtëÖ EEE|.¶  ¯¯OMvÜÝÝ¡©©IýÂÃÃüÏQã=-<³°®]»RaµµµðõõÅ•+WF3y8|ø0M¾±±±Øº9uꢢ¢àææF¹?~<?~ ‹õÑa)))tîÜ&&& Äš5k$v¡Ü©S'ò™ÆÆÆ¢G9rd“óÛô7ÞÖºuktëÖ Ó§O‡——8 ‘¼’’ÊÐâÅ‹qôèÑ&ÕÕ»wïzãëØ±#5ð>|8ÒÓÓ‘——‡øøxŒ1ƒÆýû÷qïÞ=$$$´xóOIÈÌÌ„­­-V­Z%prRQQõë×ÃÍÍM¤)USúQcDõ«…Â7±´´DTTÞ¼yƒÃ‡CWWÆÆÆ8|ø0JKKqðàAŒ1@ýþ®éÓ§#)) ÙÙÙ4hÔÔÔ`ooÄÄDܽ{555ÔD§©ñ%§»xæc8|ø0ž?Ž˜˜L›6M¢ý0 iŸÃáàÍ›7|Ô6mÚ }ûöB¢ÒüX™@ý¾OI\røðA"OoÂt>gÏžaÍš5èׯrss‘••…¥K—Š•+ˆÏ­ã>7Âê‹Ãá ¼¼\ìV A´nÝZì^Üæ"''êêê|NQDµwww<xð�úúú”£¡ììì&¥###ƒ;w"..]ºt··7¬¬¬D¾Û´iÓ$׸ÿݼy666”~þðáÃGy›m(·gÏž¸xñ"BCCQVV†?þø¾¾¾ „àíÛ·MöÆÙµkWxyyáìÙ³ÈÌÌDuuµX¯©âÚWff&­kjjŠÜ›ÜXŸ B’±�jbÒ¡CZ<}}}(**âÚµk8þ<ÜÝÝù|a´tZâøJœ.†¨vÜ’ø.'€oß¾…‹‹ dddÁ×ñœ±oß>js(�\¼xZZZ´/’'Ož„““¢££ù&VzzzPUUÅÙ³g©°êêj9r„æà!>>„êÇÛ[Ò­[7899áØ±c”R#„àäÉ“ðòò¢V.¸\.‚ƒƒV±5Ä�� �IDATƒcÇŽñ}ýòññ¡Éõê•ØúÑÓÓCff&í·aÃ())!33+W®+£!÷ �õmJ‘VWWÓcEþîÝ;dff¢_¿~TØû÷ï±iÓ&xzz TÈ ¿ z1<|ø²²²PVV‡Ã§M›6|_ã„q÷î]dffÂÙÙÚÚÚPQQøÅZêêêHMM¥9xñâRSS©•�eeeLž<l6ÿþû/ttt //™3gâÔ©S8räíËß·H^^&Mš„3fÀÓÓS`œ³gÏ‚Ãá`ôèÑ|×¶%IûÀß^Dõ«Æñ%¥S§NHOO§µu}}}¼zõ ‘‘‘°°°ÀÏ?ÿ mmmp8lß¾=zô µ{3338p�gÏž¥Ž=>|8Ž9‚S§NáÏ?ÿ¤õ‡¦ÆT®†G×Ü¿²²²Ô¾1 XYYaÇŽ8xð ÍñoðÖ”3ddd`mmÍw®ãÍ›7aee%ñÞ©†eù™ýû÷Ç™3ghwA/ê¼¼<±+Ê€p_SSƒ+V@CCÏŸ?Ç¥K—°yófš³Q| ÷¹’’Bÿþý…ž­)--1cÆð=/Aùo¸:Z^^Ž«W¯Rú²¥‘žžkkkª½‰«JJJpttÄÑ£G¡¢¢‚Ó§O7) ¾/öë×îîî`³Ùˆ‹‹£]Ò°Bœœ ]]Ý)&€z')¼ý@ý>ÙÛ·oãáÇ-¨oX¾|9öìÙƒÕ«W£¸¸ªªªÈÊÊëª1¶¶¶Ø¹s'bbb0wî\¾±„¨ö%H¿Óú7!„Ï«¯(}*IÆ·nÝ‚««+Ö¯_ÏwÖ­ŒŒ \]]Áf³±oß¾í$N-q|%L—K‚°vÌCиùkóÝñîÝ;xxx€Íf#&&†æxDEEÝ»wÇ„ °sçNÌ™3ÎÎÎ(**‚ I)‡Ë—/còäɰµµ…²²2Í›ÜСCѱcG¬Zµ ®®®¨©©šš¢¢¢ ¨¨ˆ1cÆH”Wooo˜ššBNNŽ: íøñ㈧ℇ‡ÃßßÁÁÁ¨¬¬¤òÑ©S'ôíÛW¤ü›7o‚ËåâíÛ·ÈÏÏGZZºté555>õOž<AÛ¶m1pà@²òòòPVV†gÏž¡¬¬ iiihÓ¦ ÔÕÕ¡§§‡?ÿü}ûöE‡ðôéS"((mÛ¶Å›7o0xð`L›6 Ë—/GYY&L˜�GGGüòË/øðábbbЪU+Ú9/^ă`eeÅ—ŸëׯcÔ¨Qˆ‰‰Á˜1c°}ûv<xð�†††èÔ©=z„€€�lÛ¶ íÚµÃÍ›7±páB888@UUÒÒÒÈÈÈ@xx8Ÿãað‹lÞ¼¦¦¦ÈÉÉÁþýûáããàÿæÅ)))ÐÔÔäû{È!˜4ifÏžE‹AZZáááðôô¤Õ»­­-ììì0kÖ,ʤe̘1˜8q"Æ'òø–@UUrrr¨ëÝ»wѶm[ôêÕ 555°··Ç»wï0zôhš ’¦¦&:tè�‡ƒ-[¶ÀÓӓϤ§q[$ëGÛ¶mƒ¿¿?RRRðË/¿ˆíWÛ—°ö߸¿ôìÙÏž=CEEežÚ©S'̘1ÿý7öìÙ Þ!ˆ³³3þþûolܸ‘¶B5hÐ ÈËËcÙ²eÈÌ̤êFII ¾¾¾|›â›¿q¹x¸¹¹Q«­ ¢VOY,þüóOØÚÚÂÓÓ“¶ªÊs¦uôèQôîÝ[â£.\###ÈËËcèСÈÊÊš5kDEÓ˜ÆÏôceÚÚÚ"$$˜2e Þ¾}‹uëÖÑâÔÕÕ!55•Ï#jS¨¬¬Dyy9nܸ!Vw âkè¸Ï‰££#ÂÂÂЭ[7´mÛ–ï°p///êy 6 ¹¹¹4}Êcþüù(//‡²²2öïß 6ìkEbâããáîîN U¾¾¾044D÷îÝQ\\ŒŒŒ š·OIÒyòä öíÛÈÉÉ!--oÒ ¨¨¨ !!زeËG•“çh¨¡b===ØÚÚbÖ¬YX¼x1ddd(§%’’˜˜ˆÜÜ\hiiAFFgÏž…žžäää0lØ0ÃÓÓÞÞÞ’’Bjj*fΜ)R¦žž:tè€5kÖ ''‡ïº¨ö%H¿I2)¥O!nlPQQwww¬]»ÞÞÞ(--…——Nž<‰víÚ�ÆŽ ///¨««cèСbóØÒø–ÆW¼ÒÓÓ!''Ç÷MT;nß¾=TUU‡ƒ=z4ßjí÷æææÍ›´ ð G¥â’%K–]]]ò矒„„šœ?ÿüS¨œ†°ÙlbooOôõõI@@�)))iR~ÓÓÓ‰››ùý÷߉··7µ¡šBª««…æÁÛÛ[¬l555¾ûBCCÆçÆ××—O–™™ùðáIHH ^^^døðá¤_¿~dúôé$!!Ú8]YYIlmmɾ}û!„TUU‘˜˜âììLúôéCÌÍÍÉš5kHQQ•—Ë%“&M"‹/˜Ÿ{÷î333’––F©ßлjÕ*bbbBúôéCf̘A’’’(祥¥$**ŠLŸ>hii}}}²páB’••%¶ræÌbffF~ÿýwò÷ß“àà`jñû÷ïÉâÅ‹‰¶¶6IHHàû›zÇ5AAAD__ŸŒ;–ìØ±ƒÚÄ̃ç<…·ÁŸBž={F�PuØ’á99hüKJJ";wîÚ¦ymÿêÕ«�ÉËËã“ݸ-ñÕ©ßÌoaaAŠŠŠ$êWÛ—°öߘ¼¼<€Ü¹s‡ž˜˜H+#!õm �利!«W¯&½{÷¦9øÏþC”••n,—4~ãrñœ¬ZµŠèëë“Ñ£G“}ûö‘ºº:š|žÃ‡‹/ò¥½oß>¢««K¬­­ÉÓ§O:pám|ç9!„K—.GGGÒ¿2oÞ<’““C]“DFÃgú±2y<xð€¸¹¹‘~ýú‘©S§’¹sçÒœ|ñÒæ9û>ÕË×ÒqŸ‹ÊÊJD´´´È¸qãȈ––­œ?žLž<™hiiñéSÞóòññ!cÇŽ%zzz$00P¬ƒµæ¢°°� ¹¹¹´pQõP]]M¶mÛF¬­­‰šš177' Þ‚œ¦JçÙ³g$00Œ5Ѝ©©råÊ꺙™ &ãÆ#ZZZÄÇLJæèB'0~~~”•sçÎuuušã$BêÛàªU«ˆ®®.7n‰ŠŠ"ªªª"À4”{óæMâííM† BúõëG¼¼¼hNŸxã6mmm2lØ0²bÅ òúõk‘}›Bˆ……­JÚ¾ë7QH¢O;äá!jl°lÙ22|øpRYYIÕ³¶¶6Ÿ¥!C† 6ˆÌcKæ[_íÙ³‡ôïߟ¬_¿žÏ Œ¸v|íÚ52zôh2lØ0ráªì_Û ËÍ͸¹¹ñM y³qIHLLÄ„ $Žknn.±l†oBÆŽ šÓ¨–Ê… 0räH”——‹<—366 ,À;wZìþ«ÏIPPŠŠŠ°qãF�õ–{÷îÅÕ«Wùr0|Š‹‹ÑµkW\»ví›XÑHJJ‚‹‹ îÞ½Û$3æ–šŽ(BCCQXXˆÐÐÐfI¿)ÔÕÕÁÐÐ^^^Ôù¬À—i_’êÓ/Á“'O ¦¦†ììlÚ–†oƒøøx‰÷°Ùì&9ùyþü9_XDDÄ÷gÊÀÀÀÐ\°X,¸ººbæÌ™pssûê{¯¾û÷ïÇìÙ³¿ÛÉßž={ЩS'ôèÑ?ƦM›¨³Éªªª°iÓ&,[¶Œ™ü1Eо¼o9Q$$$ÀÅÅ¥ÙÒo iiiHIIù(Grßqqq011ù(ór†fÈÀÀÀð;v,tuu‘™™Is˜ò­rÿþ}DGGcÙ²eÍ•/!µµµˆŒŒÄ£G0räH$&&RG]dggCAAv7Cc\]]%:öà[IGÑÑÑŸÅkò×àØ±cpqqùæ¼b6…÷ïßcëÖ­X¼xq“=43ü¸0@†Ïˆ´´4Ž?NmoÉð<Ú‰¢wïÞ-Î}õç„ÅbaöìÙB¼èééáܹsßÄóüžPTTü¦ÚÏÇ÷’Ž(¾¶yã§ ÌLõK´/Iôé—@FFwîÜùêé2|Û|—Ç@00004'Ìdáû¢% º>Ìá™�200000000000ü 0@†„ïr˜šš èèèÀÂÂûöíCmm--NYY‚ƒƒaddœ8q‚v½¢¢[·nÅäÉ“1`À�xxx ///­sçÎaÖ¬Y<x0üüüPTTÔ¤¼æææbÑ¢EÐÑù{çÓöÿÿ×D"Ž”ËQ"‘B§TH©tWJ"¢r'I)Gç„}HäR)r Q**JQŽ[Š„$"’{JIST³~ô›}ÚÍ43åßy>=˜µ×~¯µ×^ë½×å½Þk4œqëÖ-Úõ’’øúúÂØØšššðóóCyy¹Àò?~Œ™3g"00g¼Õ«WcРA<ãTVVbݺu077§…ß¹s¾¾¾055¥Ê*??Ÿ§¬ððp0 Ž¿[·náÊ•+\¯1 ˜™™q•W]]Í5>Û±C]]222àææMMMhjjRg}})Μ9ƒ?rý-äçàãǨ¨¨ …edd€Á`àýû÷ß)W?ÕÕÕ000ÀêÕ«yÆã¦»Ú¢“„|Yª««QUUõ½³ñ]X½z5ÜÝÝ¿i:£GFDD€F϶...Ô9–¥¥¥`0ÈÌÌä+óäÉ“4hêëëyÆko:/22¶¶¶}½¯‹Å‚²²2bbbxÆkM¹ ÊËËajjŠóçÏהּš¥õ#•ñO7�ÌÎΆ  ???˜››ÃÅÅûöí£âTUUaæÌ™(((€§§'´µµaii‰ÈÈH*ÎÒ¥KqýúuØØØÀß߯^½‚……Þ¾}KÅùçŸ`mmqãÆÁÛÛ÷ï߇µµ5Þ½{'P^ïß¿ccctíÚ›6mB×®]addD ß¿555Ô××ÃÕÕ®®®ˆÅŠ+Àb±xÊnhh@TTÔÕÕÍ3njj*üüüxÆÉÌÌ„‘‘|}}9®mݺ¢¢¢pvvÆÚµkñîÝ;àñãÇ-Ê«®®†‘‘._¾Lû“““ƒ¢¢"GxZZÄÅÅ1uêT®òØJ <<œvßòåË�>|Àúõ롤¤oooÌŸ?©©©˜3gjkky>»!MIMM…¤¤$ß6ø‘/é/((ˆ:‹¯%ZÒ]mÑIB¾,þþþX³fÍ÷ÎÆ7‡Åb!!!šššß5¶¶ÅììlXYY}×sÛ;ÅÅÅÈÏÏÇÈ‘#¿wVZÍäY÷GåG)㟮…9×®]£ùRYY‰Ý»wcÉ’%`0HJJBYYÍU»˜˜|||`ii‰nݺÁÏϲ²²Ô™*ªªª––Ƶk×`ff&“‰5kÖ`Ïž=°±±�hiiA]] pppà›×X[[ÃÛÛ †††(//GHHöìÙƒîÝ»#;;  îéÙ³' áëë 99¹e×ÔÔàØ±c8yò$Ïôׯ_ÃÙÙvvv¸|ùr‹ñ¢££áêêŠÂÂBdggÓ®EDDÐΞ±´´„œœþý÷_ÈËËs•Çd2!//ßâÇ«W¯^´ßiii�À±úȆ=ˆÓÐÐÀˆ#8®KJJâüùó´|jhh`Ô¨Q(((Àï¿ÿÎU®!BgïÞ½èÙ³'ÌÌÌ>ëÐø+W®ààÁƒ°°°h1/ÝÕ$DÈ— ¤¤yyy\¿Cß*ƒàààVË$„ !!^^^_"‹ß”Ù³gcöìÙß$­»wïB^^ƒþ&é})¤¤¤œœü½³ñSó#•ñO·(&&Æqà§ŒŒ *++©QùáÇ1{ölš«vccc<|ø7nÜ��ôïߟ֔”ðß@ãúõëÈÏϧôÜ£G888àèÑ£|óYVV†àà`XZZR鈈ˆÀÚÚ{÷îEii)�Ð@ãÙ5�ðéÓ'žò»uë†øøxŒ?""Ü_3‹Å‚··7æÎ kkkžò¶mÛ†Y³fqíÔ5?x´®®?~„””€ÆÕ>Ú ë‡Zu–Pxx8V¬XA½Ûû÷ïÃÄÄ„z_ì@^ÎæùdßÓ£GòÀTÛÚÚBII –––¦Ãüøøñ#öìÙ333hkk# €2“Šˆˆ@=ðáÃ*¾ŸŸ”••if¿¡¡¡˜6mZ«Òý–,\¸6Âb±`hhˆY³fÑâM›6 ;wŒ m…¾¤¤ÊÊÊØ½{7VQQƒC‡Á`Pï�RRR ¬¬Œ+W®PaW¯^E×®]9L5ƺ¼qãFlݺZZZ077lj'(ý°xñbŽ3áRRR0pà@ÄÇÇcòäÉ�€:pÔ©ÀÄÄ£GÆÖ­[Q]]M]#„ 99vvvPQQÁü’’êzFF¬­­qàÀ˜™™q•ÑAdjjjâܹs°±±––e²•““ƒ @YYŽŽŽX¿~=Ö­[×bZ‚0dÈøøø@WW§Nâ«§¸ñîÝ;,_¾!!!PVV懟îâ§“„|]6lØ�___ƒÁ`ÐÌ!<x�(++ÃÜÜÿüó�þíòG!//°X,0 $&&R×‹ŠŠÀ`0pÿþ}�&c›7o†¶¶6455±fͪ oÛ¶ ÞÞÞØ·otttàââÂ5nØÙÙÁßߟvüøq˜˜˜@]]6là°Vzúô)nß¾MT–——cãÆÐÔÔ„––üýýiÖ¼tž±±1"##áããªPZZ Œ3S¦LAtt4M¦±±1öîÝ‹åË—cĈ˜3g®_¿Ž«W¯bñâÅPVVƲeËP\\Lݳwï^èéé€@åž‘‘iÓ¦!<<¦¦¦ÐÔÔĶmÛðêÕ+ÂÐÐzzz8r䇵GóUÒºº:8p�1bV®\IÛ^röìYØÚÚbذapuuųgϨk-½c~º}Ÿ¯¯/ÆŽ ÄÆÆâñãÇX½z5ÔÕÕammMû.²·Ê°-+¢¢¢8tlS³âÒÒRôèѱ±±°µµÅˆ#àîîŽââbÄÄÄ`ÆŒPQQÁ¦M›>ÛÜûGë_¥§§ÃÎÎJJJ´~ó2´ž}~º 7233ann444àòåË«¾}û�^½zÅUƃ��C‡�¼|ùÒÒÒèÙ³'-Þ Aƒ™™‰††žyb+iiiZx¿~ý�€fjÚ”¼¼<((( ÿþ<åhqàÇ&>>žú7ï0µV“ÉÄ•+W€E‹aúôé022ÐØ°³²²¨2€Ç‡’’tttàååESæM¹{÷.¢££1cÆ *¬¬¬ )))ÔûbÌÙæ¿³fÍB||<Ç~€W¯^áÒ¥K8v윊ò|¶¦epãÆ c×®]ÐÓÓƒ¥¥%.^¼(Ðý„xxx %%nnnðððÀ‰'°dɰX,èèè ²²’2®¯¯Ç‘#GŸŸœœJN||<ßû÷ÄÒÒ‡¦”jQQÒÒÒpìØ1ê}½~ýqqq044Ä‚ @ÝÏž\9~ü8ÕùËÉɼ¼<¬­­¡¦¦Fû¨?ùùùø÷ß©°K—.aþüù-î×®]‹úúzlܸ°··G||<€Æ:Nû€Ÿ:u ŽŽŽ˜0a5³ž››‹Û·oÓä>~üX¿~=Ž9B›…ŽŽÆÊ•+1uêT ¼¼'N¤&{�àĉÈÏÏÇÊ•+¹ÊhŽ 2¯^½Šÿýï°°°€››ºwïŽÜÜ\hkkCEE¡¡¡PRR‚O‹éŠžž233áââ‚5kÖÀÐÐgΜx_!~~~022‚A‹ñÑ]¼t’¯Ëܹs±xñbØÚÚâöíÛ”)þƒ ­­ ìÞ½Ó¦MC‡¨ûxµË…ëׯ·Ê„ÒÝÝ·o߆¯¯/6n܈Ð&G7lØ€‹/bùòå˜>}z›Ó€GÁÝÝ>>>ÈÈÈÀ¬Y³hm³ùʓɄ••ÊÊʰqãF¸¹¹¡K—.´þ�/4DY,Ö­[555TUUÁÜܵµµØ²e ¬¬¬àââ‚°°0Ú}¡¡¡ÐÒÒBhh(z÷îMùY077Ghh(ªªª0oÞ¼ÏÚ‡ÂÂBxxxÀÃÃaaa––Fmm-Ö®]‹… bÉ’%´BNŸ>qãÆQa^^^ˆ‹‹ƒ““¶nÝ )))téÒ…ºž••{{{ìÞ½¯_¿Æ’%Kh{,¹½cAt{HH¤¤¤�SSSLŸ>úúúPRRÂŽ; ¢¢‚I“&áÉ“'m.£ÊÊJDEEÁÁÁxøð!ˆsçÎaÑ¢EØ´iNž<‰mÛ¶µ9 àÇê_åää`âĉ044DDD,--©ÅnRϾ NNN$//ãïÝ»wÿÅÄÄúúzþ’““É·äúõë�ÉÉÉ!„R^^N�p͇¢¢" åohh óçÏ' , Â‚‚‚ˆ††GÜS§N�¤²²’g¾.]ºD�’’ZxQQ@®^½ÊqOyy9:t(9pà�OÙÍÑÕÕ%;w=yò„HKK“[·nBù矈¼¼<_Y›7o&fffáOž<!�¨?RSSC]¯ªª" Ô︸8KΟ?ObbbȤI“ˆ¼¼<GyBˆ——™:u*a±X´ð¦eüôéSràÀ’œœLRRRÈæÍ›‰„„Ùºu+íž„„Z>ccc9䶆ٳg“Í›7B9}ú4@jkk¹þÎÉÉ!âââäåË—Ôý�¹rå !„ JÞÝ»w‰‚‚Yºt)ñòò"„R\\L�§OŸ¶9Ï_›²²2"..NÒÒÒ!„;vŒ,^¼˜(**’„„B!IIId̘1¤¡¡\¼x‘� ÊeùòådåÊ•�yòä !„oooâããC!$00èéé‹E>~üHäääÈÊ•+‰ªª*ihh DCCƒ$%%qÍ_@@�±¶¶¦…íܹ“üþûï„Åb&“IH\\!„ÒÒR€Ü¾}›ò_jZŸÓÓÓ9ÚýÁƒÉСC ‹Å"UUUDNNŽœ;wŽºÎd2‰ªª* HFsZ#³y»š7oÙ¸q#-lÁ‚T=û|øðDFF’áÇR\\Ì÷ž3gÎ555òîÝ;B!žžžÄÓÓ“GPÝÅO' ùºxyy‘åË—Óœœœˆ¯¯/×øüÚå�‹Å"$&&†ÒØw�@é=Byôè@ H]]'\åòéÓ'žéBˆªª*9xð õ»é·éÍ›7ý ö·$55• Û°aqww§~9r„èé鑺º:޼ ¢¯ŒŒŒ8Þ÷ˆ‰‰ MÆÄÄ ªÝÑúboß¾åÈk^^UŽ„²gÏ¢««Ká_î-åÇŽDKK‹–_gggZ™”””Ðäܹs‡«Ž%„{¹ß¼y“� EEE„îïXÝ@lllhé™™™???ê÷§OŸHŸ>}¨zòáÀöm>|8M†§§'qssk1ÿ.\ �È‹/¨°£GYYYÚ;ý´×þUdd$‘““#ÕÕÕך—± õ,99YàqTLLL«ÆhÜÆxNNNä§^|öì/^ŒM›6AEE@£i$�Ú20Ðh2ðìÙ3®£øÐÐP¤§§Óìâ%%%ñäÉó”êêjôéÓ‡J§°°ùùùÔßÓ§O€šác2™´ûkjj(ùM©­­ÅÊ•+1lØ0ÚJØ‹/hòïݻǷ\êêêðçŸÂËËë‹mb–““CCCÞ½{‡ÔÔTìÝ»ÞÞÞÔõnݺÑf §N kkkèééaÚ´i8zô(ÄÄÄpâÄ šÜÒÒRaáÂ…3ýMgIû÷ï¹sçÂØØFFFøóÏ?±sçN¬ZµŠ¶¼onnŽºº:¼|ùû÷ïÇ´iÓ($AxñâÂÂÂ0sæLŒ;ñññ<MôšòàÁ¨©©Q«Í� ¤¤„¡C‡âáÇ�€3fàøñã`±XÈÎÎÆ¬Y³`ii‰èèhÔÕÕQ¦'‚¬�/¤¤¤0oÞ<\¸p@ãþMöl~zz:€ÿÌ'DDD ¦¦yyy\¿~L&GŲeË0nÜ8dgg£¾¾ÑÑÑ044�èëë#==OŸ>E~~>z÷îåË—#''………xôèòòò0vìØóØtÕ�ÔÔÔ››‹òòrtéÒŽŽŽˆиšhdd„ß~û­ÕåPPP€††””” ¸¸Ç§®wéÒ“&MâðüÛ’Œæ´FfÓ¶ÒÐЀÌáû�� �IDAT¤¤$Œ=š§S§N=— :§K—.0`�dddðêÕ+¾æ ¯^½ÂòåËÜâÊmkt?$äÛÂb±UUÕãðj—?ÏŸ?GVV–Àûÿ:vì,]ºÎÎÎHLLäøžHIIATTô³ÒáÆ€```@YæB””D[ÙÊÉÉÁ„ ^e䦯š[Iݼy:::´þ€šš*++©þQsØú«©Ã6¶Žôû+½zõâ¨k}ûöEYYõ»ù*iAAFŽ YYYÒ`ç»i´ù;në÷BVV–Ö§…¼¼üõÔÊî›6ÕçRRRxöìÙg{äloý«–úïÚÚÚ‘‘‚‚‚PPPЪçäVϾ?í�ðíÛ·pppÀ!ChûDEE1aÂÑâ—––‚Édr˜d9rË—/ÇþýûiNWúöí‹7oÞp#PRR‚qãÆQÊÍÙÙÊÊÊÔß®]»�ü·—¯©-8ðŸ jŸ>}¨°ºº:xzzâòåË ¢™;vŒ&_WW—oÙ$%%!::NNNÔq S¦LÁãÇÁ`0hÌÖ ""‚=zÀÀÀ�7nÄ–-[¸îÁↄ„´´´8Ì@Я_¿6ÙH³ýMM&€Ænß¾}1oÞ<¬X±GŽH^yy9å�ÈÃÃÇoUYÕÖÖrõ8Ú½{wªó=aÂj “’’¨««ãÁƒ(((@jjj»6ÿdcnnŽèèh”••áØ±cPSSƒ®®.Ž;†wïÞ!22:::�÷mΛ7ééé¸{÷.TTT //™3g"-- ÷îÝCMM Õq6lÆììldeeÁÆÆrrr°°°@vv6®]»†9sæ´j¿»½²ßƒ™™Ž;†çÏŸ#66sçÎåk&Ýœ¦ñÙËæ®ÕÅÄÄhí™—Œæ´U&‹ÅÂÇùš©·?SWWG ú—-[†ùóç#33 <åîܹ>ĸqã(½äçç???0 ìÞ½»Õºëst’/ËÇñòåËVíékÞ.Û;ùùùPPPàØ—Ç뙩z¿ÿ~¨ªªâîÝ»mJ§µˆ‹‹SeûòåK\»v6±RVVÖª÷%ˆŽd2™:‹=Àl>Ðå%W´Z“wAÓ¹qã¦NJåùýû÷­ò-H¾ÛªÛ¹mÕá·}çKx³ní·‘í±ÕRÿ½ÿþ¸páPQQ‰'ÂÓÓSàúö%ÊëKðS�߿ŋCLL ¡¡¡£]wppÀ¡C‡h›V/\¸€‘#GÒfÅaoo˜˜ŽNކ†äääpöìY*ŒÉd"**Šæì"%%„êoË–-�öööˆ‹‹£ !‰‰‰X±b5ËÂb±àçç‡ØØXÄÅÅqÌ2­\¹’&ÿõë×|ËGCC¹¹¹´¿;v@ZZ¹¹¹X¿~=_Má¶¿GTT={ö¤œ²0™LZãh®tjkk‘››K›ñúøñ#áêêÊÕ¹KÓ™!nJìÑ£G‡¬¬,¸ÆéÔ©Ç eKÜ»w¹¹¹ppp€ªª* ÐªŽ‰‚‚²²²h®è_¼x¬¬,jo©¬¬,f̘øøxœ<y£G†””,X€¤¤$DEEAKKKà4¿ãÆÃëׯ333üúë¯PUUECCöìÙƒ~ýúÑÞµ‘‘Ž9‚³gÏRG}L˜0QQQHJJœ9s¨: "";;;œ={IIIÐÖÖиªœœœŒäädŽó"›Ï"¾yó†6ƒyçÎã—_~Ð8shaa½{÷"22’æì‰­¼ù“Õ”þýûC\\œ¶× ¾¾.\ &*¡¾¾žšem«LQQQÑöL‚wx霫W¯ÂÈÈ«V­‚½½=®^½ [[[tíÚ•¯Üùóçsè¥ùóçSá“'OXw ¢“„|] ­CÖ¥KèêêÒêksøµËöNNN,--©Áˆˆ~ûí7¾³ýÒÒÒ˜={6Ž?ŽàÌ™3­JGPšNú”——ãòåËPRRðß ²é™šÃ† ÿÿþÛ*]Ç‘#G"99™¶Z”ŸŸ999ß mEÐro gΜ¡­’ÊÉÉ!//c2ÿsøRß ~tëÖ OŸ>ý"g鉊ŠâÏ?ÿlóýí±ÕRÿý¼šššX·n<ˆÍ›7s,6´w~ºc jkkáââ‚øøxÄÆÆÒ°Í‘¬¬¬°oß>,Y²xóæ ÜÝÝNu.^¼ˆ3fÀÚÚ²²²´ƒ<ÇŽ‹îÝ»cÆ pttDMM äååÞ½{ÃÔÔT ¼º¹¹ÁÐÐ’’’ÐÕÕŵk×pâÄ ¤¤¤PqvíÚoooøùùáÇT>$$$0lØ0žòoܸ‹…÷ïߣ¸¸ÙÙÙèÕ«äåå!##C‹ûäÉtîÜ£Fâ*«°°xöì***N:AAA˜3g† †_~ùOŸ>ÅÆ±iÓ&tîÜeeePWWÇܹs±nÝ:TTTÀÊÊ ³gÏÆÀñéÓ'ÄÆÆ¢C‡´sþ.\¸€‡ruŸ™™ }}}ÄÆÆÂÔÔ{öìÁÇ¡¥¥ Á××aaaèÚµ+nܸ?þø¶¶¶““ƒ¨¨(nÞ¼‰]»vQf‰ü`;è †¡¡!òóóqøða¬\¹ÀæÅ×®]ƒ²²2Çï1cÆ`úôéX´hV­ZQQQìÚµ ®®®´r·¶¶† .\ �€©©)¦M›†É“'ó<þ£½ !!ùóç㯿þÂÁƒ4Î6;88௿þÂÎ;iæ^***’’ÂÚµk‘›› �PVV†´´4<==96‚ëééaéÒ¥�W¤�@SSsçÎ�RqÃÂÂàíík×®Q222ðÇ`Ê”)xõê¼¼¼(“O ±ó:gÎX[[ÃÕÕ•¶"Ïî¤?~ŠŠŠ!"%%…M›6aÙ²e`2™••EBBªªªZ<Û²9„ØÛÛãõë×HIIù,™...ÐÖÖ†””Æû÷ïÓêr[¹sç1yòdš—eAPTTäc{ýmÚ>øé®êêj¾:IÈ×gÈ!Ø»w/.]º„.]º`ôèÑpwwÇÌ™3!##UUUA\\œúfòk—í””8;;ÓÂfÏž   ôíÛ;w¦ÞÐÐ�OOOhiiAFF¥¥¥¸yó&ÍÛ§ éHKKãöíÛ¸wï^‹}ƒµk×ÂÃÃ;wÆÞ½{add„ñãÇht*Ó|Piccƒ 6ÀÛÛS§NEYY?~Œ%K–´ª\šbccƒmÛ¶ÁÝÝ666x÷îÖ¬Y&Š…W¹·¶¹¦^«Ç]]]¸ººÂÍÍ """ÈÊÊ‚ ڜΗø^‚ŠŠ ºt風¨qqq¨‚ЩS'CJJªMÅ~¤þUZZîß¿‘#GBLL gÏž…††$%%Ûäùú{ñÓ �óóó�®\?ŽéÓ§CJJ ñññ ‚——”••Eó:·oß>0™LÄÅÅ!..Ž&‡½’åàà€îÝ»#::ĤI“°uëVg+UTTššŠððp¬Zµ zzzHII¡fäjjjàêê �ðôô¤Ýëææ†íÛ·ó”?}útj6$'';vì@@@@›:y ¸¬¡¡###$&&"00IIIHJJÂÛ·o1fÌ„„„`âĉ�MüTUU©ó·:uêgggœ:u Û¶mƒœœôõõáïïO­|BgggšM7IIIhiiQ¦´&L@ii)BBBðìÙ3hjjâàÁƒ”™áàÁƒ±téR$''#$$]»vÅøñã‘••Õ¢«ùæ 8ÉÉÉØ¾};N:xyyQ6÷cÆŒ‡‡ÜÜÜàïï &Ð~ <<!!!X¿~=$%%aee{{{Z:ì-ccc*Œ½Ÿ­©¸öŽ™™vìØAÛ‹g``€€€�“ÞN:aîܹؿ?µ2(** ;;;„……qìRTT„¾¾>FE 4 ¥¥…áÇÓVueee¡®®Në\¨ªª¢k×®ÔÇ!66–*w6�ÀñÁ1b:„ÀÀ@ôë×Oàó¶–-[†ž={âÈ‘#xûö-LMM‘˜˜(°¾`0>|8z÷îMuÒÚ*SKK ééé CDDLLL ­­ýÙ¦v‹-úîæ-:uâ«“„|}¦NŠGaÉ’%ÐÔÔDXXeΞ1WSS£&m�ÁÚe{åõë×ÈÈÈÀßÿM _¾|9å¡PNN3fÌ &´>}ú„Áƒcÿþý¸}û6ȳÞR:ëÖ­ƒ‹‹ ^¼xC‡qÜ'!!)S¦`Ó¦M�€)S¦ÀÑÑ‘2<{ö,–-[F»G^^™™™ ¬Y³0hÐ XXX|–Ù௿þŠôôtìܹÎÎÎ5j¶oßNûÞ} x•{[á¶JÚ¹sgDEE!((®®®èÒ¥ ?{ ð¹ß Aèׯ±eËÄÄÄÀÄÄ .ÄË—/[-‹]Ž=Š®]»bÕªU­ºÿGê_IIIáÑ£G8|ø0ªªª`dd„¨¨(ˆŠŠþP@†““qrrâ¸À BZZ¬¬¬Žû¥º!B„ʶmÛ••…èèhžñàîîŽ;wîüô«F, úúúpttló`!B>AÛe{%==‹/ƽ{÷Zmšù½Óyóæ ~ýõWÜ¿ŸëJ¼FðêÕ+|הּK îܹƒÉ“'cåÊ•«ÔBZ&%%…çÑGM‰8.Ðè4ª9¡¡¡?ß  !B„| >ŒE‹ý”ƒ?&“‰½{÷BII ’’’¸pá˜L¦ÀæëB„¡ÓÖ}yí!n+[B8IMMÅâÅ‹¿w6Ú5 @LL ¬­­!**úYæÂB¾. !B„4ãÁƒˆ‰‰ÁÚµk¿wV¾ ?~Duu5þ÷¿ÿ¡®®&&&8uêí¸!B„Ž££#Ç1?J:ãÆÃÍ›7¿úàõG'&&†§N![7äääpðàAÌš5 ¿üò Í1¢öƒ°µ "äÿ‚ìUTTlµ ñ III¬^½«W¯þÞY"€`í²=ó%˜|ëtÄÄÄ8¼¥ áD8AÆŸN:ATTC† Á¾}û0oÞ<tìØ666ß;kBšñS!Dˆ!B„"DˆoG‡ ** QQQ(**"88Ë–-CRRÒ÷Κf€B„"Dˆ!B„ùlØ«€:u‚²²2üýýáàà ð‘[B¾  !B„"Dˆ!B>öà½øûï¿cÆ ˜6mþý÷ßï=!ÿá�Pˆ!B„"Dˆ!_öà=TUU…‡‡¬¬¬ý½³'?é�0++ +W®ÄèÑ£aff†C‡¡®®Ž§¢¢~~~ÐÖÖ†­­-N:E»^UU…Ý»wcÆŒ1b\\\PXXȑֹsç°páB¨««ÃËË oÞ¼iU^ïß¿U«VaôèÑpvvÆ­[·h×KJJàëë ccchjjÂÏÏåååËüø1fΜ‰ÀÀ@žñV¯^Í×tee%Ö­[sssZøõë×Á`08þ"""¸Ê©¯¯G||</^ eeeØÚÚâÂ… _o‰Ë—/côèч½Ö××ãÈ‘#èÕ«ÊÊÊøÊi+gΜƒÁÀǹþþ¿·÷ÁïÝN:•k]b0<gùµ£æ´¥]µTÿ¹±f;yølÛ¶í«ÛG‹‹ vîÜùUä)***P[[K 366ÆîÝ»¿S޾,UUU˜;wn«ôþŠ““Ö­[÷½³ñÙ¬^½îîîí&~íáæÍ›066Fqqq«Òg±XPVVFLL ×ëwî܃ÁhµÜöÂÇQQQñ½³ñCÒtðÇþw̘1pssƒ©©)rrrZ%ïGé_•——ÃÔÔçÏŸÿÞYáËO7�ÌÎΆ  ???˜››ÃÅÅûöí£âTUUaæÌ™(((€§§'´µµaii‰ÈÈH*ÎÒ¥KqýúuØØØÀß߯^½‚……Þ¾}KÅùçŸ`mmqãÆÁÛÛ÷ï߇µµ5Þ½{'P^ïß¿ccctíÚ›6mB×®]addDuß¿555Ô××ÃÕÕ®®®ˆÅŠ+Àb±xÊnhh@TTÔÕÕù¬›šš ???žq233add___ŽkìÎWjj*._¾Lýéêêr•uàÀCKK ;vìÀÀ¡««‹k×® t½9UUUذa´´´8”Jqq1æÎ {{û¯:øò¼Þ¿w»víZZº|ù2<==!++‹ßÿkzüÚQsÚÒ®xÕÿæbÓ¦MèÝ»7߸íÁêøñãñÏ?ÿ|ïl|5ºuë&“ùEžñGxŸ?:, ÐÔÔüaÒ!„ðíSp£¸¸ùùù9rägç¡=’šš IIÉ6•ÍÿuDDD ** ˜˜˜@]]zzzðòòBYYLLLpïÞ½ïͯ¢gºc Fމk×®á×_¥Â*++±{÷n,Y² III(++É' .. Ñ ²,--Ñ­[7øùùAVV � ªª iii\»v fff`2™X³f öìÙC¹·ÕÒÒ‚ºº:àààÀ7¯!!!°¶¶†··7  Q^^ŽìٳݻwGvv6 @ÝÓ³gOÂ××rrr-Ê®©©Á±cÇpòäIž®Þ_¿~ gggØÙÙáòåË-Æ‹ŽŽ†««+ 9–ïkjj��:::åûÜS¦LužŽrrrpúôiŒ3†ïõ棰°/^„¶¶6íÚ¥K— ¤¤„¨¨(Ìœ9“oÞ„|>¼Þ¿w«ªªJ‹Ïb±àíí WW×]pókGÍiK»âUÿ›säÈüñÇ‘‘᯽Ã`0ü½³ñƒ… ÂÕÕ3gΤ¾YmaïÞ½èÙ³'ÌÌÌйsç/˜K!lJJJ——‡#Fü0騪ª"55µÕ÷ݽ{òòò<xðgçAÈÏEqq1’’’0kÖ,8::âÂ… 8}úô÷ÎÖWGJJ ÉÉÉß;ñÓ­�Љ‰Ñ� ##ƒÊÊJjT~øðaÌž=›ö!566ÆÃ‡qãÆ �@ÿþý©ÁÐxnðßj×õëבŸŸ}}}*N=ààà€£GòÍgYY‚ƒƒaiiI¥#""kkkìÝ»¥¥¥�@뤠V>}úÄS~·nÝñãÇCD„ûkfw®çÎ kkkžò¶mÛ†Y³fqí4ÔÔÔ OŸ>-þª««accC­°öîÝ›v˜ªˆˆäääÀd2º�7n„««+!øí·ßŽ´mmmáåå…^½zñ|>A`ªmmm¡¤¤KKKÓa~|üø{öì™™´µµ€ªª*�@DDzôè>Pñýüü ¬¬L3ÿ Å´iÓ>ûy¾¼Þ‡ ï¶)YYYHKKk±~ ÚŽšÓÚvÅ«þ7åÍ›7 M6ãðáÃpwwǨQ£ààà€›7oR×_¿~ÀÀ@XXX@II óçÏçX½|ðà\\\ ¬¬ ssóWƒvïÞAƒáÁƒÉ}ñâþüóOŒ5 Ó¦MChh(”••©ëvvvð÷÷�”––¢G8qâfÍš…aÆÁÕÕÏž=£ÉÌÉÉÁ‚  ¬¬ GGG¬_¿ž§Y¯ò¹uë -ß, ÚÚÚ8tèFüü|ØÚÚ‚Á`ÐLÑîܹC•ÙŒ38¥ðññ¡&¢££i3ýÆÆÆØ·o\]]1bÄ®2šÃ«}³eFFFÂÇÇ***”Nd2™ „ŽŽ °gÏ 2„2ÓÑÑAÇŽ‘––Æ3}~ 2>>>ÐÕÕÅ©S§ø~GÚ ‹ÅƒÁ@bb"VTTƒû÷ïSa999X¸p!† †E‹¡  €ºÆ®o§OŸ†½½=FŒ?þø¯^½ú*yþRäååQƒ"AÊ¡¼¼›7o†¶¶6455±fÍ”””�hÔ;ÞÞÞØ·otttàââÂ5 QG¬X±£F‚……6oÞL3»zõ*ììì ¬¬Œe˖Ѷ´dggƒÁ`Pe»mÛ6øøø`Ó¦MÐÔÔ„¡¡!¢££9V6²³³aeeE _SSCÕc„‡‡Óâ "7==vvvPRRâèOÕÕÕáÀ°°°Àˆ#°råJ¼yó¥¥¥`08wî1jÔ(DGGóÔüê×™3g0yòd�G4í¶DFFÕ×177ÇØ±c±sçNj’hYðÒýõttt¨ïd]]lllàè興ˆÈÈÈо¡ÏŸ?çxöoIQQttt°víZTWWÃÖÖ999¸}ûv‹÷ühý«–êiuu5UÆ:1mÚ4„‡‡ÃÔÔšššØ¶m^½z…ÀÀ@BOO¯UÏù¥øé€ÜÈÌÌ„¹¹9DDDÐÐЀ˗/st�ûöí �-~\<x��:t(�àåË—––FÏž=iñ „ÌÌL444ðÌ{¯ ´´4-¼_¿~�@35mJ^^пžò´8ðcOu,ù)7^²jjjðá訨`Ô¨Qptt¤™kÖÔÔ ++‹*Ãæ0™L¤§§CEEE ë„ܺu 999¨¯¯ç™?~eÐDDDpãÆ c×®]ÐÓÓƒ¥¥%ǞÖ „ÀÃÃ)))pssƒ‡‡Nœ8%K–€ÅbAGG•••”Òfï]ÌÏϧ™RÆÇÇó°o-w~ïþÈ‘#X¼x1äåå¹^ok;j¿v%èóäååÉdbÈ!´ðÕ«WcÔ¨Q ‚¬¬,´´´¨Øû÷ïñæÍØÛÛcÇŽ””„¹¹9^¿~  Q÷hkkCAA»wïÆ´iÓСCŽ´cccáé鉘˜(**ò•[YY SSS444 00ÖÖÖØ²e Ï竬¬Ddd$ììì°{÷n¼~ýK–,¡Úann.´µµ¡¢¢‚ÐÐP())ÁÇLJo¹µT>#FŒ€¾¾>mà“ŸŸK—.ÁÐЇ†¼¼<üýýqûömšÙyzz:ÆŽ‹¿ÿþJJJ077ÇóçÏ4š)›››£¶¶[¶l••\\\FË×®]»0nÜ8ìÞ½›CFsøµo6vvv`±XX·nÔÔÔ@››Î;‡5kÖÀÝÝ'NœÀÇ©{ÄÄÄ0aÂ\½z•oYòBOO™™™pqqÁš5k`hhˆ3gÎpì‘ÿܺu ÚÚÚøý÷߆áÇs¸‰¯¬¬ÄÖ­[1}útìØ±OŸ>ÅÔ©SiêöÆõë×iƒ"~¸»»ãöíÛðõõÅÆ1`À�šµÃ† pñâE,_¾Ó§OçšNEE&OžŒ=z`÷îÝpttį¿þJ;ÜýÚµk˜2e ‚ƒƒÑÐÐ�---j É]»vá—_~¿¿?fÏž™3gRZ ±¾Ÿ>}ãÆ£~7­Çþù'm°/ˆÜœœLœ8†††ˆˆˆ€¥¥%Í”ÞËË qqqprrÂÖ­[!%%E›P400ÀÈ‘#áååCCCžºà]¿444( ˆÜÜ\žƒ—¦ÄÄÄ ##+V¬À_ý…ÀÛÛ›‡›à¥;<<<ðþý{ìÝ»�pìØ1Ü»wÚÇþòåKdeeQò322 ¯¯ÿÕW¡¹ñàÁèèè`ÕªU°²²BXXºu놹sçR“‰Üø‘úWüêisâââPXXxxx ,, ÒÒÒ¨­­ÅÚµk±páBžñ‹ãääDòòò8þÞ½{'ð_LL ©¯¯è/99™|K®_¿N�œœB!ååå�×|(**’ÐÐPŽð††2þ|²`Á*,((ˆhhhpÄ=uê@*++yæëÒ¥K�)))¡…�äêÕ«÷”——“¡C‡’ð”Ý]]]²sçNZØ“'Oˆ´´4¹uë!„þù‡ÈËËó•µyófbffF »zõ*‰ŒŒ$çÎ#‰‰‰dÙ²e�9þ<§ªªŠ444p•N[,3n×?}úDjjjhñ^¾|I�ÿý—CFZZ@Þ¾}Ë÷[ÃìÙ³ÉæÍ› !„œ>}š� µµµ\çääqqqòòåKêþ‚‚€\¹r…Bˆ……%ïîÝ»DAA,]º”xyyB)..&�ÈÓ§O¿ès| x½6¼ÞýãÇ �réÒ¥ïoK;jNkÚ·úß”¿ÿþ›ÈËË‹E…‘ê7‹Å"VVVdݺu\e|øð� iii„Bœœœˆ¯¯/׸ÄÆÆ†œ;wŽ� ©©©-æ­¹Üýû÷}}}RWWGÅ9xð >|8õ»iý~óæ G™Þ¼y“� EEE„BæÍ›G6nÜHKwÁ‚Týq²˜Ê�� �IDATå¿ò‰ŒŒ$¿ýöùôé!„;v¥K—Rñ‡NŽ;Æ!³©.¯©©!âââäÔ©S„B8@LLLh:)&&†HHHwïÞ $£9‚´o###Žw™““C�çÏŸSaìr}òä ¶eË¢§§Ç5í¶ðáÃI†N Hqqñ“ÝÐÐ@�„„*ìÑ£G�)(( „4Ö ^u…]ߨeG!¥¥¥¤gÏž$11ñ‹åõKÂb±ˆ††‰‰‰!„ð/‡ºº:"..N"""¸Ê  TÝo)¼¼<€äæær•Ó¼.úô‰èêê???B!YYY�UwÙz¥) , kÖ¬¡~—””ÐÞ'»ÎòªÇüäFFF999R]]Íñ wîÜáªë ù¯®4ï×ñÒ‚Ô¯„„ Å¾KsÒÓÓ �RQQA…]¼x‘V-é�~º###ƒ� 'Nœ ={ö$/^¤â.[¶Œ¬ZµŠúmffFŽ9"Pž¿$�ˆŒŒ Ù·o!„‡’^½z‘çÏŸ“‚‚"))IÓiüh¯ý+^õ´ùw–]'šöqvìØA´´´h÷%'' <ŽŠ‰‰iÕÛÏÉɉüÔ+€Ïž=ÃâÅ‹±iÓ&j…¡[·n�@[Mž={ÆuŠôôtxyyQa’’’xòä ‡IDuu5úôéC¥SXXˆüü|êïéÓ§�@Íð57}cÏl²MNÙÔÖÖbåÊ•6lÍëß‹/hòÙT[WW‡?ÿü^^^_dóöرc1kÖ,Lœ8fff ¢E‹hÞ»uëÆuåÒ¥KpqqÁ®]»¸îñj麨¨èwÙÃòâÅ „……aæÌ™;v,âããQ]]-н<€ššµÚ �JJJ:t(5Û?cÆ ?~, ÙÙÙ˜5k,--ºº:ÊäFàö¿w &pÝ÷ɦµí¨9-µ«¶òîÝ;¨©©q¬¨7ýÍ`0 §§G™›æŠ7nÄ”)S(§555`±XˆçØÙ”ãÇÃÆÆ;vì€íZKrP+fMW*]µ`Ó£G�ú´¡¡III=z4-N§NøÊáU>ÈËËCnn.eÖZèÎ;cÀ€xÿþ=€F¯‡:::4¤¦¦†ÊÊJJGó“ÑAÚ7�«‘ÂÂBhhhÐöŒr[áíׯÍD²%***hß„üü|®+f]ºtÁ€ ##ƒW¯^}5sPn°X,œ>}Z ºÒ´nôêÕ ZZZ´òlO<þYYY¯¼tìØþþþXºt)œ‘˜˜Èñ=‘’’âØ^Ñ<Ì›7–––ðññÁ•+WxZ!‰ŠŠÂÌÌ ×¯_øÙ¤¥¥i¦rÍ÷ÿ ZyÉÕÖÖ†ŒŒ D«ï9r$dee[”%!!Aû-ˆîhmý¤}5•É6©oªWšë�At‡ŽŽÖ¬Yƒ©S§â?þ€––wÚ´iØ¿?Þ¿G!))‰ã[ð-Á–-[0þ|�ÀàÁƒajjJí=ÖÔÔäék¢½õ¯Zê¿óª§‚ЫW¯váÕù§�¾}û2dÍM²¨¨(&L˜€¢¢"ZüÒÒR0™LS²#GŽ`ùòåØ¿?Í9Dß¾}ñæÍŽcJJJ0nÜ8ªcáìì eeeêo×®]�þÛsÔ|ÿ ÛµOŸ>TX]]<==qùòeÑLŽ;F“ß’çͦ$%%!::NNN”‹ý)S¦àñãÇ`0ŸÝÁøñã‘™™ÉÓRnn.¬­­±aÃÊ$£5׿5ååå” ?~¼UeU[[Ëá®hİ;>&L@NN ‘’’¨««ãÁƒ(((@jjj»7ÿ~ﶪª Û·o‡““ÏAIkÚQsxµ«¶BÈ-u‡ðË/¿��nܸ :¾¾¾HHH â}üø/_¾äÙŽôõõ)S”¦&ì¼äûÙ¦›m¥iG‡ÅbáãÇ|Íß¡iùôéÓ+V¬ÀÙ³g‘›› &“Ù&χM{L&“ãÙÙõŒ—#+^¦À‚´onÔÕÕq½¯9õõõ•mnn.í› ¬¬LkuuuHKKƒ……–-[†ùóç#33“ëžÝÏ¥¥zÛÐЀÊÊÊ6Õ•Ž;¶[6ùùùPPPàpŠÂ«ý:;;ãáÇ7nöïßUUUܽ{·U鈉‰aß¾}8}ú4zõê777XXXð< S§N­ÒyÍ'µnܸ©S§RíæÓ§OÕc^rû÷ï . �˜8q"<==AÁû÷ï[í³-ºƒ_ýâ×¾šÃÖMÍq›#¨î`Lغ‘͸qãлwo\½zçÏŸ‡³³3‡/ŒoÁÑ£G1{ölZØÚµk[[[äææBGG‡ë½í±ÕRÿW=Aö“~ ~Êàû÷ï±xñbˆ‰‰!44”£á988àСC´Íù.\ÀÈ‘#i3’‰‰‰°··GLL ÇÀJCCrrr8{ö,Æd2…Y³fQa))) „Pì=6}ûö…½½=âââ(¥FAbb"V¬XA­\°X,øùù!66qqq³_+W®¤Égïïá…††rssi;v쀴´4rss±~ýz¾2šÂM)ß½{ÆÆÆTEg2™´ÆQXXˆéÓ§cþüùpuu帟ßõúúúo~þ˽{÷›› ¨ªªbÀ€­n°QPP@VV?~L…½xñYYYÔÞRYYY̘1ñññ8yò$F ))),X°�IIIˆŠŠ¢Íüýˆð{·�pöìY444ÀÄÄ„ãZÓº$h;@›Iä×®šÇ äääp|š·Ë+W®@]]ú¿ªª*¬­­1bÄÚ$T—.] ««Ëó̤ž={"((bbbpvv¦VCyÉõ$''ÓÚ‘ 0nˆŠŠÂÈÈˆã¼Ææú¡¹.�x—�X[[cß¾}ˆÅÒ¥Ki4‘V¯^9’ãÙóóó!''DZ?œMëˆ í›ƒÆíÛ·ñèÑ#ži½|ù’co)7tuuißBußÕ«Wadd„U«VÁÞÞW¯^…­­-ºvíÚâsµô^ˆˆˆà·ß~kq–[TT¦¦¦|ë �Úñ=•••¸rå ÏòüžäääÀÒÒ’ñ+6ÒÒÒ˜={6Ž?ŽàÌ™3­JhìT>ÎÎΈÇéÓ§iΦšNxBpéÒ%¨©©µå14:IaïÿýRù!** MMM¬[·ÄæÍ›QZZ 999äååñlqƒ—î�x×/vÿ¥iÙñj_lšöàÁˆ‹‹càÀ-æQÝqëÖ-8::bûöígÝŠ‰‰ÁÑÑñññ8tèÐwsÇmÀ¦¨¨ˆŒŒ ¬Y³ùùùÔþüæ´ÇþUKýw åzú#ñÓQ[[ ÄÇÇ#66–æx„mîbee…}ûöaÉ’%pppÀ›7oàîîŽððpJ9\¼x3fÌ€µµ5dee‘™™IÉ;v,ºwïŽ 6ÀÑÑ555——Gtt4z÷î SSSòêææCCCHJJRg¡8q)))Tœ]»vÁÛÛ~~~øðá• 6Œ§ü7n€Åbáýû÷(..Fvv6zõêyyyõOž<AçÎ1jÔ(®² QQQgÏž¡¢¢ÙÙÙèÔ©F…ùóçcøðá1bDEE‘™™‰\¹r@£‚UWWÇܹs±nÝ:<{ö 3gÎDmm-LLLhf(ÊÊʨ¬¬äy½[·n°··Çëׯ‘’’‚OŸ>!??ŸRä÷îÝCçÎ1hÐ ÔÕÕ¡¤¤„ÚŒ~óæMHHH`äÈ‘<gä¸ÁV\ÁÁÁ044D~~>>Œ•+WøÏ¼øÚµkT>›þ3f ¦OŸŽE‹aÕªUÅ®]»àêêJ+wkkkØØØ`áÂ…”I‹©©)¦M›†É“'ó<þ£=P]]Ýâû¨©©áùnùå444 $$®®®&=Íë X; ƒ··7®]»†òmW™™™Ð××Gll,LMMyÖÿ¦ôïßÏž=CUUͬuýúõèÞ½;444péÒ%\ºt ��yyy$''ãàÁƒ<x0GçÏÝÝ3g΄ŒŒ TUUQTTqqqšžéÝ»7<ˆ‰'RÎ$øÉµ¶¶†¿¿?\\\0kÖ,¼ÿÛ¶mkÝËn†‹‹ ´µµ!%%…ñãÇãþýû´6Âíýñ+ qÒê—_~Á–-[ŸŸOKsüøñ8yò$UùæÓÆÆÛ¶mƒ»»;lllðîÝ;¬Y³þþþƒ¡–h^GmßÍQWW§Ú»‡‡ÄÄÄ(gM)))iñ,LA¹sç1yòä“hÚV’““¹þŸWg–ÍìÙ³„¾}û¢sç·…¯X±‚g]a³|ùrTVVBVV‡†¦¦&Æßæ2øš¤¤¤ÀÙٙƫàéé ---ÈÈÈ ´´7oÞ¤yû$'OžàСCÐÔÔ„¤¤$²³³9~~~èÙ³' €ÔÔTܼy!!!mzÎW¯^áÒ¥K´ó•544`mmÍ·ó"-- ÷ïß§¾ÏgÏž…††$%%1~üxèêêÂÕÕnnnAVV,XÀS&/Ýð®_ìÉ ãÇCQQ¿ÿþ»@ƒ'''ÊzÅ××›6mâiÂOwTUUÁÙÙ[·n…››Þ½{‡+V 11‘ÒW“&MŠ+   €±cÇòÍã·DII JJJ<ãüHý+^õô[šÒ.?Ý�0??�À1 rüøqLŸ>RRRˆGPP¼¼¼ ¬¬Œ¨¨(šÍô¾}ûÀd2‡¸¸8šö쵃ƒºwïŽèèh<x“&MÂÖ­[9–ç[BEE©©©ǪU« §§‡””ª¡ÔÔÔP+$žžž´{ÝÜܰ}ûvžò§OŸN͆äää`ÇŽàøÀ Âh‡ÅkhhÀÈÈÉÉɘ3gNž<‰¸¸80 hkk#''‡zŽÎ;CUU•òäxöìYjE£ùÊjAA._¾Ìóº’’†ŽÞ½{£cÇŽ())††gÉ’%�½�VVVbÊ”)Ô5¶¹á›7oZ}P÷À‘œœŒíÛ·ãÔ©S011——µâ2fÌxxxÀÍÍ þþþ˜0aí·ÂÃÂõë×CRRVVV°··§¥Ã>;ÏØØ˜ c+ô¦^àÚ+¯_¿nñ}ñ}·YYYHOOçz†_óºðoG@ãÌŸºº:ºví*P»’””„––UGZªÿM™À^‚Ÿ>}Šß~û ···ÇíÛ·qàÀèéé!==Ú«`bb‚]»v!88Ý»wÇÔ©S1gÎê^sssJÇlÞ¼jjj˜;w.GÙ :G޾¾>aooÏSnŸ>}pîÜ9ÊÔVUUãÆ£íMl-ZZZHOOGXX"""`bbmmmªÓÄíýñ+ Ñ\múôéèß¿?ÇêÏÚµkáããƒùóçÃÑÑQ ௿þŠôôtìܹÎÎÎ5j¶oßNksüh^G:vì(PûnN‡°gÏcݺuèÕ«e‰ÂÞCU__ììl8:: œ?n,Z´ˆ¯ùQÓ¶ÒÒÿaùòå”g>999̘1ƒæÑOKK çÏŸÇßÿÈÈHLš4‰¦OÙL:‘‘‘xûö-,--±mÛ¶VïUý¼~ýøûï¿iá¼ÊáÓ§O<x0öïßÛ·oCQQ033kU:¢¢¢èر#6oÞŒ¢¢"Œ;©©©´væâ₨¨(ÃÐÐçÏŸoõ7 ÛuРATX‡NÕciiiØÙÙµÊs­””=z„Ç£ªª FFFˆŠŠ‚¨¨(DEE…   ¸ºº¢K—.044äÛáæ¥;�ÞõkĈ8tèѯ_? ´ÿ~ìØ±Ø¸q#$$$°dÉšU7øéŽ-[¶@DDK—.Ð8)˜””„ÀÀ@êœç!C†`̘1˜9sf«'·Û?RÿŠW=ý‘€ '''âääÄq¡¥eZn¤¥¥ÁÊÊJอùÈ "DÈ!“&M‚¦¦&å4ÊØØS¦L¡>Þí™M›6áÍ›74NŸ‹Å‚¾¾>[ÜÏ!HùÔ××CKK +V¬ ±ø³röìYÌ;OŸ>EÇŽqåÊŒ?EEE-‰ò³QZZŠ>}úàêÕ«ínEƒéééX¼x1îÝ»÷U¨ß*^àÕ«W´UúöJKºãkÔ¯ŒŒ èé顲²’«c³¯É“'O //»wïbøðáß4m!ŸOJJŠÀŽ{âãã[åä‡ÛÑE¡¡¡?ß  !B„|/ ±`Á899qx{ko<xèׯ?~ŒÀÀ@ÚY_­…ÉdbïÞ½PRR‚¤¤$.\¸�&“)°Y|KdggãÚµÿÇÞ™ÇSýüu•¡e-)i”J2ˆJ$Ù²LB¤…i¦M²¤Å|%-úEJ›J›’b†l“d’JMÚ0’A¢IQ²d)ÜóûÃã~ÆÇ½î½d¢æó|<<î9çó>çs–÷ç,ïsÎ.»0÷ßæòåËxùò%Fމªª*øúú" €êà‡††bÉ’%ÿ™Áßç¯}yŸs<üHNNƲe˺,þöð¥ë‰‰‰000¸5ˆ3�d```èDLMM1qâDdeeA__¿«“Ó&„444 88?†žž.]ºD3]m/ïß¿GMM þïÿþ ˜9s&âãã?z6<&&Ë–-ë’“í>, .\@ZZTUUñóÏ?Sƒæòòr\¹r ]œJ~8:: uíÁç?¢¢¢:åÔäOÁ—®;€f½{èÐ!xxxt›&º?Œ (C'S[[Ûæ! í¥¦¦Fè½w Ÿ]aúE^ÁÀÀÀЕ0ƒ?†Î„ü10000t&Ì�á?3�d```````````øÀ �þ#|‘À´´4¬Y³êêê033éS§ÐÐÐ@ SQQ???èèè`Þ¼yˆ§ùWWWãСC˜;w.”••áêêŠÂÂB®¸._¾Œ%K–@CCÞÞÞ(--mWZ<x€uëÖA]]ÎÎθwïÍ¿¨¨[·n…±±1´´´àçç‡òòr¡å?yòvvvØ·oßpëׯ§]êÊ‹ÊÊJlܸæææ4÷ŒŒ °X,®¿ÐÐPžr‡eË–AII óæÍõkׄöo‹7n@]]vÙ0G^XXˆ7oÞ”ÓQ~ÿýw°X,¼ÿžço†ÎÁØØ‡úWd———ÃÄÄW®\ùWäwMMM¨¨¨@cc#ÍýßÌ—ÿ*ÂèÅß~û , ?¦ÜªªªŸ~ú JJJ033ÃÉ“'Q__ÿo'¹ÛPSSƒêêê®NF—°~ýz¬^½ú“Æ£®®N}s !puu¥îó,++‹ÅÂíÛ·Ê<{ö,FŽÉ¥_ZsõêU°X,TUU}ä[tááá˜7oW_ï߀ÍfCII QQQ|õ'ß?ŸÃ÷­->—þÕç”Ç_Ü�0==¶¶¶9r$üüü`nnWWW?~œ S]] ;;;äççÃÓÓ:::°°°@xx8fÅŠÈÈÈ€­­-üýýñòåKÌš5 ¯_¿¦ÂüöÛo°¶¶Æ”)S°iÓ&<xð�ÖÖÖxûö­Pi}ðàŒÑ§Oøúú¢OŸ>022¢UUU˜8q"áææ777DGGcÕªU`³Ù|e755!""ˆŒŒä699~~~|Ãܾ}FFFغu+—§S“œœŒ7nPÓ§Oç)+$$û÷¶6öìÙƒ#F`úôé¸sçŽPþ­©®®†´µµ‘™™Ió{öì-Z{{ûuðÇðå@éê$𥸸’’’(((èê¤t;:³ì„Ñ‹EEE<OÀÎÉÉALL ôõõ±{÷n̘1›7o†¿¿§¥¯»ãïï//¯®NÆ'‡ÍfãܹsÐÒÒêÒx:ÚÒÓÓaiiÙ¥÷ vwž={†¼¼<L˜0¡«“Ònºû÷íKàsÉã/®…O˜0wîÜ¡ÝùRYY‰C‡aùòå`±X8þ<Þ¼yƒØØXê´>111lÞ¼èÛ·/üüü ++KÝ©¢¦¦iiiܹsfff¨­­…——Ž= [[[�€¶¶6444pîÜ9888LëÁƒammM›6ÅbÁÐÐååå8xð Ž= ¤§§cøðáÔ3 €¡¡!¶nÝ 99¹6e×ÕÕá×_ÅÙ³g±~ýú6ýzõ ÎÎÎX¸p!nܸÑf¸ÈÈH¸¹¹¡°°ééé\q€®®.DEE¾÷ìÙ³±páBê!dff"11“&MèßšgÏž¡°°ׯ_‡ŽŽÍ/55ŠŠŠˆˆˆ€À´1ü·‘’’Â… º: äúõëÈÊÊ‚ 0`À€ËF/666ÂÓÓ .DXXÍoÊ”)´Ž¹±±1 üüóÏïpÚº7EEEÈÉɲ²r—ÅÃb±°ÿþvË$„àܹsðööîŒ$~R,X€ |’¸rss!//o¿ýö“Ä×Y0ß·ŸÏ)¿¸@111® ?eddPYYIÊOŸ> ÐŽj766ÆÃ‡ñçŸ�† F»PSRRÀ?«]ÈËË£]ôÜ¿888à—_~˜Î7oÞ`ÿþý°°° âµµ5Ž;†²²2�  þ�`РA�€>ð•ß·o_ÄÅÅaêÔ©á]Ìl6›6m¢E‹`mmÍWÞ®]»0þ|ž—ºº: <¸ÍÁ_MM lmm©ÖAƒÑ.‘œœjkk…ò€mÛ¶ÁÍÍ „Œ?¡¡¡PPPàŠ{Þ¼yðööÆÀù¾Ÿ0pÕóæÍƒ¢¢",,,¸L‡ñþý{=zfffÐÑÑA@@�e&ŠþýûãÝ»wTx???())ÑÌ~ƒ‚‚0gΜ~Ÿcccœ>}«W¯†ŠŠ p÷î]Ê?776l€žžTTTàé鉗/_Òd\ºt ?üðáàà€¬¬,®xÞ¿¥K—ÂÂÂUUUBÉÍÌÌÄâÅ‹¡¤¤GGGlÙ²7nÐ\WY,._¾  ÙÌÉÚÚ!!!033ƒºº:vî܉ššJ!±±±°¶¶†ºº:¶lÙ‚E‹µyqwYYú÷ïÄÄDØÛÛCYYk×®¥ÒyêÔ)ÈÈÈÐêûßÿ ‹…¤¤$Œ1� ¤¤‹…Páþúë/¸ººBII sçÎ嚬)++ÃæÍ›©‰–ÈÈHÊš€“®ØØXÌŸ?cÇŽ…››Š‹‹y¾‡02溎͛7CUU•Ò/^¼ÀÏ?ÿ Ì™3AAAPRRâ— dee‘””eee:tHhkŒ–«ÃÃÃQ]]ÿýï\~¼.c®¯¯‡¼¼<¾úê«v§ésÃÇÇ[·nÅþýûÁb±hæT577Ço¿ý ù³mÛ6ìܹÚÚÚ077Gllìg3›Î!''‡°Ùl°X,š.xüø1­Ý–——cûöíÐÑÑ––¼¼¼PTT 9O6mÚ„ãÇCWW®®®<ãáÅÂ… ¹VœÏœ9ƒ™3gBCC>>>\íãùóçÈÎΦ *ËË˱mÛ6hiiA[[þþþ´ö‚™3gòÔmµ}atƱcÇàââeeeüðÃÈÈÈÀ­[·¨­!+W®Ä³gϨgŽ;===�*߯^½Š9sæ 88&&&ÐÒÒ®]»ðòåKìÛ·†††ÐÓÓCXX—ÅUëUÒ††„„„`Ö¬YPVVÆš5khÛ.^¼ˆyóæñÔ©m•1!.\ÀÂ… ¡ªªŠµk×Rõ¢ås[·nÅäÉ“1kÖ,DGGãÉ“'X¿~=444`mm›7oRÏ´þ¾EDDpéÜ–fÅœoBtt4æÍ›eee¬^½Ïž=CTTæÎ UUUøúú~´¹÷çÖ¿JIIÁÂ… ©þ §ßÏ«!L=ë ¾¸ /nß¾ sssˆˆˆ ©© 7nÜàX 2�¸:‹8æVcÆŒ�”””@ZZšk–yäÈ‘¸}û6šššø¦‰£¤¥¥iîC‡�š©iKrrr   €aÆñ• Í‡¸¸8êcÌ«Ã"¬¬ºº:¼{÷ªªªPQQ££#Í\³®®iiimš¬ÕÖÖ"%%ªªªBùBpïÞ=dffRûÚJŸ <h"""øóÏ?allŒ@OO\{Û‚$%%ÁÝ݈ÅòåËÁf³¡««‹ÊÊJʘ³w1//fÚ'pÀÞX¿~=TTTYYYhkkSûh‹‹‹Ñ·o_¬]»ÞÞÞ(,,„££#ÕÙ‹‹‹ƒ‹‹ LMMqøðaLœ8=zô ÉojjÂæÍ›qÿþ};v æ™${�� �IDATåfeeAGGªªª ‚¢¢"6oÞÌ÷=bcc‘——‡5kÖ`Ë–- £Í®Ÿ:u ^^^°³³ÃÎ;ñæÍ›6÷¿r¨¬¬ÄÎ;accƒ={öàùóç°²²B]]ŒŒŒPRR‚´´4*üÕ«W¡¯¯jv1..ÙÙÙ4]’’‚É“'ãÈ‘#PTT„¹¹9u luu5ÌÍÍQ__;vÀÒÒ®®®8|ø0-]áááX¸p!:„W¯^aùòåmîF&ÐÜe³Ùظq#&NœˆÊÊJ˜˜˜ ©© ûö탵µ5vìØÁ7Ï„aäÈ‘ˆGXX ªªŠãÇ·kŸ’0zñÁƒððð@@@�m²ª5¸zõ*‚‚‚àãッvªNê®,Z´Ë–-üyó �Íù¡££:tsæÌ¡µë 6 ±±Û¶mìY³`oo¸¸¸®z‘‘‘Ñ.ÊÕ«W#;;[·nŶmÛ0|øpHHHPþ>>>¸~ý:\\\`ccÓáx�àÑ£GX½z56oÞŒ«W¯bþüù´=s­W¶jkkaii‰7oÞ`Û¶mpwwG¯^½huøÉ“'ðððà©î¶/¬Î ‚¶¶6‚‚‚0hÐ êœsss¡ºº?þøãG틉‰Aaa!<<<àááÇCZZõõõذa–,Y‚åË—ÓVt!HLLÄ”)S(7oooÄÄÄÀÉÉ ;wî„””M/¤¥¥ÁÞÞ¾MÊ«Œ###±fÍXYY! �ååå˜1cµ8�4[‘III! �&&&°±±¾¾>±gϨªªÂÔÔOŸ>ípUVV"""Ø·o>|ˆ#FàòåËXºt)|}}qöìYìÚµ«Ãq�ŸWÿ*333fÌ€¡¡!BCCaaaA-ÎðB˜zÖ%899‘œœ®¿·oß ýE…ú»páù”ddd�$33“BHyy9À3£G&AAA\îMMMä§Ÿ~"‹/¦Ü‰¦¦&WØøøx€TVVòMWjj*@ŠŠŠhî?&�È­[·¸ž)//'cÆŒ!!!!|e·fúôédïÞ½4·§OŸiiirïÞ=B!¿ýö‘——(kûöíÄÌÌŒævëÖ-N._¾LÈÊ•+ �råÊ*Luu5ijjâ)388˜Œ=ºÍ<ãåÿáÃRWWG WRRB�?þøƒKÆ¥K—�òúõkïØ,X@¶oßN!$11‘� õõõ<gff’Þ½{“’’êùüü|€Ü¼y“BȬY³(y¹¹¹DAA¬X±‚x{{Byöì@ž?Þ©ïÑÙ‘ƒR¿Ùl6±´´$7nä>--� %%%¤¦¦†Œ=ºM]Á‘@ÆŒC?~Üf:ZÊ%„ü‘lÛ¶fñâÅTþ¾{÷Ž� —.]"„’’’ÂÕžOž<IÆŒCØl6y÷î‘––&III”}}=@Î;Ç3M¥¥¥´2'„²²22`À�’@!dåÊ•dݺu”¿™™ #„4·]�$77—+_Z꯺º:Ò»woO!$$$„Ìœ9“Ö£¢¢H¿~ýÈÛ·o©tµÔ=wïÞ%�ÚÌcA29éÚºu+í¹'N}}}ÒÐÐ@Ë×qãÆñŒ§#466’Ë—/SSS¢  ÀS/´F½XWWGLMMÉéÓ§ !„<zôˆ� =â’çííM��DMMdddt›}x{{𛓓W]à@¬­­in{÷î%ß}÷a³ÙÿZ:;6›M455ITT!¤¹ïÐZpêK~~>ihh ½{÷&¡¡¡<åòáþñBˆšš9yò$õ»å·‰WÛæ|K’““)7²zõjêwXXÑÓÓ£µS‚t#!¼Û¾°:£¥.{ýú5WZsrr¨|$„£G’éÓ§Bç{[éß³gÑÖÖ¦¥×ÙÙ™–'EEE49ýõϾ!¼ó½µNåUÆÕÕÕDNNŽ\¾|™r«­­%jjjÔw5 €ØÚÚÒâ333#~~~Ôï>ÁƒSõ¤õ÷í×_åÒ¹žžžÄÝݽÍô_»v� /^¼ Ü~ùå"++Ûf¯£t×þUxx8‘““#555\~Âô!xÕ³ .=ŽŠŠŠj××ÏÉɉ|ÑS‘ÅÅÅX¶l|}}©Õ£¾}û�mh6(..æ9Š BJJ Í.^RROŸ>å2O©©©ÁàÁƒ©x ‘——Gý=þ�¨¾–f^À?ûé8&§êëë±fÍŒ;sçÎ¥Ü_¼xA“ÿþ}ùÒÐЀŸþÞÞÞ²‰yòäɘ?>f̘333béÒ¥Ô d@s¾óšùNMM…««+8@›õä/**Ú%ûh^¼xÇÃÎΓ'OF\\Íä…˜8q"µÚ �ŠŠŠ3f >|�˜;w.Μ96›ôôtÌŸ?ˆŒŒDCCez"Ì pWÓrõ„ÅbAOO2±&„àÒ¥KX³f LLL¨:ýþý{¡  �cÇŽmSöÊ•+±víZ9ròòò”;?¹MMM8þ<ÔÕÕi²Úk’'%%…üü|455¡¨¨%%%7nåßz¥²-ZæÏÀ¡­­MÕƒ9sæàĉ¨ªªÂ£Gpþüy´+âââ>|8µúu÷î]èêêÒÚ!g5Ž£—ZÓ¿�Üú’ƒ°2[[Jdggcúôé´Õ aW2h:///çO=zôÀСC¡¨¨ˆ‡¶ù-å £9‚µ§ØÇÇuuuxøð!ŒŒŒ0mÚ4ž§Iÿ`³Ùˆ‹‹ƒššZ›aZ·‰'"++«]'_w%ÿý7ÒÒÒ„Þÿ׳gOøûûcÅŠpvvFBB×÷DJJŠk{E{ãáÅðáÃa``@YæBpþüyÚÊVff&¦M›&tÛl©9´nûÑCœoËSt9ºIØï¯0 8«® 2„¦_Z¯’æççc„ ••*^:µuáÙ³g´ïJ¯^½`jjÊuR|Kdeei}JQQQÈËËwêI­œ¾iËmHRRR(..þè9»[ÿª­þ»ŽŽddd```€ÀÀ@äçç·ë=yÕ³®à‹�¾~ý5jmÿ¨¨(¦M›F;²h¶u®­­å2É ƒ‹‹ Nœ8A;teÈ!(--åºö¡¨¨S¦L¡”›³³3”””¨¿�øg/_ëý5ÔÁƒSn ðôôÄ7H3-øõ×_iòÛ:y³%çÏŸGdd$œœœ¨+fÏž'Ož€ÅbјADDS§NÅíÛ·ùîßÈÊÊ‚µµ5|||`hhØnÿOMyy9u�‡‡Μ9Ó®¼ª¯¯çy ¼„„5™6m233QXXˆ¤¤$èêêBCCÈÏÏGrrògaþÉ‹=zà믿мodË–-077Çž={h§ôr>¿º³bÅ Œ3¡¡¡´?¹l6›~ -n3A§ò CÏž=©I)S¦`РA¸uë®\¹ggg®½ÍÂв“U[[ËeÊÉ騵µWixGdÍAÇÌ·EEEMç)))q]sïÞ=8;;cêÔ©‘‘AII LLLøÊF/>|ø«V­ÂéÓ§!** ‹Eu¿ýö[žÇâ‹‹‹ãÛo¿…dddpñâŽ÷çÎû÷ïQRRÒ®=}œúû¹ì›ÌË˃‚‚×¾<~ïìì쌇bÊ”)8qâÔÔÔ››Û¡xÚKïÞ½©¼-))Á;wh“oÞ¼iWy Ò@çé!aâjOÚ…çÏ?ÿ„•••檪ªvéaÒÍù¦µÎ'111¾&ç¼&Ù™œwÆ·K˜wDwì_µÕ6l®]»†€€�TTT`ÆŒðôôº¾uF~u_ä�°ªª Ë–-ƒ˜˜‚‚‚ &&FówppÀ©S§h›V¯]»† &ÐV`oo¨¨(®•¦¦&ääähóÚÚZDDD`þüù”[RR!ÔgŸË!C`oo˜˜ªB€U«VQ³,l6~~~ˆŽŽFLL ×,Óš5khò_½z%0455‘••EûÛ³g¤¥¥‘••…-[¶”Ñ^ $77ÆÆÆTE¯­­¥5ŽÂÂBØØØà§Ÿ~‚››×ó‚ü?ùý/÷ïßGVV ¦¦†áÇ·«c¢  €´´4<yò„r{ñâÒÒÒ¨½¥²²²˜;w.âââpöìY¨««CJJ ‹/ÆùóçmmíN·ƒÖuñæÍ›ÐÐÐ��ÄÇÇÃÊÊ zzz3f í�໚­¬¬Œ°°0DDD`÷îÝ”;?¹¢¢¢022ÂüA“õ1@YYYôë×¶‡€×G uý@›U®¬¬ÄÍ›7©z &&GGGÄÅÅáÔ©S<7¥·÷¾« &àÂ… ´v“——999®=Ñüh9#ÛQ™ãÇçzNØç Aƒh:+++�Íp‹-‚¾¾>FމÜÜ\¬]»–6+ÜÂèÅÁƒs…9wî�àܹsÈÊÊBïÞ½y– ‹Å‚¸¸85 ò¥Ãb±h²^½zaúôé\Wõ´¤´´”6¡ó×_ÁØØø³É³ÌÌLXXXPƒŒ?^àl¿´´4,X€3gÎ`øðáøý÷ßÛ°´œü*//Ç7 ¨¨àŸAeË{/ÇŽ‹?þø£Ã“5¼è,=Äaó½#üþûï´UR999äää<,«= 6 ½{÷¦µ•ÆÆF\»v­Í³:Bß¾}ñüùónq—^wì_µÕšûZZZظq#Nž<‰íÛ·Óög~|q×@Ô××ÃÕÕqqqˆŽŽ¦<2|øpÈÈÈÀÒÒÇÇòåËáàà€ÒÒR¬^½ÁÁÁÔ üõë×1wî\X[[CVV–v‘çäÉ“!!!8::¢®®òòòˆŒŒÄ AƒÎ4spww‡¡¡!$%%©{îbcc‘””D…9pà�6mÚ???¼{÷ŽJG¿~ýøšÈÍ3Ul6UUUxöìÒÓÓ1pà@ÈËËSlOŸ>…¸¸8TTTxÊ*,,DEEŠ‹‹QQQôôt|õÕWPQQÁO?ý„qãÆAYY¢¢¢¸}û6<H>õæÍhhh`Ñ¢Eظq#Š‹‹agg‡úúzÌœ9T<JJJ¨¬¬äëß·o_ØÛÛãÕ«WHJJ‡hf`÷ï߇¸¸8Fމ††Q'ݽ{ýúõÄ ¸&Á9 gÿþý044D^^NŸ>5kÖ�øÇ¼øÎ;T:[þž4illl°téR¬[·¢¢¢8pà�ÜÜÜhùnmm [[[,Y²ýúõ�˜˜˜`Μ9øþûïù^ÿÑØ²e $$$ ©©‰ÔÔT¤¦¦" �@s' ,, £F‚¸¸8Ž=J=7dÈlذëÖ­›ÍÆ7ß|ƒììlhjjÒN,SWWGXX¬¬¬0zôhXYYñ• �®®®ÐÑÑ””¦NŠÐʰ½ôïß>>>prrBMM פÀ]ÿ9¸¸¸ ²²²²²8}ú4´´´0uêTÊßÔÔ«V­‚‚‚&OžL¹4rrrˆŽŽFSS†*Ôê ­­-víÚ…Õ«WÃÖÖoß¾…——üýýѧO.st^>|›6mÂ;w0bÄ2ÛÂÚÚþþþpuuÅüùóQUUõч�ÍR|÷ÝwرcÍŠBddd„Ò‹­u$gp2nÜ8ªó|üøq¤¤¤ÀÄÄÒÒÒhhhÀ¹sç ..cc㎼ÚgǨQ£pìØ1¤¦¦¢W¯^PWWÇêÕ«agg¨©©áñãÇèÝ»7õͼzõ*Ö®]‹Ù³gãåË—ðööFttt¿‰ð$%%ÁÙÙ™æ¶`ÁbÈ!§]ÞÔÔOOOhkkCFFeee¸{÷.í´Oaã‘––Fvv6îß¿ßfß`Æ ððð€¸¸8Ž;###Jçdddp *mmmáããƒM›6ÁÊÊ oÞ¼Á“'O°|ùòvåKK:ª3Ú ¿|ï(/_¾Djj*ͲdêÔ©˜>}:ÜÜÜàî¥añâÅŽGJJ ¾¾¾X¹r%jkk!++‹sçΡººššìê TUUÑ«W/øûûÃÐÐ?FLL ÌÌÌ:-aùœúW—.]ƒ¨~äÅ‹¡©© III'ôw'¾¸`^^u_ëYó3gÎÀÆÆRRRˆ‹‹C`` ¼½½¡¤¤„ˆˆÚ›ãÇ£¶¶111ˆ‰‰¡ÉáÌT;88@BB‘‘‘8yò$LMM±sçN¡g+UUU‘œœŒàà`¬[·zzzHJJ¢fäêêê¨Õ/OOOÚ³îîî´•^ØØØP³!™™™Ø³g:ÔÙ ¡]Ь©© ###\¸p?üðΞ=‹˜˜°X,èèè 33“zqqq¨©©Q{µ.^¼HÍlµ^YÍÏÏÇ7øú+**bܸq4hzö쉢¢"hjjRa8§””TVVböìٔǔ´´´”ï©M¼1b.\¸€Ý»w#>>3g΄··7Õqž4i<<<àîîL›6öÛÀÀ�ÁÁÁ8xð ¶lÙIIIXZZÂÞÞžç.ÖEÎ� å)pÝ{{{dgg#$$zzzHII¡VbV­Z…÷ïßcݺuPTTļyóh§^zyyAZZ»wïÆ«W¯ ««ËóHKKKøûûÃÞÞrrråjkk#%%‡Fhh(fΜ 21srr‚„„Nž<‰êêjª¾qÌ™Z×VVVÇëׯaaa]»vÑ:_£F¤I“`ggG›¬èÝ»7"""°eˤ¤¤`Û¶mB �¿ù椤¤`ïÞ½pvv†ŠŠ vïÞÝ®‰¬¬,444¨ŽZGe<—/_ÆîÝ»áää555L™2…Ú#ÚQttt0mÚ´’Ñâýû÷ˆGff&¾ýö[èëëcÆ 2åý±²²Â£G°|ùrhiiáðáÃ077§¾—Û·oÇĉ±hÑ"ê555ôéÓ‡ê´EGGsÝíÚ]yõê®^½Š#GŽÐÜ]\\¨ åää0wî\êdÃ>àÛo¿Å‰'Ñ£Gcß¾}|;àmųqãF¸ººâÅ‹8uê×sýúõÃìÙ³áëë  ù>^GGGÊDðâÅ‹X¹r%íyyyܾ}˜?>FމY³f}”ÕDgè!aà—ï…×*©¸¸8"""777ôêÕ †††=X¹r% €°°0¼~ý&&&HHHèÔÕð¡C‡"!!;vì@TTfΜ‰%K– ¤¤¤Óâ–Ï©%%%…GáôéÓ¨®®†‘‘""" **úY �YNNNÄÉɉ˃3†K—.ÁÒÒRè°ÿ•P†ÿ*ÆÆÆ˜={6V¬XÑÕIá ›Í†¾¾>?zï+‡/^`èСÈËËã9_VV†ÁƒãÖ­[´•½Ö<}úòòòÈÍÍ¥ð¥âëë‹ÒÒRÚáQ ÿvíÚ…´´4DFFvuR:DJJ –-[†û÷ï·Û4³«ã)--Å7ß|ƒ`ôèÑ"óK$ �/_¾¤,Y:‹¤¤$¡z‹‹‹kסpœ« Zôå­�2000´Emm-Ž;EEEHJJâÚµk¨­­Úl›¸víF=z 44...Ô¾ƒŽ’˜˜¦ÞŸ+'OžD¿~ý0tèP<yòûöí£.Ïe`øÜè込-n’““±lÙ²®NC§À �þ3¼ÿ555ø¿ÿû?444`æÌ™ˆçy‰°B››‹ÀÀ@ :–––pppø¨“¾Þ¿C‡ÁÃãۜÖ™BÐÐЀàà`<~üzzz¸téÆßÕIc`莎ŽB_ÓÝâ™2e îÞ½û¯^?w¢¢¢øžÂÉÀð9Á´v†N§åAFÝ III¬_¿ëׯï4™ŠŠŠí2[äœbÉ111üõ×_›´n ‹ÅÂÒ¥K±téÒ®N C7¡£1u:ó�“O˜˜X»Eû/ò1… Ý/òn˜ Ãf�ÈÀÀÀÀÀÀÀÀÀÀÀð�200000000000üGø"€iiiX³f ÔÕÕaff†S§N¡¡¡¦¢¢~~~ÐÑÑÁ¼yóO󯮮ơC‡0wî\(++ÃÕÕ………\q]¾|K–,††¼½½QZZÚ®´>xð�ëÖ­ƒºº:œqïÞ=šQQ¶nÝ ccchiiÁÏÏåååBËòä ììì°oß>¾áÖ¯_/ðèÊÊJlܸæææ4÷ŒŒ °X,®¿ÐÐPžr‡eË–AII óæÍõkׄöçEg”ùÇòûᅢÅbáýû÷<ÿ׸qãÔÕÕi—ï *[+++žu‰Åbá?þh3.Aí¨5iWmÕ^xyyµ™†ÆÆF°X,$$$”Óݨ©©Auu5Ím×®]v‡bw`ûöí¸uëVW'ÁÁÁ<Û§^ýý÷ßÔ7JQQóçÏGrrr§úãprrÂÆ»:Íúõë±zõên±±1:Ô¦ÿÝ»wallŒgÏžµ+~6› %%%DEEñôÿ믿Àb±Ú-·»ðþý{TTTtu2ðùô¯ÊËËabb‚+W®tuRòÅ �ÓÓÓakk‹‘#GÂÏÏæææpuuÅñãÇ©0ÕÕÕ°³³C~~><==¡££ „‡‡SaV¬XŒŒ ØÚÚÂßß/_¾Ä¬Y³ðúõk*Ìo¿ýkkkL™2›6mƒ`mm·oß •ÖÀØØ}úô¯¯/úôé###ê_UU…‰'¢±±nnnpssCtt4V­Z6›ÍWvSS""" ¡¡!ðbÝäädøùùñ sûömaëÖ­\~õõõ”œ7nPÓ§Oç)+$$û÷¶6öìÙƒ#F`úôé¸sçŽPþ­é¬2g誫«áããmmmdffÒü•í† huèÆðôô„¬¬,¾ûî;žñ jG­éH»âWÿ[SXX___ 4H`ØÏ xyyuu2þU$$$päÈ‘–#è¤WAÔÔÔÀÈȈ«=ÈÉÉ�âããñèÑ#ØÚÚ" �LJ‘‘?:í ‡ÍfãܹsÐÒÒúlâ!„ìSðâÙ³gÈËËÄ >: Ý‘äädHJJv(oþ»|¬îÿT|q×@L˜0wîÜÁ7ß|C¹UVVâСCX¾|9X,Ο?7oÞ 66½{÷Ð| òæÍ›aaa¾}ûÂÏϲ²²Ô\jjj––Æ;w`ff†ÚÚZxyyáèÑ£°µµ�hkkCCCç΃ƒƒƒÀ´<xÖÖÖØ´iX, Q^^ŽƒâèÑ£@zz:†N=3`À�bëÖ­TG€uuuøõ×_qöìY¾GÞ¿zõ ÎÎÎX¸p!nܸÑf¸ÈÈH¸¹¹¡°°ééé\q€®®.DEE¾÷ìÙ³±páBê>dff"11“&MèßšÎ*s†ÎáÙ³g(,,Äõëס££CóT¶jjj´ðl6›6m‚››[›Gp jG­éH»âWÿ[†µk×BFF†o8†î‰……V®\‰uëÖAII©Ãr®_¿Ž¬¬,,X°� h÷óµµµ——o³ƒïèèH»#òûï¿ÇÛ·o SSÓ§›áã(**BNN”••?›xÔÔÔ:´zœ›› yyy|ûí·†/)))\¸p¡«“!_Ü  ˜˜m ��222¨¬¬¤Få§OŸÆ‚ ¨�Ðl"ñðáCüùçŸ�€aÆÑ>®’’’�þYíÊÈÈ@^^ôõõ©0ýû÷‡ƒƒ~ùåé|óæ öïß *X[[ãØ±c(++�Z'�µªðáþòûö틸¸8L:""¼‹™Ó¹^´h¬­­ùÊÛµkæÏŸqqq.¿ºº: <¸ÍÁ_MM lmm©Õ¶AƒÑ.Sœœjkk…ò€mÛ¶ÁÍÍ „N+sApÕóæÍƒ¢¢",,,ÚmFúþý{=zfffÐÑÑA@@�eRŠþýûãÝ»wTx???())Ñ̃‚‚0gΜvÅû)?~<BCC¡  Àå'LÙ¶$-- —.]j³~ ÛŽZÓÞvůþ·¤´´°³³£Ü!ˆ…µµ5TTT°aîçÊÊʰyófj$22’kÖùÒ¥Køá‡ ¨¨dee� °jÕ*¨¨¨`Ö¬Yؾ}{›f1ÆÆÆ8}ú4V¯^ 888àîÝ»�šÛéˆ#¸L··mÛ†eË–ÁÇÇ[·nÅþýûÁb±h¦g¥¥¥ðõõÅä~{Æ�� �IDATÉ“ahhˆÈÈHÚ,(¿zÏÉßÍ›7Ã××ZZZ<e´F™›6mÂñãÇ¡«« WWW®òPWWÇ–-[°hÑ"Ê$wèСpwwJóCVVIIIPVVÆ¡C‡„¶ áðîÝ;¾÷޵ü>ÍïUWWii饷3a³Ù\fÎ?‹Åƒ(·ÌÌL,Y²cÇŽÅÒ¥K‘ŸŸOù•••¡ÿþHLL„½½=”••±víZ¼|ùò“¾K{ÉÉÉ¡EÂäCyy9¶oßhiiÁËË EEE�ڮíãë[·naáÂ…PRRÂÊ•+i[ZÒÓÓÁb±¨¼¶=¦§§ÃÒÒ’º@¾®®ûö탮®.tuuL /ŒÜ””,\¸Òs-ÛaCCBBB0kÖ,(++cÍš5(--EYYX,._¾ GGG¨¨¨ 22’f2 4×Kœ:uJ`ýúý÷ßñý÷ß�zôèÁÕÞxqõêUª¯cnnŽÉ“'cïÞ½Ô$9ЬƒÃÃñyóf¨ªªRý"~úìÿûtuu©ïdCClmmáèèˆÐÐPÈÈÈо¡ÿý7×»ww>·þU[õ´¦¦†ª‹@s˜3g‚ƒƒabb---ìÚµ /_¾Ä¾}û`hh==½v½ggñÅ �yqûöm˜››CDDMMM¸qãWpÈ!�ÐæÇ¥  ��0fÌ�@II ¤¥¥¹fvGŽ‰Û·o£©©‰oš8{[¬‡ �4SÓ–äää@AAÆ ã+@›?qqq(((€«««@åÆOV]]Þ½{UUU¨¨¨ÀÑÑ‘f®YWW‡´´4*[S[[‹””¨ªª åOÁ½{÷™™‰ÆÆFžÏtF™·FDDþù'ŒqàÀèééÁ‚¶Ç„xxx )) îîîððð@ll,–/_6› ]]]TVVRJ»±±aaaÈËË£™RÆÅÅ °w5‚êAe†eË–A^^ž§GÛQkµ+aß'''µµµ5jåvúôixyyÁÎÎ{öìáìVWWÃÜÜõõõرc,--áêêŠÃ‡SaâââàââSSS>|'ND=PQQï¿ÿýû÷Ç¡C‡àèèˆo¾ù†ï¥Îëׯ‡ŠŠ !++ mmm¢OŸ>pttį¿þJuÈêëëqüøqØÚÚbÑ¢EX¶læÍ›‡ììl¸¸¸P2ÓÒÒлwoìܹ ,€õTï98p�_ý5üýý¹d´FX™>>>¸~ý:\\\`cc�8uêU;wîÄ›7o¸½êêêˆý(Ó¯‘#G">>aaaHHH€ªª*Ž?Žªª*¡ž÷îΜ9EEEèêêÂÛÛ›ç^ªôôt$''ÃÓÓ?†££c‡Óü)¹wïtttðÝwßáðáÃ7nRRRha*++±sçNØØØ`Ïž=xþü9¬¬¬hêîFFFmP$ˆÕ«W#;;[·nŶmÛ0|øpÚÀŸWn0zàÎ;˜={6öïߦ¦&hkkSM^j„$&&bÊ”)Ôowww\¾|^^^øùçŸiƒ}aäfffbÆŒ044Dhh(,,,h¦ôÞÞÞˆ‰‰““vîÜ )))Ú„¢&L˜�oooB__—.]¢üóòòšš CCC�üë—¦¦&öïß�ÈÊÊBvv¶€’l&** W¯^ŪU«ð¿ÿý!!!Ø´i-ÌÂ… Áf³±qãFLœ8Q >óðð@UUŽ;�øõ×_qÿþ}j{II ÒÒÒ(ùW¯^…¾¾þ¿¾ Ý™|Ný+Aõ´5111(,,„‡‡<<<pøðaHKK£¾¾6lÀ’%K„zÇNÇÉɉäääpý½}ûV迨¨(ÒØØ(Ôß… ȧ$##ƒ� ™™™„BÊËË �žé=z4 ârojj"?ýôY¼x1åH455¹ÂÆÇÇ�¤²²’oºRSS �RTTDsüø1@nݺÅõLyy93f á+»5Ó§O'{÷=}ú”HKK“{÷îBùí·ßˆ¼¼¼@YÛ·o'fff4·[·n‘ððprùòe’@V®\I�+W®Paª««ISSO™ÁÁÁdôèÑmæ/ÿ>ºº:žá;£Ì…eÁ‚dûöí„B �R__Ïówff&éÝ»7)))¡žÏÏÏ'�ÈÍ›7 !„Ìš5‹’—››KÈŠ+ˆ··7!„gÏž�äùóçN󧢤¤„� üñG›aø•ý“'O�’ššÚæóiG­iO»âUÿ[räÈ"//OØl6!„wïÞYYY’””D…©¯¯'�ȹsç!„„„„™3gÒÚGTTéׯyûö-©©©!£GæYsrr�’••%0í„bddD<Hýf³ÙÄÒÒ’lܸ‘&¯  €Bȵk׈¼¼<©©©!„âííM\\\h2ˆ ÍmñâÅÄËË‹"\½ ¶¶¶mÊh°2 ȇ¨0ïÞ½#ÒÒÒ|˃B._¾L�—/_òŒ¿½466’Ë—/SSS¢  À·Mpˆ‰‰!ÑÑÑäÊ•+$**Š˜ššyyy®º>nÜ8€� .$/^¼è”4 MMM\yúèÑ#€äççBšËwÛ¶m´ç/^LéºÒÒRZyBHYY0`�IHHøoÑ~Øl6ÑÔÔ$QQQ„ÁùÐÐÐ@z÷îMBCCyÊãU‡yÅ#HѾs>| Ó§O'~~~„BÒÒÒ�ª= Ó‹ŠŠhåy÷î]€üý÷ßTŽÛÓ§O…’Näää(}Ó’¿þú‹§®'䟺ÒZG†‡‡“ñãÇSù·gϲbÅ Ú3üê×¹sç€6û.­III!�HEEåvýúuZ‘­[·ÒžFŸ]½z•� ±±±dÀ€äúõëTØ•+W’uëÖQ¿ÍÌÌHXX˜PiîÎt×þ¿zúîÝ;€\ºt‰òOhÙÇÙ³gÑÖÖ¦=wá¡ÇQQQQí£ñã999‘/z°¸¸Ë–-ƒ¯¯/µÂÀÙëÕrh6 (..æ9Š BJJ ¼½½)7III<}ú”Ë$¢¦¦ƒ¦â),,D^^õ÷üùs� føZ¯pf69&§êëë±fÍŒ;–vâÞ‹/hòïß¿/0_ðóÏ?ÃÛÛ»S6oOž<óçÏÇŒ3`ff†ÀÀ@,]º{÷î¥ÂôíÛ—ç*Jjj*\]]qàÀžæNmù‹ŠŠò4Çë¬2o‹/^àðáð³³ÃäÉ“‡šš¡ž-((Àĉ©•G�PTTĘ1cððáC�ÀܹsqæÌ°Ùl¤§§cþüù°°°@dd$(“aV€»;‚Ê>..Ó¦Mã¹ï“C{ÛQkÚjWåíÛ·˜8q"µ¢^\\ŒââbŒ7Ž Ó£GÚ3wïÞ…®®.­}Lœ8•••xþü9ŠŠŠPPP€±cÇrŧ  €üؼy3nÞ¼)Ðú åj?‹Å‚žže=nÜ8Ìœ9“Z‰ILLÄŠ+h¦Ó‚dÍ+²³aê=/ZÊh°2¥¤¤h¦éEEE())á[�(“òÖ'ž¶¦¢¢‚¦óòòx®NõèÑC‡…¢¢">|È¥‹xaeekkkèééaΜ9øå—_ &&†ØØXZ¸ÜÜ\¼{÷™™™”¹}[&ÕÝ6›ÄÄD¨««ÓÜ¿úê+®°-ëÖÀ¡­­Í·Þt%ÿý7ÒÒÒ„^yéÙ³'üýý±bÅ 8;;#!!ë{Òº󊧽z@TTfffÈÈÈúÝZ·ÇÖûÿ ¡©©IÛûÌ«mñ“«££ 00fœŸŸ &@VV¶MYýúõ£ý600@NN²²²(óÑÖæ}í­_´ù–29ûˆ9}?�\–cÂè3]]]xyyÁÊÊ k×®…¶¶6vΜ98q⪪ªðèÑ#œ?m¾Cw¥»õ¯Úê¿ó«§Â0pàÀvæÿoñÅ�_¿~ Œ5жWETTÓ¦MÃãÇiáËÊÊP[[ËeJœ8q‚v8Ä!CPZZÊuíCQQ¦L™Bu朡¤¤Dý8p�À?{ŽŠ‹‹iÏsÌL¹544ÀÓÓ7nÜ@`` Íäá×_¥ÉoëäÍ–œ?‘‘‘prr¢ŽŸ={6ž<y‹õÑaL:·oßæ»‡'++ ÖÖÖðññ¡L2ÚãßšÎ*ó¶(//§�òððÀ™3gÚ•WõõõÔÒ–HHHPŸiÓ¦!33………HJJ‚®®.444PPP€üü|$''w{óOaT¶ÕÕÕØ½{7œœœøšRµ§µ†_»ê(„Ú¾ΞB~¦„µµµ\¦Ìœw¥>€¼Ú’˜˜Ž?ŽÄÄD 8îîî˜5kV»Ž.ïÑ£¾þúk�Í—ü§OŸFEE‚ƒƒ;t HË0õ^ŒÖtT&'Ÿ™vrÂñÓ_@s=n©•””¸êâ½{÷àì쌩S§BFF%%%011á+—ýúõƒ¶¶6O3Ð>}ú@UU¾¾¾HMM¥ö‡v5må_SS*++NVð¢gÏž÷âvyyyPPPà:…_=rvvÆÃ‡1eÊœ8qjjjÈÍÍmW<Ñ_}õU»t^ëöøçŸÂÊÊŠÒU>|àÙ&Û#wذa¸víPQQ3fÀÓÓ„TUUµÛ${ðàÁXµj.^¼ˆ¬¬,ÔÖÖ <5UPý¦Í·„Óäg–/¬>ã L8úšÃ”)S0hРܺu W®\³³3×¹ÝîØ¿j«ÿίž ƒ0ûI?_ä�°ªª Ë–-ƒ˜˜‚‚‚¸žƒƒN:E›Ý½ví&L˜@›‘LHH€½½=¢¢¢¸Vššš““ÃÅ‹)·ÚÚZDDD`þüù”[RR!Ôߎ;�4 íííC)5B°jÕ*jå‚ÍfÃÏÏÑÑш‰‰ášýZ³f Mþ«W¯榦&²²²h{öì´´4²²²°eË2ZÂK)çææÂØØ˜ªèµµµ´ÆQXXüôÓOpssãz^cc#­£ÝYeÎû÷ï#++ PSSÃðáÃùv6[£  €´´4<yò„r{ñâÒÒÒ¨½¥²²²˜;w.âââpöìY¨««CJJ ‹/ÆùóçA›ùûT¶�pñâE455aæÌ™\~-ë’°í�m&QP»j^Xúõë‡ÌÌL*}Æ CïÞ½i{ Z$&L˜€ .Ðês^^äää0|øpjF½­Õ}‹…qãÆÁÙÙqqqHLL¤viÝN�p鈛7oBCCƒú­¯¯ÌÌLìÝ»´Õ2‹ÕîNž0õ^Z–{GeÊÊÊReÄ×G›³w´OŸ>|Ó4}útšþ%„Pû?‹ŠŠ°hÑ"èëëcäÈ‘ÈÍÍÅÚµki3Ôüh­Wëëë‘••E•Gë{N&¥ûßFDDãÇos–[TT&&&\w{òú–¼yó†ú¿²²7oÞlW½ù”dff‚*AùÀAZZ ,À™3g0|øpüþûïíŠà¯�Ð&™!HMMÅĉ;òš�šIáìÿš÷¼fggãÑ£G– 4× ---lܸ'OžÄöíÛQVV999äääðlñÂÚÚÇGtt4V¬XÁ5¸ãW¿8ý—–yǯÍshyàSAAz÷î#F´™FaôÙ½{÷àèèˆÝ»wsÝu+&&GGGÄÅÅáÔ©SÝú¸¶èŽý«¶úï@Ûõôsâ‹»¢¾¾®®®ˆ‹‹Ctt4íàNgÊÒÒÇÇòåËáàà€ÒÒR¬^½ÁÁÁ”r¸~ý:æÎ kkkÈÊÊâöíÛ”œÉ“'CBB>>>pttD]]äåå‰Aƒ =»ëîîCCCHJJRw¡ÅÆÆ"))‰ sàÀlÚ´ ~~~x÷~ýúñ4 kÉŸþ 6›ªª*<{ö ééé8p äå幎¨úô)ÄÅÅ¡¢¢ÂSVaa!***P\\ŒŠŠ ¤§§ã«¯¾‚ŠŠ ~úé'Œ7ÊÊÊÅíÛ·qðàAܼy@³‚ÕÐÐÀ¢E‹°qãFÃÎÎõõõ˜9s&Í EII •••|ýûöí {{{¼zõ IIIhjjê”2ç`‘ýû÷ÃÐÐyyy8}ú4Ö¬YàSÓ;wîPélù{Ò¤I°±±ÁÒ¥K±nÝ:ˆŠŠâÀpss£å»µµ5lmm±dÉʤÅÄÄsæÌÁ÷ßÏ÷úî@MM òòò¨ëýû÷!..Ž‘#G¢®®ŽoÙ~ýõ×hjjÂÁƒáææÆeÒÓº.µ£Ã‡cÓ¦M¸sçFŒ!°]ݾ}úúúˆŽŽ†‰‰ ßúß’aÆ¡¸¸ÕÕÕ@ÿþýáëë '''ÔÔÔpM€­­-víÚ…Õ«WÃÖÖoß¾…——üýýѧOôéÓ6lÀºuëÀf³ñÍ7ß ;;šššèÓ§N:---HJJ"==êlBhí„cF¶eËHHH@SS©©©HMME@@�•ž`åʕزe """h³•£F±cÇššŠ^½z 5y"l½çGërï¨ÌþýûÃÇLJoy�̓äÁƒcàÀB¥Ož<Áwß}‡;vð]‰æEEE,--±`ÁŒ1>|@tt4zôè+++�ÀÒ¥K1xð`Lš4  @EEŽ;†%K–Ðí]Å‚ ˆ!C†@\\œë²ðU«VAGGRRR˜:u*<x@Ó§\\\PYY YYYœ>}ZZZ˜:uê§|¡IJJ‚³³3Í_>455ÁÓÓÚÚÚ‘‘AYYîÞ½K;íS˜xž>}Ú¦ààçç‡`øðáHNNÆÝ»wqðàÁ½çË—/‘ššJ»kWSSÖÖÖX²d <<< &&FZ",—.]ƒ0aˆ‰‰áâÅ‹ÐÔÔ„¤¤$¦NŠéÓ§ÃÍÍ îîîAZZ/^ÌW¦¦¦&¾þúkìØ±yyy\þüêçà¸3gÎ`ôèÑøî»ï„”899QÖ+[·n…¯¯/_ HŸUWWÃÙÙ;wî„»»;Þ¾}‹U«V!!!šì155ŪU«   €É“' Lcwãsê_ñ«§‚NèïN|qÀ¼¼<êD·Ö³ gΜ ¤¤¤‡ÀÀ@x{{CII 4›éãÇ£¶¶111ˆ‰‰¡ÉáÌ;88@BB‘‘‘8yò$LMM±sçN®åù¶PUUErr2‚ƒƒ±nÝ:èéé!)) ŠŠŠ�š÷1qVH<==iϺ»»c÷îÝ|åÛØØP³!™™™Ø³g¸>°ÂB»,^SSFFF¸pá~øáœ={111`±XÐÑÑAff&õâââPSS£Nr¼xñ"5ßze5??7nÜà믨¨ˆqãÆaРAèÙ³'þúë¯N)sAŒ1.\ÀîÝ»™3gÂÛÛ›Úo3iÒ$xxxÀÝÝþþþ˜6mí·‚ƒƒqðàAlÙ²’’’°´´„½½=-ÎÝyÆÆÆ”G¡·<®»òêÕ+hjjR¿—/_ ùØäÇ ,Û´´4¤¤¤ð¼Ã¯u]·# yæOCC}úôª]IJJB[[›21m«þ·dÿœüüùsŒ?�°råJôë×'OžD}}=lmmñÃ?PÏ|óÍ7HIIÁÞ½{áìì ìÞ½›Vþ^^^––ÆîÝ»ñêÕ+èêêbÒ¤IEÏž=±}ûv<~ü“'OFrr2•?-Û {{{dgg#$$zzzHIIáZ•Ò××ÇÁƒ1cÆ š»••=z„åË—CKK‹vRi[ôìÙS¨zÏÖåþ12œœ !!“'O¢ºº³gÏ�Ú>«üü|˜™™µkº5:::˜6mZ‡žýꫯàììŒøøxìÚµ rrrÐ×ׇ¿¿?µªíææ†sçÎáĉ¸ÿ>ÔÔÔ0oÞ<XYY uë¿‹‹ u2ŸœœæÎK;ÑO[[W®\Á‘#GSSSš>å`ee…ððp¼~ýصk—Ð'l~J^½z…«W¯âÈ‘#4w~ùðáÃ|ûí·8qâ²³³1zôhìÛ·fffíŠG��WWWDDDàÙ³g044Ä•+WÚµÿ½%Ô‘#GRn=zô@pp0öïß7BZZ .Ä­[·„–+%%…GáôéÓ¨®®†‘‘""" ** QQQDDD 00nnnèÕ« v¸¿úê+ØØØ`ذa<WŽùÕ/eeeœ:u ûöíÃСC±ÿ~¡ößOž<Û¶mC¿~ý°|ùršU/é³;v@DD+V¬�Ð|rìùóç±oß>êžçQ£FaÒ¤I°³³ãknÚ]ùœúWüêéç4�d999'''.Îh\.]ºKKK¡Ã¶Ìx†/BLMM¡¥¥E;4ª»`llŒÙ³gS‰¶ðööFee%?QʺŽ/^`èСÈËËÃØ±cñæÍ›ÿgïÌãjÎþ?þº™”e,™ŒL´L&JQ)¤T*J.-¨±MH‹Êô›%ßJÊ–ß‘,m"²ÅX[$$‘AjDhÕ=¿?zÜϷϽ·Û-QÌçùx|þøœÏ9ïs>çsÎùœå}ÞÊÊÊØ·o,,,:;yÿZÊÊÊ0pà@ܸqã‹XÑHKKƒ££#<xðI¨Ÿ+aãåË—4Í®JCCôôô°råJÚù¬Ÿ¢|]ºt ¨¨¨z†ç§àÙ³gPPPÀýû÷»„�CÛHMMyA"))©M‹ÿý7Ÿ[xxø×·ÈÀÀÀÐY°X,,[¶ ‹/†““Ÿµ·/7oÞ ""qqq”O£GpùòeüôÓOèÖ­8�ju %%ÒÒÒv8/׉ }y_r<Â8wî;-þ¶™™‰ôôô¯¾>§¤¤`êÔ©­n b`à �:333Œ3999044ììä´™‹/¢wïÞ_ĪK{ „àþýû Á?ü�+++888P{SSS±iÓ&‘Uù�`Ù²e"{ð¥Ä#Œøøø±šü9HLL„££ãg³-¼ÿðòòê2&º>Ì�¡DZcÇZ=;¯3àݳ(6›ýU5ÒÊÊÊ´3Jy‰ŠŠúŒ©ah iii‘ͪw>—åÕζð ೫7~ -©©~Šòŵú¹‘À½{÷>{¼ _6Ì�¡ƒéŠƒ?Ñè T>%_å9€ ü0@† Ì�á_ÂW9�ÌÈÈ€§§'´´´`nnŽèèhÔ××Óü¼}ûÐ×ׇNœ8A{^UU…ˆˆØØØ@MM ®®®(((à‹ëÂ… X²d ´µµáããƒÒÒÒ6¥õáÇøõ×_¡¥¥gggܹs‡ö¼¨¨›6m‚‰‰ tuu€òòr‘å?}ú¶¶¶Ø¹s'ß³óçÏcîܹPVV†Î;'TÖµk× ¥¥E;Ìh:XšÅbñ]ïß¿(çSåmdd¤Àt4ÏÓ/^ÀÛÛZZZ˜?>’““…Êl §OŸ¦½7ïý¿ A奡¡IIIptt„ªª*ìììpùòeêù¬Y³~C‹…?ÿü³Å¸Z«G¼´§^UTT`ݺu˜>}z«ï¾fÍšVÓÐQܾ}&&&(,,ü$òkjjPUUEs»téX,*++?IœŸ›Ó§OcÏž= dee ,û��TVV"!!‹-‚ªª*ÌÍͱÿ~ÔÕÕurÊ;'''¬[·®³“ñѬ^½��---êûBàêêJ!*++‹ÅÂÍ›7[•yüøq(**¢¡¡á“¤¹±±oß¾ýdò…ÑÐÐ�‹…“'OÒÜ9TUUÿÉâ~ûöm§ÕŸC‡AUUµSâþÒøRúWååå055ÅÅ‹;;)­òÕ �333amm EEE`úôépuuÅÞ½{)?UUU°µµE~~>¼½½¡¯¯KKK:tˆò³|ùrdeeÁÚÚxùò%,,,ðÏ?ÿP~þøã°ÙlŒ?ëׯÇÇÁf³ñæÍ‘ÒúðáC˜˜˜ W¯^ð÷÷G¯^½`llLu+++1fÌ444ÀÍÍ nnnHHHÀÊ•+Ááp„ÊnllÄÑ£G¡­­ØØX¾ç§NÂÂ… ahhˆ]»vaäÈ‘066Xh«ªªàëë ===dggó=¯¨¨€——®]»F»Z2¦ð©ò¶¦¦ÆÆÆ|é““Ðt¾™©©)�XXX &&Fh^2´ aå%** »v킞ž¶oßyyyLž<ééé�€µk×ò}?oooÈÊÊbôèÑãk­ñÒžzuóæMcÓ¦M­¾AAü Ñ‘ß�� �IDATýý!--ݪߎ€Òj{ð1bÍš5ŸL~W _¿~ðóóûèíÇZ�ävDÏ;G«“'O�äææ"11†††Ø¶m¦L™‚ 6 00ð£âeè88’““¡««+ðy{ËHff&¬¬¬>ÙùÅÅÅèß¿?=zôIä·‡ÂÂBäååA]]ý“Å1aÂüñÇŸL>ÿ/ÅzñWgT]]ééé´3_***¥K—‚ÅbáÔ©Sxýú5ÍT»„„6lØ�KKKôîÝ••¥ÎTÑÔÔ„ŒŒ ÒÓÓannŽÚÚZ¬Y³¿ÿþ;¬­­�zzzÐÖÖFrr2ZMkXXØl6Ö¯_‹###”——#,, ¿ÿþ;úôéƒÌÌL :” 3`À�aÓ¦MÔÀFï޽Ñ#Gpüøq¬^½šïyll,±xñb�€±±1þþûoœ?S¦L¡ù-,,DAA®\¹}}}>YåååPSSkñ‡Ç˧ÊÛÚÚZ(((´˜Žäädˆ‰‰ÁÏÏâââ066†¤¤$Ö­[ ‹/Ê´uWFXy™9s&æÏŸO!5uêTdgg#%%cÇŽ…¦¦&Í?‡ÃÁúõëáææÖâ÷i­ñÒžz 777 33SèûÇÄÄ`ÕªU<x°P…¦¦f««÷ ÂÑÑÑ¢¢"Nž<‰¹sç¶[Ε+W““ƒyóæaÀ€mÿîÝ;�À¤I“N ?žÖ¾™˜˜`àÀpqqÁÿýßÿARR²ÝigèŠŠŠ›› 555¾g, »víj³LB’““áããÓIüb¸ÿ>ðã?vvRDBJJ gΜéìdˆÄW·(!!ÁwàçàÁƒQQQAÊ<ˆyóæÑLµ›˜˜àñãǸuë�`È!´5û÷ïà3´YYYÈËË£ôܯ_?888àðáí¦óõëרµk,--©xÄÄÄÀf³±gÏ”••�­“ €ZUøðáƒPù½{÷FRR&L˜�11þÏ,))‰Û·oSr8îß¿yyy>¿#GŽÄ ¤¤$0®W¯^ 5{Ÿ’’KKKj…¯£òÖÏÏnnnÔw­®®:ˆ+((À°aÃh+###<}úYYY-†ãÂTÛÙÙAYY–––|ªÃ­ñþý{üþûï077‡¾¾>‚ƒƒ)õº _¿~¨®®¦ü@UU•¦žŽÙ³g·)Þω°ò"--M;@XLL rrr¨­­(+##çÏŸoñ\:Që/m­W[·nÅܹs[í`—––"88¶¶¶”›‰‰ <Œ5 ¸}û6€¦UkyyyJEŒ‹ŸŸQVV†~ýú!%%öööPSSêU«ðòåKÊoff&X,Í7í~~~ ‚žž¦OŸŽcÇŽÒjü¾¾¾Ø´ivíÚ‹E©µq‰ŠŠÂ´iÓ ¥¥…   ÔÔÔPÏ!8sæ æÏŸ ¬Zµ EEEÔóK—.Íf#** æææeð"ŠL]]]\¸pÖÖÖÐÓÓ£Võ²³³±xñb¨ªªbÙ²eظq#¥nØ­[7,_¾[·ný(8YYY¤¦¦BMM "kƒpy÷îØ¢ö„ Cžëêê   €îÝ»·+ÍŸ‡Ã§Ò÷äɰX,<|ørËÎÎÆ’%K0bÄüòË/ÈÏϧž‰Rö»"¹¹¹B-óçÏç[±‹‹Ã´iÓ ­­ ___¾róüùsܽ{—T644àÀ077‡ªª*\\\pãÆ DGGcðàÁ´öôï¿ÿ¦¶B´®¨¨ˆú÷«ªªÒ¾‡ÃAll,fΜ‰qãÆ!((ˆúgq¿QBBììì ¦¦">>666ÐÐЀ¿¿?Mœ‚cÇŽÍfcÔ¨QX»v­À¼â]õLKKÃüù󡬬LõZkÃ�àÑ£GX¹r%F lÞ¼ïß¿‡––òòò`gg‹ES5½yó&-Z555¸»»ÓTì·nÝŠõë×cÓ¦M7n,,,€§OŸbõêÕÐÖÖ›ÍÆõë×ii*))ÁÿýßÿaÔ¨Q˜9s¦Hç²þ[øÒúW‚Ê"ÐôOg±X¸pဦÒìÙ³ SSSèêêbëÖ­xùò%vîÜ ###´é=;Нn�(ˆ›7obúôéCcc#®]»Æ×4h�´øsáªE >@SE–‘‘á›åUTTÄÍ›7ÑØØ(4MÜýl2224÷~ø�hêÍÉÍÍ…’’† "T>�?.ÎÎÎÈÉÉ••RRRàîîŽÐ:®¢Èâp8xüø1Ö¬YyyyXXX 44”ŸgÏžáÚµk|ûˆ¸´'o !¸sç²³³©[uu5âââ ¬¬ŒI“&ÁÇLJÖh<|?" åïΛ·nÝ‚‰‰ BCCa``�KKK¾=‘-A——RSSáîî///;v K—.‡ÃÁ¤I“PQQA©.644 &&yyy4Uʤ¤¤.P·°²×œÚÚZ¤¥¥ACCCàó˜˜8::BAAAàóöÖ#^Z«W¢¾Onn.jkk1lØ0šûêÕ«1jÔ(„„„@VVzzz(((@¯^½°lÙ29r„*‹uuuØ»w/µú]QQ   Ì™3Û·oÇóçÏ1kÖ,jµHÖ®]‹††øùùÁÂÂöööHJJj5þ ÀÑÑvvv¸{÷.\\\hrŸ>} ///lܸ111´ÕØØXxzzbÖ¬YFyy9¦L™B”;v yyyðôô(ƒQdÞ¸qÿùÏ`aawwwôéÓ999Ð×ׇ††ÂÃᬬŒ 6Ðd1ÙÙÙµ—RQQ'Nœ@LL Nž< ìÝ»WdÕÒwïÞ¡ºº5j–-[F©G7çÑ£G¸téÂÃÃáëë‹°°0‘ËhWáÎ;Ð××ÇèÑ£±{÷n¨¨¨ --æ§#Êþç&++«Íªšýõ<<<°aÃ\ºt sçÎ¥Ù-à] ‹ŽŽÆ–-[àììŒ={ö@GG}ûö…±±1JJJ‘‘A…½té ¡¦¦Öb8iiijÕ")) wïÞ¥Úˆˆ>|+W®Dpp0rssñÛo¿QíEEEŽ= ìܹ?†¼¼<.\¸€_~ùþþþ8~ü8¶nÝJ¥éàÁƒX³f lmm±}ûv€„¤¤¤`üøñ�š& ¦L™###8p�–––––nµ {ûö-f̘~ýú!""Ë–-Ã÷ß <x  ÄÝ»w)UëëׯãçŸÆÌ™3 iiiÌž=›Ö¯ ƒ””‚ƒƒajjŠ9sæÀÐÐÊÊÊØ¾};444`ff†gÏžhÚz0mÚ4466bçΰ±±º¯ý߯—Ô¿j©,¶Dbb" àåå///ìÞ½222¨««ÃÚµk±dɑޱÃqrr"¹¹¹|×›7oD¾âããICCƒH×™3gÈç$++‹� ÙÙÙ„BÊËË �éøé§ŸHxx8Ÿ{cc#Y´hY¼x1åBtttøüž8q‚� BÓuõêU€ÑÜŸ<yB�7nð…)//'Ç'QQQBeó2yòd²cÇš[cc#Ù²e @]/^*§¤¤„� þù'åöáò{÷nrâÄ ráÂITTT›Í&uuu„B8©¬¬(ócòöÇäÝ»wÔóÄÄD’@.^¼Hâã㉙™QPP ò¸°° 0€,\¸œ;wŽ=z”Lž<™� ¡¡¡Bß½%æÍ›G6oÞL!$%%…� Þ›÷>;;›ôìÙ“”””Páóóó �rýúuB!”¼û÷ï%%%²|ùrâããC½�òüùóv¥÷s"¨¼ðI~úé'õåéÓ§�¹zõj‹áÛSxiK½Ú¼y3177oñùÿû_¢  @8åfllL¨{‡C¬¬¬Èºuë!„äææ�äÑ£G„B._¾LHMM )--¥•B)++# 'Ož$„’‘‘A�ÐÊUs‚ƒƒ ›Í¦¹íرƒŒ=šp8¡ñBˆqqq¡…OKKãkçöïßO†N8©ªª"rrräÂ… ÔóÚÚZ¢©©IåEk2xi‹LÞò°páBâççGs[¼x1U¯!äåË—�MþÇÐÐÐ@.\¸@ÌÌ̈’’’ÐzÀåÆäСCäÂ… ääÉ“dÅŠÛfªÝÖÔÔ$YYY’探±±‘� ÉÉÉ”Û_ýE�üü|BHSþ û&¢”ý®‡Ã!:::$>>žrÓÔÔ$û÷ï§î›ÿ3¸ïؼâ¶ñçΣÜ|}}‰‡‡u¿bÅ ²|ùrõdÅŠä×_¥îÍÍÍILLL«áž={F�û÷ïSn¯_¿&={ö$999”ÛãÇ©öFPú/_¾L�/^Pn‡&²²²¤±±‘TWWYYY’ššJ=¯««ã+/EEE´òrèÐ!"''GµKÍÖ†qŸ5‡æ¨¨¨#GŽÐÜ,,,h߬¡¡¨¨¨Pß$88˜X[[Ó˜››“€€�êþÇdàÀTYØ¿?144$õõõ”Ÿýû÷ébèºý+ae±ººš� çÏŸ'„þÏmß¾èééÑÂ9sFäqT|||›Æh‚ÆxNNNäËš2l#ÅÅÅptt„¿¿?µÂлwo� -M+YÅÅÅGñáááHKK£éß÷ïßÏž=ãÛìYSSƒRñ //ºž?�”ª"ïÌwf“«É¥®®žžž1blll(÷/^Ðä?xð@¤¼ ÂéÓ§‘••…ÂÂBcúôé)<qqq,]º3fÌÀ”)S°xñbÄÄÄ 11‘Ú+Åb±ðí·ß ÿ1y+..NSÉ›5kØl6 0{öl>|8vì€&µ¿‹/‚ÃáÀÓÓééé À¿‚Ô/^¼ÀîÝ»akk‹qãÆ!))I¨ÊZs=z„1cÆP«Í� ¬¬ŒáÇãñãÇ��ÄÅÅÃá 33sçÎ…¥¥%bccQ__O©Äˆ²ÜÕ¹zõ*\]]*Pu7)) 'NÄØ±c[”ÑÖzÄKKõª½¼yócÆŒáSÕk~Ïb±```@©›«¨¨`Ú´iÔêGJJ –/_NS«nþ»ï¾ƒžžUfD¡[·n´û1cÆ ''ååå"Å/ RRRÈÏÏGcc#ŠŠŠPXXêy=`ff&Ô:js¼´EfóòÔØØˆS§NAKK‹æ‡WerÀ€èÙ³'m–_õõõ´67//¯_¿æó×­[7üðÃPVVÆãÇùþ9‚7næÎ‹)S¦ÀÜÜ!!!øå—_(«‘\|}}ñîÝ;<~üÆÆÆ˜8q¢@KÊ]‡ƒ”””V¿ ðñeÿsò÷ß###Càþ?Q:t(¦NJiÆBpêÔ)j% �æÌ™ƒäädÌž=Ä«W¯¨g³gÏÆ¾}ûPYY‰¿þú §NÂÔ©S[ 'ˆÂÂBÔÖÖbôèÑ”EZ®jK=Ü6·¹:½””Š‹‹ñþý{£¸¸˜VyÛ'€ÕS__ƒÆÔ©SBSÖ†)))aáÂ…°´´Ä† pýúu¡Zuuu8qâ,X@½ó7ß|ƒ¼¼<¡+ù²²²´ÿ¸¸8¨0wîÜÁäÉ“i+ßʠϗJWë_µÔVEá»ï¾k“5ÿOÅW;�üçŸààà€aÆÑö­ˆ‹‹câĉxòä ÍYYjkkù111pqqÁ¾}ûhÆ! „ÒÒR¾£ ŠŠŠ0~üxJÇÙÙªªªÔ à{ŽŠ‹‹iṪˆ¤Üêëëáíík×®!$$„¶‡êÈ‘#4ù\a¼zõ ¿ýöV®\ --- :žžžÇŠ+>º`röÖÔ*?6o[£oß¾ÐÓÓ£©t©««cÿþý¸sç¶mÛF &EP•——SFj¼¼¼צAC]]@sÓ}úô¡:>'NDvv6 ššŠI“&A[[=B~~>Î;×åÕ?E!''l6¾¾¾022â{^UU…mÛ¶ÁÉÉIèO²-õˆaõª½BD2KÝ­[7jR„ÅbaáÂ…8xð Þ¾}‹ÈÈH˜™™ ÿÍ7ß|”ÁnêÞ½{»âDóŽ:·óÇ»ŸNBBBh> Úãö±29Þ¿ߪZ>‡ÃAmmm«ÜÞ¾}KksUUUiG™�M=gggL˜0ƒFII LMM…Ê„˜˜&L˜€›7oò¥KRR?þø#|}}1xð`œ={¶Íò?5-åecc#***Zý&‚øØ²ÿ)ÉË˃’’ÒG-éÙ³'õO())Azz:Íæ¤I“——‡E‹!==ŠŠŠHLLÐd(HZZ7nÜÀÅ‹áììLÙE®¥t�MViŸ?N»Ù h AmCk–‹oݺ…Y³fQíÿ!CpùòeãíÛ·˜2e ¼½½AÚ†IHH`ïÞ½HIIÁwß}wwwXXXàíÛ·ãíÞ½;dddÂ÷΂þU\õKš»ÕÕÕuÊ_ ]±ÕRÿ]XYaÿ¹ÏÉW9�¬¬¬„££#$$$ ÚsDGGÓf°._¾ uuuÚŒäÉ“'aooøøx¾•ŽŽäääh?ÝÚÚZ=z”fE.55„êÚ²e €¦A޽½=©†‚“'ObåÊ•Ô,‡ÃA@@�˜˜YYYZ:<==iò[›Ñ@ýty,î Œ÷ÌDa*ðÜY’æû¶xgq:"ohmÞJ]]rrrh3¼i?räf̘Ñâ´æ<xð�999ppp€¦¦&†Ú&à JJJÈÈÈÀÓ§O)·/^ ##ƒÚÿ(++ $%%áøñãÐÒÒ‚””/^ŒS§NáèÑ£ÐÓÓ9ήHAAæÌ™ƒE‹ÁÍÍM Ÿ³gÏ¢±±Ó¦Mã{Ö¼“.j=èe°µzÅë_Túöí‹ììl¾zÁ[/¯_¿mmmêÞÐÐÙÙÙØ±c´µµùÊl󦊊 \¿~*3‚àM{ii)mFþÞ½{011¡¡Ââg±Xm>'kÈ!èÙ³'moECC._¾,R]k†[ÇÛ+“kñ—w¿ o{ÁøjI[‹´´4­Í%„`Ö¬Y�š&©,X�CCC(**âþýûXµjmVZ‚:Å÷ï߇‰‰ X,–À¶™ÅbARR²ÕtNÄÄÄ0räÈ'ÅÅÅajjÚê7Ú^ö;“ììlXZZ¶ye§ù@¸¼¼×®]ƒ²²2€ÿ *ia¾ýö[˜››#44kÖ¬ÁîÝ»4 x–-[†¤¤$DGGó´h)—æeLVV xöì† B»Z2TÔ‚ê± ~ÄéÓ§i«ž@S¹ÑÕÕźuë°ÿ~lÞ¼™ÚÿÛZ¦¢¢ggg$%%!%%…2Ä%&&FkÅÄÄ0}útܺu ²²²´wþ˜:6räHœ9s†ÖguÀðo +ö¯Zê¿ÂËâ—ÂW·þ\WWWWW$%%!!!v¦ÍСC1xð`XYYaïÞ½Xºt)PZZ DFFR3‹W®\ Øl6deeiµŽ7}úô¯¯/–-[†wïÞAAA±±±––y¦×ÝÝFFFèß¿?uÚ±cÇh–¡BCC±~ýz ººšJGß¾}1bÄ¡òoݺ‡ƒÊÊJ"33ß}÷äååáææwww”––BAAEEEˆŠŠ‚‡‡ŸÕššššÓƒ )) EEE`ûöí°²²Â÷ßòòr„††bÅŠT§l÷îÝX¿~=ÒÓÓ!//ß!yK½½=^½z…ÔÔTÔÔÔÀÊÊ óæÍƒ¼¼<>|ø€„„tëÖêœÝ¾}gÏž…¦¦&êëëqþüy$''#!!A¤6×°È®]»`dd„¼¼<<xžžž�þ§^œžžUUU¾û±cÇbΜ9øå—_ð믿B\\¡¡¡pssèQ£¨xØl6¬­­±dÉôíÛ�`jjŠÙ³gcÆŒBÿè +/ïÞ½ƒ­­-êêê0mÚ4šõUUUU|ûí·hllDXXÜÜܨ÷çòúõkhkkcÁ‚”GQêol­^ݼy†††HHH€©©) ðöí[ãíÛ·ÈÌÌD÷îÝiß hêÜ£ªªŠ¦†¸qãFôéÓ:::¸zõ*®^½Šàà`êù€°bÅ lܸGå›!tqqAEEdeeqðàAèêêb„ óŸ÷]&C«V­ÂÌ™3ñòåKøøø !!A¤ø‡ †={öàêÕ«èÑ£ŸÚž ¤¤¤àïï+V ¶¶²²²HNNFUUU[ƒ·ŽŒLWWWèëëCJJ &LÀÇiuøß@ãcŽïxúô)F-[¶]}n‰E‹AEEjjjÇÍ›7FYŒŽŽFZZLMM!##ƒúúz$''CRR&&&íN÷§`Þ¼y Á Aƒ ))Éw˜÷Ê•+[ý&@ÛÊ~g“šš gggš›ŒŒ îÞ½‹´øÏ^»v-¼¼¼ ))‰={öÀØØ˜zǬ¬,¾AeDDúôéƒüõõõ¸víMUÞÌÌ +W®„’’Æ'R8iiiÈÉÉ!!!øá‡ðý÷ßcóæÍptt„¸¸8444PXXˆ²²2,Z´¨]yÔ¯_?øûûÃÉÉ 555|“½@“ÇÕ«Wiç7Ÿ?>„ºº:$$$pöìYèèèP“|-µaÏž=Ctt4tuuÑ¿dff¢gÏžTÛ8aÂ?~œúÿüôÓOpwwǘ1c(Ãv•••¸yó&<<<Úmm—Íf# �®®®˜;w.*++i†qþí|Iý+ae±5 ý]НÍÌ­[·h†Mš_qqq”¿—/_’Õ«W“1cÆŸþ™¶ášB~þùçå4'))‰ØÚÚ’ñãÇ“M›6‘òòò6¥7;;›899‘Ñ£GwwwjÃ3!MZJƒ»»{«²øÂS²£££‰••QRR"VVV$::šfT… wó>ï•––FJJJHxx8%gΜ9$&&†&'99™˜››“ÒÒÒÍ[___âââB8©©©! ÄÁÁ >œ˜˜˜-[¶PqBÈlj««+QWW'ÆÆÆÄ××—‹ð•þÇ™3gˆ±±1=z4ùí·ßH@@�µøýû÷ÄËË‹hjj’sçÎñÝBHEEñ÷÷'ãÇ'fffdÏž=Ô&f.\ã)Í �$::ºMéí „•—½{÷¶øí¹eÿúõë�)((à“]]]MØl6_>«G„ÐË (õ*??Ÿ“ÌÌLB!ÞÞÞ|~ùÒWPP@�{÷îQnÆÆÆÄÞÞžüüóÏdäÈ‘ÄÅÅ…<yò„/ì™3gHÏž=ie–kdÁÓÓ“˜™™âççGÛPÎk†·¾MMMòÛo¿‘‘#G’yóæ 4H"(~B©©©!6l ***dÉ’%¤¡¡AàÆöääd€2rP__O<HÌÍÍÉØ±cɆ HYYå_Íëx{e6fccCTTTˆ‡‡111!›6m¢ÅÝ·o_ûEEq¶„½xñ"qss#:::dìØ±dÕªU´²\XXHˆµµ5QRR¢Ú¹– �u&ÕÕÕÄßߟ¨««“3f˜˜¢®®N{Ÿ‹/¢®®ÎמŠRö»\#B>¤¹§§§“±cÇ[[[òáÃ>#0}ûö%;vì zzzDOO“êêj*¼IHH Éüã?ˆƒƒQQQ!ãÇ'›7oæË—±cÇ’íÛ··)Ü7È´iÓÈ„ È¥K—(÷³gÏ{{{¢¤¤Df̘A"##©ôƒÇÌÝ»w �òìÙ3Ê-55•� µµµ„¦zE¦NJôôôHHHùùçŸ)#0.\ JJJ4ƒ)·nÝ"îîîdìØ±DEE…¬\¹’¯Ô†???bhhHˆ¹víõ¼¨¨ˆ,^¼˜¨««ÓŒ�>xð€¸¸¸uuu2yòdZÙd¦¹1.ãÇ'{÷î¥î?~LœœœˆŠŠ Y¸p!c†‡/¥%¬,Šb&&&†ï»w†–““qrrârGã¢pþüyXYY‰ì·«ÍT2000t„˜™™AWW—2ldbb‚™3gbùòåBÃúøø ¢¢!!!”[YYˆ7nÐfòÛÂÖ­[‘‘‘ØØØ6ÇÿµÂáp`hhˆeË–ÁÆÆ„Ìž=LJŸŸ_g'Sö?'iiipttă:̸Gii)¾ÿþ{<|ø?ýô“Èáž={Ü¿¿Å-]™àà`¼|ù’¦%! ÿ¦6Œáë"55•2ÖÔIII"ûšŒSñQ�� �IDATþõ©€2000t, Ë–-ÃâÅ‹áääÄw–eK¼y󈋋ûÄ)ìšñjjkk±gÏ(++£ÿþ¸|ù2jkk)•ò{÷îáØ±cÈÉÉéä”2|©´wÿŸ0ZÚÿ×)))˜:uj«ÛDº*çΣq•¯½ c`èh˜ CbffF³`hh(R˜‹/¢wïÞ¶ÒÑÙñjÞ¿ššüç?ÿA}}=¦M›†'NPû4¹û{x÷t20ˆÊ²eËgð1Œ?·oßnÓ òýû÷ˆˆˆ€——W—±6ØVâããÛl•ùkoÃ:F”¡ƒ©­­mó9z !ïÞ½c¾Ãg§3T@¿Êc :f ñeÁb±˜oÆÀÀÀÀð¯�200000000000üK`€ ÿ˜ ÿ„¯r�˜‘‘OOOhiiÁÜÜÑÑѨ¯¯§ùyûö- ¯¯;;;œ8q‚ö¼ªª °±±šš\]]QPPÀ×… °dÉhkkÃÇÇ¥¥¥mJëÇñ믿BKK ÎÎθsçíyQQ6mÚèêê" �ååå"Ëúô)lmm±sçN¾gçÏŸÇܹs¡¬¬ ;;;œ;wN¨¬k×®AKK W®\¡¹ÛÛÛƒÅbñ]ïß¿(çSåmdd¤Àt4ÏÓ/^ÀÛÛZZZ˜?>’““…Êl §OŸ¦½7ï=CÇqèÐ!ØÙÙñÕ뎢¦¦UUU4·­[·ÂÆÆæ“Ä÷o¥  �ýúõÃñãÇ[ôSSSƒ©S§bõêÕ”[}}=.]ºwwwèêêBWWþþþmn>‚êп…Õ«WÃÃÃã³Æ£¥¥…�h2jäêêŠ;v�h:S‘ÅbáæÍ›­Ê<~ü8ÑÐÐ ÔߥK—Àb±PYYù‘oÑ1|êBs8TUU/Ô_[òýsP^^SSS\¼x±³“Òf¾”þÕ—”Ç_Ý�033ÖÖÖPTTD@@�¦OŸWWWìÝ»—òSUU[[[äççÃÛÛúúú°´´Ä¡C‡(?Ë—/GVV¬­­ˆ—/_ÂÂÂÿüóåç?þ�›ÍÆøñã±~ýz<|øl6oÞ¼)­>„‰‰ zõêôêÕ ÆÆÆÔ€¥²²cÆŒACCÜÜÜàææ†„„¬\¹G¨ìÆÆF=zÚÚÚ€>uê.\CCCìÚµ #GŽ„±±±ÀB[UU___èéé!;;›ïyEE¼¼¼píÚ5Ú%...0mŸ*okjj`llÌ—999�Mç™šš¢±±ÐÑÑ……bbb„æ%ÿÀÀ@¬Y³¦³“Ñ%!„tˆœ÷ïßcÕªU¨¨¨ê/$$.\ ¹UWWcãÆPVVÆúõë±hÑ"œ;w?ÿü3êêê:$} Ç¿µq8$''CWW·Sãio=ÍÌÌ„••U‡žgøµQXXˆ¼¼<¨««wvRÚLGµß -ó¥äñWWÃÕÕÕ‘žžŽï¿ÿžr«¨¨@DD–.] ‹…S§Náõë×8vìeùMBB6l€¥¥%z÷�ÈÊÊRçèhjjBFFééé077Gmm-Ö¬YƒßÿÖÖÖ��===hkk#99­¦5,, l6ëׯ‹Å‚‘‘Êˈßÿ}úôAff&†J…0`�ŒŒŒ°iÓ&j`#ˆwïÞáÈ‘#8~ü8mæœKll,±xñb�€±±1þþûoœ?S¦L¡ù-,,DAA®\¹}}}>YåååPSSù‡÷©ò¶¶¶ -¦#99bbbðó󃸸8Œ!))‰uëÖÁ‚:Œ¡ë3oÞ<Ì›7¯³“ñ¯ÄÓÓl6ºººuÎØž={ðí·ß õsýúuìß¿4÷þýûãâÅ‹´øutt0jÔ(äççcôèÑíNÃÇPTT„ÜÜ\¨©©uZ<, »víj³LB’““áããÓIü¬|ÎÂýû÷¡  €üñ³Ä×QHIIáÌ™3Œ¯š/)¿º@ Úà�ŒŠŠ jT~ðàAÌ›7föÛÄÄ?Æ­[·��C† ¡u.ú÷ï�ÔìrVVòòòh=÷ë×8|øp«é|ýú5víÚKKK*111°ÙlìÙ³eee�@ü€´´4�àÇBå÷îÝIII˜0aÄÄø?³¤¤$nß¾MÉáp8¸ÿ>äååùüŽ9€’’’À¸^½z%Ô„zJJ ,--©¾ŽÊ[???¸¹¹QßµººZè ®  �Æ £­LáéÓ§ÈÊÊj1î ÚÎÎÊÊʰ´´äSn÷ïßã÷߇¹¹9ôõõL©I8p�ýúõCuu5å? �ªªª4µßððpÌž=»Mñ~nLLLpðàAêpmܾ}�pçÎ>Õ\‡}}}DGG£¬¬ ýúõCJJ ìíí¡¦¦†U«VáåË—”ÿ={öÀÀÀ ]ñ×ÔÔ@^^žR—ââççGGGøúúbÓ¦MصkX,M•«´´þþþ7nŒŒŒK›íö}&5Ò 6Àßߺººeð"ŠÌõë×cïÞ½˜4i\]]4uæŽ;6› ---lܸ ,ÀÉ“'[ŒK~üñG˜››ÃÎÎíšíÌÉÉA``     ÐÏ›7oàââ‚°°0¨ªªò=ç|rUúõë׿ô0t,ÂêУGàêê UUULŸ>üñ€¦rìç燠  èééaúôé8vìØ3›Î%77—p8°X,Z{òä X,>| iuóæÍÐ×ׇ®®.Ö¬Yƒ¢¢"�-×mÞx1þ|ÒÜâââ0mÚ4hkkÃ××—O£æùóç¸{÷.mPY^^???èêêBOO4 ¤¨¨(L›6 ZZZ BMM õÌÄㆠ ¡¡AiY•••aÆ ;v,fΜ‰ØØXšLìÙ³...PSSÃÏ?ÿŒ¬¬,ܸqŽŽŽPUUÅŠ+PXXH…iþO%ß/]º„Ù³g#22¦¦¦ÐÕÕÅÖ­[ñòåKìܹFFF000@LL ŸÆï*i}}=¢¢¢`aa555xzzÒÔÑÏž= ;;;Œ1nnn(..¦ž k¿Ïœ9ƒùóçCCC«V­¢ÊEóp›6m¸qã`aa„„<}ú«W¯†¶¶6Øl6®_¿N…©©©‹Å¢4*Ž=Ê×¶6W+æþ‹`gg555xxx °°ñññ°±±††üýý?ZÝûKë_¥¥¥aþüùPVV¦õMyóXÔrÖ|u@AܼyÓ§O‡˜˜qíÚ5¾Õ Aƒ�€ÖÉlΣG��Ç�”””@FF  ùSTTÄÍ›7ÑØØ(4MÜÆAFF†æþÃ?��M²9¹¹¹PRRÂ!C„Ê pàÇÅÙÙ999°²²BJJ ÜÝÝ1`À�ØÚÚ¶I‡ÃÁãDZfÍÈËËá¡¡4µ®gÏžáÚµk-6íÉ[Bîܹƒììlj¯Buu5âââ ¬¬ŒI“&ÁÇLJöƒ<x0222héàv.Zúî¼ypëÖ-˜˜˜ 44°´´äÛÙ„xyy!55îîîðòò±cǰtéRp8Lš4 ÔÀ¨¡¡111ÈËË£©Þ&%%Íf‹gg²zõjŒ5 !!!••…žž  ¦¦CCCœ?žò›——‡«W¯ÂÈÈ@Óª}PPæÌ™ƒíÛ·ãùóç˜5kÞ½{÷Ññ÷êÕ Ë–-Ñ#G¨ï_WW‡½{÷ÂÚÚ ,€££#ìììp÷î]¸¸¸P2322гgOaÞ¼y°µµ¥úÖ¾/—ÐÐP|ûí· ä“Á‹¨2}}}qåʸ¸¸`Μ9�€èèh¬Y³¶¶¶ Âëׯù½íaÅŠÈÏχ––Œ±`Ájp- ÕÕÕpssî]»ZlÇ!€±±±Ðo_¾|‰«W¯âÈ‘#pvvFxx¸ÀI,†ÏKKuèÑ£GÐ×ׇ’’"""0{öltëÖ ·víZ444ÀÏϰ··GRRRg½F»ÈÊÊj“ ¥‡‡îÞ½‹M›6ÁÏÏC‡¥Md ªÛí‰�þúë/xxx`Æ ¸téæÎKÛ3Ç»²U[[ +++¼~ý~~~pwwG=hý§OŸÂËË 7nDLL ßÊãüùóÁáp°nÝ:Œ3UUU˜>}:êêê°eËXYYÁÕÕ»w礪 ‡žžÂÃÃ!--MÙ˜>}:ÂÃÃQUU…… ~Ô°ÄÄDÀËË ^^^ؽ{7dddPWW‡µk×bÉ’%Xºt)mE‡‚””Œ?žróññAbb"œœœ)))ôèуzž‘‘{{{DDDàÕ«WXºt)m¥ o OOOÌš5 ÁÁÁ(//Ç”)S¨Å I‹LJJ ÁÁÁ055Åœ9s`hheeelß¾033ógÏÚG8zô(°sçN<~üòòò¸pá~ùåøûûãøñãØºuk»ã�¾¬þUvv6¦L™###8p�–––Ô⌠D)g‚““ÉÍÍå»Þ¼y#òODºÎœ9C>'YYY�ÉÎÎ&„R^^N�LÇO?ýDÂÃÃùÜÉ¢E‹ÈâÅ‹)·¢££Ãç÷ĉ�©¨¨š®«W¯�¤¨¨ˆæþäɀܸqƒ/Lyy9>|8‰ŠŠ*›—É“'“;vÐÜÉ–-[�êºxñ¢P9%%%�ùóÏ?)·>Ý»w“'N .ÈÈH¢¢¢BØl6©««#„ÂápHee¥@™“·>| ïÞ½£ž'&&’„„rñâEOÌÌ̈‚‚•Ç………dÀ€dáÂ…äܹsäèÑ£dòäÉ� úî-1oÞ<²yófB!)))�õÞ¼÷ÙÙÙ¤gÏž¤¤¤„ ŸŸŸO�ëׯB±°° äÝ¿Ÿ())‘åË—ê�çÏŸ·+½Ÿ cccFÝs8beeEÖ­[G!äСCdäÈ‘äÇ„B¶oßN–/_N!¤´´”–'„RVVF @Nž<I!ä÷ß'“'Onwü¹¹¹�yôè!„Ë—/RSSC!ÄÇLJ¸¸¸Ðd“9sæÐÜ/^LÖ¬YCíûkkëeð"ªÌ©S§RyI!ÕÕÕDFF†¤¦¦Rnuuu�INNn1ßÚJII Ù²e éÛ·/ùå—_Èû÷ï[ ãëëKV¬XA !„(((?þøƒæçôéÓd̘1äÍ›7„B¼½½‰··7Ÿ¬äädZ–@8N¼ÃÇ"¨999‘M›6 ôLØl6ÍmÇŽdôèÑ_Ì7åp8DGG‡ÄÇÇBšþo¼uþ"�H~~>©¯¯'={ö$(OPÝ!„hjj’ýû÷S÷ÍÿMÜ6µy¿‚û/9wîåæëëK<<<¨û˜˜b``@êëëùÒ–––Æ××Ù¿?>|8õ½Œù¾wTT™6mUÿ !$>>žôíÛ—ªïÆÆÆ´¾Ø?ÿü×VnžŸŸO¡ÿZË÷–Ò¿}ûv¢§§GK¯³³3-OŠŠŠhrîÝ»'°/Gˆà|¿}û6@ž<yBü«ªªˆœœ¹páåV[[K455©ÿ𠉹¹9  î?|ø@H•“êêj€œ?žBÈ‘#GˆŠŠ M†··7qwwo1ý—/_&�È‹/(·Ã‡YYYÚ7íºjÿêСCDNNŽê/4‡7E-ggΜyߦ1š 1ž““ùªW�‹‹‹áèèhhh�hR@[šV²Š‹‹ŽâÃÃÑ––FÓ‹ïß¿?ž={ƧžRSSƒRñ //ºž?�Ô _mm--<w…ƒ«É¥®®žžž1bÍá‹/hò<x RÞáôéÓÈÊÊBaa!‚ƒƒ©™µ¶ ..Ž¥K—bÆŒ˜2e /^Œ˜˜$&&"33@“ªVK{}>&oÅÅÅ!))I=Ÿ5kØl6 0{öl>|8vì€&uÚ‹/‚ÃáÀÓÓééé À¿Û/^¼ÀîÝ»akk‹qãÆ!))‰¦ò"ŒGa̘1Ôj3�(++cøðáxüø1�ÀÆÆqqqàp8ÈÌÌÄܹsaii‰ØØXÔ××Sª'¢¬�w6ÍUôX, (ë©S§"77999”ú ¯ÚEóðß}÷ôôô¨|úØøUTT0mÚ4¤¥¥hRS^¾|¹PUf^™@S¹áªˆò}Ñ\/¢Ê”’’¢©6¡¤¤***”[ó•–Öh©Ýâ¥OŸ>PTTDÏž=ñ÷ß·ª®wåÊ>|ëׯoQ«àåË—pqqÁ®]»ZUçœ>}:êëëQRR‚}ûöaöìÙ”J!CׂÃá )) ššš-úá-£cÆŒANNN›,_w&ÿý7222DÞÿ÷Í7ß 00Ë—/‡³³3Nž<É÷?á­Ûí‰GC‡ÅÔ©S) BN:E[ÙÊÎÎÆÄ‰E^e”’’B~~>MŠW“çöíÛ˜4i­þ3BÛ�4OܶAÔÿ¯(|÷Ýw|emРAxýú5uÏ»JšŸŸuuuÈÊÊŠ7ÝÍû ‚ÚïÂÂBZûÝ£G˜™™ñYŠoެ¬,­O)..…µÔÊí›6߆$%%…âââ¶ÈÙÕúW-ýõõõ1xð`L:!!!ÈÏÏoÓ{ *gÁW;�üçŸààà€aÆÑöˆ‹‹câĉxòä ÍYYjkkù111pqqÁ¾}ûhFW „ÒÒR>³ãEEE?~<Õ¸9;;CUU•ºBCCüo/_s]pઈ¤Üêëëáíík×®!$$„¦ZpäÈšüÉ“'·š7¯^½Âo¿ý†•+WBKK C‡…§§'ÂÃñbÅŠ.˜ÜF«5µÊÍÛÖèÛ·/ôôôhj êêêØ¿?îܹƒmÛ¶QƒIQTåå唑///ÄÅŵéX€ºº: ûôéƒîÝ»�&Nœˆììl 55“&M‚¶¶6=z„üü|œ;wî‹PÿD·nݨ‰€båÊ•8{ö,rrrP[[Ûª¡o¾ù†6àÿ˜øY,.\ˆƒâíÛ·ˆŒŒ„™™Y›e6Šò}[“ÁK{erÕ‹Z³Ü-µ[\*++qèÐ!Œ?QQQ8tèNœ8 ‰eÖ××ÃÙÙùùù8p uLËÓ§O1sæLÊ\úŽ;ðøñcŒ?žò€€€�°X,DDDÐä~óÍ74h.\ˆ•+W2V}»(ïß¿GIII›öôqÛzae½+‘——%%%¾}yÂÞÙÙÙ™*ïûö탦¦&î߿߮xÚJÏž=©¼-))Azz:ͲåëׯÛô½D1 U[[ËwÄw€Ù’åpArE‰«-i5ž[·naÖ¬YTš+++ÛÔΊ’nîàŠ7Ÿ$$$hý?^õZë/µ÷Ñœ1Æ¥+ö¯Zú2—/_Fpp0Þ¾}‹)S¦ÀÛÛ[äòÖùÕ|•ÀÊÊJ8::BBBááá|DGGÓö‚]¾|êêêÐÒÒ¢ÜNž< {{{ÄÇÇó ¬ttt ''‡³gÏRnµµµ8zô(æÎK¹¥¦¦‚B][¶lÐ4ȱ··Gbb"U !8yò$V®\IͲp8 !!‰‰‰|³Lžžž4ù¯^½j5¸³s¼ wÖ–stxî,Isã¼³8‘· ´'Þ†¬®®999´Y4Þ´9r3fÌ Vˆ…ñàÁäääÀÁÁššš:th›:&JJJÈÈÈÀÓ§O)·/^ ##ƒÚÿ(++ $%%áøñãÐÒÒ‚””/^ŒS§NáèÑ£ÐÓÓ9Î΄·,^¿~ÚÚÚÔ=›ÍÆÞ½{‘€åË—ó îšÏºVTTàúõëT>ñÂ[D‰ßÐÐÙÙÙØ±c´µµiå„Åbµù8Q¾¯(ÔÖÖRõª½2eeeÑ·o_ÚÞ†¶t†Zj·€&#Z£GF\\vî܉'NÀÀÀ ÕÆnݺ!::999´KFFÛ·oGNN†ŽE‹ñùY´hå>cÆ 466 ì¸tïÞoÅ¡sà­C=zôÀäÉ“%Ä¥´´”¶²pïÞ=˜˜˜´j-¶« KKKjp &&†‘#G¶:©*##ƒyóæ!..C‡ÅéÓ§Û¨4_™+//ǵk× ¬¬ àƒJEEEÊψ#ð矶z&`[PWWÇ™3ghíu^^äääøl3´Qó½=œ>}š¶J*''‡ÜÜ\¾ÉüaÈ!èÙ³'­®444àòåË"õUD¥wïÞxþüy—8K¯+ö¯„ýÅÅÅ¡««‹uëÖaÿþýؼy3mæ—ÀWw D]]\]]‘””„„„J½hRy<x0¬¬¬°wï^,]º(--…‡‡"##©Nè•+W`cc6› YYYÚAžãÆCŸ>}àëë‹eË–áÝ»wPPP@ll,¤¥¥ajj*RZÝÝÝadd„þýûcòäÉHOODZcÇššJù Åúõë€êêj*}ûöň#„Ê¿uë8*++QXXˆÌÌL|÷Ýw——‡››ÜÝÝQZZ !** |VTkjj——GuÈ<x�III(**¢  �Û·o‡••¾ÿþ{”——#44+V¬ ªÝ»wcýúõHOO‡¼¼|‡ä-!öööxõêRSSQSS+++Ì›7òòòøðáЭ[7Ìš5 @“êÉÙ³g¡©©‰úúzœ?ÉÉÉHHHéGÊ5гk×.!//„§§'€ÿ©§§§CUU•ï~ìØ±˜3g~ùåüúë¯Ghh(ÜÜÜ0jÔ(*6› kkk,Y²}ûö�˜ššböìÙ˜1c†Ðã?º7nDŸ>} ££ƒ«W¯âêÕ«¦žëèèàÛo¿Å–-[——ÇÞÅÅ••ÅÁƒ¡««‹ &ðùã- ÜYäÖâ0`�V¬X7âèÑ£´Y¹aÆaÏž=¸zõ*zôèA›j Q¿¯0^¿~ mmm,X°�ëÖ­k·Ì~ýúÁ××NNN¨©©á›Pù²³³}}}‘Wã¦N™ 4KJJBAAz&Hí“Û&qýܺu «V­‚äää ..ŽÛ·o#44”Rëeè\Õ!ØÚÚbðàÁÐÔÔÄ“'OгgOª]¿téV­Z…™3gâåË—ðññABBB'¿‰è¤¦¦ÂÙÙ™æ6oÞ<„„„`РA””¤ÞØØoooèééaðàÁ(++ÃíÛ·iÖ>EGFFwïÞŃZì¬]»^^^””Äž={`llLµ©YYY|ƒJkkkøúúbýúõ˜5k^¿~§OŸbéÒ¥mÊ—æX[[cëÖ­ððð€µµ5Þ¼yƒ5kÖ 00½zõj·\^„å{{áj~®ô„ 0yòdªO%&&†ŒŒ ꈭö %%¬X±µµµ••Err2ªªª¨þLG ¡¡=z 00FFFxòä annÞaqˆÊ—Ô¿:þ<>|uuuHHHàìÙ³ÐÑÑAÿþý[µÐß•øê€yyy”¥;Þ=Eqqq˜3g¤¤¤””„øøø@UUG¥Y›Û»w/jkk‘˜˜ˆÄÄDšîLºƒƒúôéƒØØXìß¿fff y¶RCCçÎCdd$~ýõW 55•š‘{÷îÜÜÜ��ÞÞÞ´°îîîØ¶m›PùsæÌ¡fC²³³±}ûvÃÓÓÐÒÒBRRîÝ»555,Z´ˆfeŒË«W¯ ££CÝsÿ´´4 >“'OF\\îÝ» ,Y²l6›êÊÊÊB[[›jÜ;"oY,TTT --o¾ùÝ»w‡³³3Nœ8­[·BNN††† ¤VS{õê…/^àðáÃ4hôôôpéÒ%ªái yyyœ9sÛ¶mÉ'0mÚ4øøøP:÷cÇŽ…——ÜÝ݈‰'Òî§NŠÈÈH„……aãÆèß¿?¬¬¬`ooO‹‡{Ö¢‰‰ å6nÜ8ê›~)ØÛÛãîÝ»ˆŠŠ‚ÒÒÒhúùÝ»wÇœ9s0dÈ«Y³fÍ¡C‡ðÏ?ÿÀÒÒ[·n8Pç- ¢Æ4­†……ñ}9kÖ,üõ×_Xºt)tuuù,Ô â›o¾éû CRRšššÔêùÇÈtrrBŸ>}°ÿ~TUUaæÌ™�ZV³•mÛ¶uº Ë?þˆåË—ãÌ™3 C¯^½0aÂddd<2‚áó’P¶^�� ÊIDAT#¨MŸ>jÓ7oÞŒ1cÆ`Á‚TMMMôêÕ‹ê´%$$<{¶+òêÕ+\ºt ÿýïiî...”…B999ØØØP– ?|ø€üûöíÃÝ»wñÓO?açÎÿßÞ½»Fµµ~½qT‚¢¢)Ri±²+ñ IŒØYXIˆ‰·¿ÀÒBT$…Ö^Šh1ŠA¼VF´3Š " 6â|…èç%ÉIrf²gò>OfÖ¬Å@’ù±öì5îð±æ9zôh:t(FFFâüùó[¼xq´··Ç±cÇ""¢½½=:;;þŸ¾~ýztwwÿ6fåÊ•qÿþýèéé‰={öĪU«¢­­í?]6ØØØCCCqúôé8pà@¬]»6N:õÛÿ»Jï}ŸªÑvIçÏŸ½½½ÑÓÓ‡Ž Ä–-[þstwwDzeËââÅ‹ñþýûرcG Vt7¼©©)ãøñãÑ××Û·o}ûöÅëׯ+6ÇDÕÓç«¥K—Æ‹/âÂ… ñéӧغukôööƼyóê*�guuu•»ººþz`¢Š#¾×ð®]»&üÜJÿ¢µeÛ¶mÑÞÞû÷ïó9_¿~7Æ‘#G~;~äÝ»w±bÅŠ¸wïÞÏ?ÌÕ˜?âûí»?~ü===Sš§žŒŒŒDSSS ÿëÕ0ÝNž<>ŒK—.½”)ŠŽŽŽxúôé¤/Í,zž·oßFccc<{ö,š››+òš3щ'âÍ›7¿]I•píÚµq<úUÿ„Ÿñý¦Q:sæÌÌÛêãGâÁƒ…‚úáÇ8{öl\¾|¹ù«íùóçqûöíhnnŽ9sæÄ¹sçâàÁƒ“ú>"01Sý^^-Ì3ÚλqãFttt½ ¨âÊ•+ÑÑÑñ×wN§Ë­[·¢¡¡aÊ»Œµ®\.Ç“'O¢§§'šššb×®]±wïÞÂ/ß„™¨³³sRG­ÔÒ<ëׯÇW=^ë]__߸wá„zâ·¨¸_od4–±.£Y¾|ù¤oß=•ùwïÞ]·GjLÄêÕ«ãôéÓE/&äÇÍêU%o`2ÝóüóÏ?ãáÂw?Î#„™`F��Àß ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ��„���HB���$!���’€���I@��€$ ��@�� ‰¹ELzóæÍIùòåK,Y²¤ «��êÝû÷ïcáÂ…Ó>ïìÙ³ãóçÏÑÐÐ0ísOE!¸iÓ¦I¹{÷nlذ¡ «��êÝÐÐP¬Y³¦¹_¾|9¥V¹~ýzV3¾B°\.OiÌTÆ��TS=µJÍàÀÀ@ìܹsÒã���Šòg«Œ×5Eª©�ˆR©mmm“��P¤Ñ°\.×\€¿A¥R)F}lÖ¬YcŽ��rûµŠôk«”ËåŸ}3ÚWQk.lð×7§T*E©Túíñ±Æ@�� –Œ×*?vǺÊqºÕD�ŽöóDÆ��Ô‚ñš¦–:¦&°µµ5"âç. ���êÉX­ÒÖÖ­­­5Ó15€---Q.—ãêÕ«.��êÆX­ÒÚÚú³sjEÍ`Äÿ#Ð ��POþl•–––š‹¿ˆ Àˆ÷M€��@--�k±]j.�ÿmL-¾‰��@±Êår|ûö­°¹ë¥U ;pªç^ÔÊ��@í(2�¨‡V)$�ïܹ3­ã��€™oxx¸°¹ë¥U¦=�7oÞ<ÝS��³‹^����ÓC���$!���’€���I@��€$ ��@�� ��DÅ‚ïïï¯ÔK��P ÀuëÖUâe���¨¢Šà¢E‹*ñ2���T‘ï���$!���’€���I@��€$ ��@�� ��„���HB���$!���’˜;Ö¯^½šÎu���Pev���’€���I@��€$ ��@s#"Μ9Sô:���¨²ÿ]£ªS!8«����IEND®B`‚�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/screenshots/opensnitch-ui-proc-details.png�����������������������������������������0000664�0000000�0000000�00000225106�15003540030�0024114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR��i��G���#b ×���sBIT|dˆ���tEXtSoftware�mate-screenshotÈ–ðJ�� �IDATxœìÝwxTUþÇñ÷LfRH'@„^Bh¡÷Žt¤(*mEtꬻ¶Ÿ²* Ѝˆ² °)"Ò»€t 5¡‡„„6™ùý )$’ÏëyæÜ{O½÷Î$ß9ç\·5j”íÖm""""""""’¿¦M›f¸ùgSÆFm ¦^½z¸ºº>øš‰ˆˆˆˆˆˆˆ Ô®]ÛvèÐ!¦Nj�0À�Mÿþýñöö.ØŠˆˆˆˆˆˆˆ!W¯^eîܹL:Õ`V€FDDDDDDDäóôô¤fÍš�8Œ5ÊÖ¹sg ¸Z""""""""E··7NNNÿ2ZƒFDDDDDDDîYzz:)))¤¦¦’žžŽÍfÃh4b4qttÄÙ٣ѨònáááÜ´p°ˆˆˆˆˆˆˆÈݰÙl$%%‘œœŒÕjµoƒ «ÕŠÅb!99'''Š+†Á`È-Ë"Yž‚4"""""""r×l6×®]#55›ÍF‰%(Q¢nnn˜L&, ×®]ãÒ¥KÄÄÄØnnnwÈx˜ËSFDDDDDDDîŠÍfãúõ뤥¥a6›©Zµ*ÅŠ³ï·Z­F<<<ðððÀÏÏcÇŽ‘––Fbb"®®®wÈxØËË¿‰Y"""""""R¤¤¦¦’––†Éd"((ggg¬VkŽ/jÔ¨ƒƒ‹…ÔÔT•w“;ÒØl6ÒÒÒHHHàòåË\¾|™„„ÒÒÒìó±DDD$?ØH&:.}Â>lòãÜêú‘‚e³Ù°X,X,*V¬ˆÁ` ==ý¶/£ÑHÅŠ±X,öÅwUÞ w<ÝÉb±˜˜ˆ»»;U«V **Š„„\]]1›Íwš¥ˆˆHþ²Äºv9žäÌÙ+X=ü)[¾&-ºu¢nÉø9•CئU¬þ-œ³—âH1¹ããWà6=éU¿Ôm¿)±]ÝÆŒÍ!ÔÄ“ÿ~žfžžšLZº&gGLy9kÙÃÌq_±·dW^{³es+ÔǦÏþÁÜp+Å‚ŸäÍçšQÜ~¼…ߎç‹Ý>t{ý º•¹míï¼®ùéNÚ y8WwÛžìÓe=·¹e˜yˆˆˆä?«ÕJzz:899ÙÔÍ ggg<<<HNN&==“éöá‰ÂRÞ7ß|CåÊ•iÕª•ýØM›6Á°aÃîº<¸‹‘4ÉÉɸ¹¹Œ——^^^ãææFrròf'""’¯l‰á,ô>S—nçÈE>åËá™vŽ°Ë™ñþD~<p•¼œßëUöþð Sÿ·•c16¼ËW¢Œ‡+'ræºcž²08úPÚOÿ2ø8Àv•­ÓÆ3vüçl‰»Ÿ­°qýðO,Ù-Ó;­ñÀêšòr®î¶=9¤Ërnïw"""÷AÆL//¯L#I¶mÛFttt¦mgÏžeÛ¶m™¶yyy‘––vÏåe¼’’’øä“OHJJÊv~•W£F ¾ùæÖ¬YCzz:6l°nî¥<¸‹‘4)))”/_‹Å’i»¯¯/ááḻ»ßi–"""ù$‰Ã‹¿ã—×)ò£7Å× ë5Žýü3Veãÿ£ê[C©çzÿ¨µÛÊÊ߯€ÿ#¼üÚ£”ÿc�Í’L*Îyû–Ä¥:½_ý?zÛ3½?uÍÂ`ÀH<¿/ZN³êOPÝå•[@òå\Ý©[ÏmAå!""rÒÒÒìë´ÀÀÆï¿ÿμyó?~<~~~\¸p‰'R¥J4h`_H×ÉÉ ‹ÅrG ëÞZ^†ôôt¦L™Bhh(“'Oæïÿ;™ŽÉ¯òZ´hÙlfêÔ©;vŒß~û‘#GÒ²eK’’’»zºSÆó¿oÝ&""Rl‰Ù²;\êÐýñ&øeÌl2ºQ¥ë@ {EÇ÷±uuEðõ¸YÄux’Ú—7°)ôi¨ßùqmä‡#`»v‚ ‹—°%ô W,n”®Ù’Þ}ÛSÙÝøÇtš¯9Ûì Z[gýþS\5– ¨MtªŒóµx¬�éXÒm`¾ñál09ã”QáÛäá–~ˆï_›ÆvSKþþN.|þ憧Gùñç™_¢=/ý«•€Ô ì]¹„Õû"9Ÿ`¦D™J4ëó4mþè›ä3lûa¡O“`*EPûþ h_‘lcUÕhÑô*[·leáꦼÚ+3pëtêûÇ-þiS·Ôõ­–D|ôKN{ÓáåÑ·¢$íåÛÏfZyz½þmK±%î`ú³ -Û—kO Ëö,_Äê}ǹà€wÙš´x´7m+ºa°ìa渙o2„Ç]w³b{$ þ]ya”wæº&cÑG“Y{Þ‹¦Ï¾Ê“µÜÈhzúíΕ}XÖ¾¯p5”_W®cOø.^MÁìéOÕÆÝèÝ%cÎé*Ùn:·ï š)•è?±ä×½œ¼”@z±øÔ§×Ðæ\˜‘×<r¹­Ùç?¬Õò œˆˆÜ?F£Ñ¾ÞJzzº}û¸qãøì³ÏøðÃ2dßÿ=•*Ub̘1$&&Ú3 X­Ö<1r*/==3fpæÌ&L˜À”)S˜2e Ï=÷\¦iFùU^bb" 6¤]»v¬Y³†V­ZѼys®]»–)ý–w1ÝÉÉɉèèhûâ9¯èèhœœœnŸˆˆÈ}’uœ“©`ªX›Zî·|JÒ aEŒ6 Q'£¹ñ1›FäªïXsÎòUJãpùëgMeQX X¢ùåóI,؃[PKÚÔñàò®ÅLŸ½ƒ+öï%¬œÛ4—ŸŽÚ(èO±Ähö,ÅòðTªQÕÝ€õìZ&ÿk"3æÿÊïÇãHË2&ç<2s¦\£¶Ôô1€¡85Ûv¦k›êx�Û%6MŸÈW«c¤VP�N×’±¹üùí‘-ö¿ßò~8];ÃeEDO08R±óc4ó±rvÝ6\ÈhðM•ϵr¨«±uêà`‹%âØel€åt8Çâ“II:NXD6 -ò'ÒŒ”«[ÃE6~1‘™kB‰w«LÍÊ®$Dlgá§Ÿ±ü”Å^¯¸-ß1sýðñÅÛß—’7Ÿ~["‡ÍfÝ9þíÓ/øÏ� ‡s•Kßs‰ˆ°Ë8øU£^ÝJx¦E³gåLfm¸€-×t™Y"—ñåwëOõ¥nËö4.{ãQžÎyÏ#·ë çü³¿DDDòÂh4b0HMMÍ4ÍçêÕ«¼øâ‹TªT‰)S¦ÈË/¿LBBB¦ãRSS1 y MdWžÅb᫯¾âäÉ“¼ÿþû”/_žwÞy‡¨¨(fΜi_¼7¿ÊËx­^½šuëÖѶm[¶lÙŠ+²s§åÁ]Œ¤qvv&!!°°0J•*ÀÅ‹IMMÕT')P¶¤k$Å\ɺD‡7OLHNNùs«_GžõQM6b·Mã³cÃ^:&‡òë©t|;=à b²]£äÕ7ø!ì7Æ7¡µÛô†â­ùæãT5ÃÅ_ÿÃÛ‹Nq$ô¶êué?¢é߯`ïÅ“ì]’½–â^¡‡õ£®ÏŸÖ9åa­|Sõ ΔoÚžÚ;×s8®ÁzÐæU}-ëYž„c•~¼üb;JÝ<²÷8†¡D[ž{ó1ª˜­œ[õ!ï-"<ìÖ*e³ýÆÆàRž}ppÆ.V-ÜAýçeÚŸ|è—œû'¡ ­s¨«­N=–Ÿ!úX$×:•">2’wo<ã8qì–&58A¢±,j• =b«$áT}�/½ÐCgV|ÄÄgؼñþQ!—êôÿ,m2†OYöü±ÃÊÕ=?2gk Žz1´Ge\nß¹Ýî\åÜ÷oÍóïµþó¼ø+ÿy{'ájÛÖ9§Ë4�ÙFê¥ ÄÙ ˜-š¶mF…âN’ò”X"sºl\?žKþ"""wÉh4âààÀõë×qssË´/..Ž—^z‰É“'óâ‹/›å)G‰‰‰˜L&ŒFcžfçäT^‹-6l&“‰ëׯc2™x÷Ýw Çf³ÙËͯò¶oßÎÿû_FŽI«V­¨U«S§NÅf³Ñ²eË»nÜEÆd2áîîNrr2§OŸnŒ®qwwÏójÅ"""÷ƒÁÉ'l$%ÄsÝ™þ µ‘|ý:é6pw-†ÃQ fgœ� xס¼Ã!Â/œçÌ… ¤Ø¬œÿå^øå¦lÒH½yý73&‡÷-…Ùp’Ô´lp­ÔgþÙ‚KÇö±kçoü¶'œKÇ7ðÝ÷¾”y±5%n›GnÍø•«§Ng3R®f0%r8ÜÞN#%ý|1Ζ––KFÜj÷¦g­Ã|ð'–ì¯A=ûÑ6âÎç±2Õ ~µ©°œ3'r<5ˆøcp­ý8mÎÍã§cáœIóæèÑ8ŒÍ©UÊFÜÁS\µ)„·ÀL™zu)ýói¢/œçªµøŒÝ,•õÉ]¶„½,üñ"q†�ºõï@™lseÈtøM¬ÄÙÀ¯›ö~ú2q ×H²‚15•Ô[£ 9FE 8WoBƒGر>ÿ9°·2uhÓµêøâ”§<r»n“¿¢5""r\]]‰ÅÅ%ëüÙ¸¸8ÆÇåË—³ RÄÅÅQ¬X±{.¯N:¤¦¦’’r㋸ôôtÌf3uêÔÉ4½*¿Ê;}ú4£G¦^½z\ºt‰ºuë2zôhöíÛ—iZÔÝ”wÇQƒÁ€ÙlÆl6k䌈ˆ*þå(c2päÄïì¿ÒŒ–7VÁÏ¡ý'H78Q®BiŒÍšÑxcT‰ƒ³Ù0P¼éF¶+ýçh£3Þ>Æ,£€?Ü…LÓ‚ŒÎ”¬Ö„®ÕšÐ±ý*>ýà'N?ÂÉ´Ö”È. ’]¹2`ùã©éÖÜÓØÿ7ò6ŠÂèE“~ÝØqt¿/ZN1ß?ƒ4fÇÛôOŽ•ð§vV,äXäqâO›©Ò*„ÚÞY¾2’c‘Þ„ŸÿÎuð7¸øÇ/:Ö›;ÜdÂáæíµÊ¦¸be¨àv•‹Çϱ{GÊVÃ9§Æçv®rx WZÄb>û|-W˶¢Ïã½(_<Š¥ÎçHÎ=}Ñ^!<ýÏÊ´>ð»÷dïÞ=¬˜qŠÄ±ÿäñÊ·O»ë ×ü«<ÀÇÒ‹ˆÈCÅf³áááÁéÓ§¹~ý:ÎΙçÑZ­VΟ?ŸmÚääd)W®\¦ÀÆÝ”—娔”{Ð&¿Ë9r$ÉÉÉ\½z€øøxêÔ©CãÆ¹téÒ]—w±&ˆˆHaeð¬C“ Hgå9“”ñÇj*ç¶ÏgÙÁ$ðn@«Ú®IbM¼BlŠ °‘x4”S銕 ¤|` ^Fq'N“Vª eÊüñ*]×¼|zÚâˆ>CòM±£Ù|ãƒ×¹.wõ 쀃ƒH#--£<J•Äl°rîà.äýw€<1”lÉãqˆÙΦЌ5`Œx•+‡—!·þÉ®®7ê[ºn]ü±„¯ÛJ„¥<Õ*¹àW#ˆ¶(ö¯ÚÃI›/µkûaÀ€·Ÿ/N+Qû÷q)ÀÆ•°PÎZ x—+‡ÇíúÑÁ—ÖºRÉÉÊÅMsYžœ5˜“§s•]{lÄDå²ÅHÙ†i^» e¼o €åÔ·°¦“nô 0¤#}‡ŽeÔ#m±DE]Ú§<nsäš¿ˆˆÈݱÙl J–,Ittt¶k·d÷JMM%**ŠR¥Ja0²Lƒ*ìå?ž¸¸¸LeÄÅÅqþüù{*îòéN"""…’Áƒ†"üÂwl?¸€ÿ¼µ™ò%0ÄáDôU,®éòô£TwƾV‹-f 3>¸Dp98s0œD‡2tíP×2~t¨ºùáë˜þq-BÊàt-š4`Xß`n·Þjú‰µ|ùÑZâ\KàWÊk,Qg.sÝêDÅvm¨jþ³yo_1üK{c;ÉÚÙ?p-¨:7  FsûìeÓ‰¥Lúø õ+:6™ªý‡ÒÊçN;ñV&Ú?NÛÝóëÙtlÄ*µ¢Cµ9÷ONu5‚Á¯.uýV²"4 cà£Tq7`,DuÏ_ىѿ3unD_kv ]Ù½¬ˆ\ʤNPÝ'žcû“Z,ˆN­q î¶-pðoË÷ñŸ¥'Ø<o)u^{œê7 §ÉÓ¹"»öÔÇ»dIœ gˆüùkfÇTÂvü7§ßô-XNýpK-ÇðîQ” ªHIÇŽí:‡Õ¡åË»c4ò”‡K.×A³¸\ò¿ãkBDDäOiiixxx`±X8}ú4~~~¹>T(%%…óçÏS¼xqÜÜÜHNNVy7Ñ粈ˆ<T ÅCxòµWÒ)„JÞiœ‹ˆä’µÕ›÷áÅ7ÇÒ½šk¦?ƒW%ªzÆvð4–Rué6j]ËšÀàK›ç^axÇ:”L9ÂÆËYóû,é)$æaèÍ3˜Ž4¤²·‘øó'8q>·€Ztxr Ïu*ËÝM0q Bç§y´n�DïfËöݱ‚K úyŽî!åp‰9È–-{9•l%=ùN£@9p,O—~Íñ1Þ¼¸Ìíú'‡ºý©]׃Í@‰ªUn<‰ÉTšÕ\1`¤d­:”É8I¦²t5šÞ q¾Æþ£ ¸wbÄ+#i™í|±ì˜h?ÎåMX/nbî’#$Ýô…VÞÎUví±Q,¤7O¶­Š5šý»_®3T»9„—K?üYÒ|,žÄ‰=›X»q?1®UhýäºW0å1r¹Òn“¿ˆˆÈ½INNÆÇÇ???Î;ǹsçˆ'%%‹ÅBJJ ñññœ={–sçÎáïï©©9<i²—g5j”mÔ¨QwUQ‘¿,ËfŽûŠ}þ½xóµÎøßÃâ©6n]Ï5ë–œ·ÞYw_§»?ê¯"?[Sà=“OÈ1›o ˆˆ<Œœ1™LÄÆÆGRR‹“É„‹‹ ^^^/^œ´´´;aRÊ›6mš¦;‰ˆˆÜëߪYÓgŸcnåä5¼Ê[ê‡ë¯ôülM÷L>U —J‰ˆˆä»äädŒF#îîî/^ûš,‹‹ÅBbbbžG]ËSFDDDDDDDò…Õj%999_F®Åò´&ˆˆˆˆˆˆˆH! ‘4""R4™BøÛäé] ;¤)¤)¤)¤)r\88 àAÖCDDDDDDD¤HˆŽŽÎv»FÒˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ Òˆˆˆˆˆˆˆˆ¦üÊ(""‚„„bbbò%?<==©X±b¾ä'"""""""R˜åK&""ƒÁ@ëÖ­ó#;»={öI¥J•ò5_‘Â&_¦;%$$P¯^½üÈ*“âããó=_‘Â&_FÒÄÄÄ`³Ùì?/98‰ù{ß'Ù’xGù8›\y¼Þ<Zkl¦¼EDDDDDDDvù¶&ÍÍAšy{Þá«~Çq0äœ}‹vزnu¦mé6 Ï,¬H¯à1ùU-‘¿„| ÒÀŸšÔôdÜ]½øè“³=îåq¯P¬˜[–}©éÉö| C~VODDDDDDD¤ÐÊב4Á³ƒ6¬¼:þß¿ë·,Û,éi7ÒÞ4*GDDDDDDD¤(Èב4úÖ~…ß• 5=9˾·ÌcuìûlYu Ë>GgúÖ~å~TIDDDDDDD¤P»/#iúÔOŸZã³óølL?Ï:û'7i$ˆˆˆˆˆˆˆ5÷%Hs³Óq¡LÛúN¦bYöYR7§â<Uÿª–lœ_UùË1æWFAš[_S·Œ UƒN´kÜ ~ÕìÇWð«FŸ6é\£?î{7Çô""""""""EÁ}Ic³Y‰I;NŠK1µþs½™ÛpÑvó©¡$¦š‘"í¾,|³A!oóéÖÁøUðàb…cx™�8‘¼èS—9"žW[/¸ßÕ)Ôîët§»·ñ빸¸&G6ijkûNÅ$)1…ý¿ErdC<³Ë2¤ßëlݱQÓDDDDDDD¤Èº¯ÓJø”d¯óròß±Ùl8šqp0‘fI%­\iRI}.•Ô´4Ø« ŒˆˆˆˆˆˆˆYù6’²Ž¦Ù`?W¯ÆqåJ —/]Âd2c0p4;‘’œÂñ㑤$§à€‘óç/hˆˆˆˆˆˆˆY÷u$Íé3§I³¤aµZ‰‹åHøÂŽ„’œœŒÙìH›Ö­0;:ráÂ…,騑¢â¾.|áüEpttÂÓÓ› *ѤI³)ªîëHš .0òÙgî(Ü~yXÝ× ÍôÏ¿¼ã<rûYDDDDDD2{óÍ7yï½÷ º"’òeá`ooo¬V+ƒ!ÛÇhßÍË`0`µZñööÎ*Šˆˆˆˆˆ<”¦M›Æþó CAWEDîQ¾i<== µjòãeµZ ÅÓÓ3?ª("""""òК?>S§NU Fä/._¦;U®\™cÇŽ±gÏbccó#K¼½½ñôô¤råÊù’ŸˆˆˆˆˆÈÃjñâÅôèц®¥#Dþ¢òmMš*UªäWV""""""rÊ•+Ç‚ èÛ·/&“‰Áƒ+P#òt_Á-""""""÷ŸÙl&00Y³f1pà@ÜÜÜèÛ·¯5"1 ÒˆˆˆˆˆˆüÅ9::b6›©R¥ _ý5C‡Åd2Ñ«W/jDþBòeá`)8˜ÍfÌf3U«VeÊ”)¼ôÒKüúë¯]5¹ Òˆˆˆˆˆˆ<2FÓ8::R³fM>üðCFŽÉÖ­[ ºj"’G Òˆˆˆˆˆˆ<24#jêÖ­ËÛo¿ÍÓO?ÍŽ; ºz"’Z“F䊈ˆ !!˜˜˜‚®ÊCÉÇÇOOO*V¬XÐU)šŒ`MHH¯¼ò ƒ bÑ¢EÔ©S§ «("¹PF䉈ˆÀh4Ò¶mÛ‚®ÊCí÷ß'22’J•*tUDDDD¸›4ÿ6nܘ±cÇÒ·o_–,YBpppAWSDroAš3gÎpýúu®]»–_Y>´ÜÜÜpss#  Èô[Qls†Œ¶'$$(@ó�Ô¯_Ÿõë×¹ë¬ µû»¨µ7ÃÍí‘ÂËh4b6›1tîÜ™“'OfÚß»wo~þùg*W®\0‘\åKæÌ™3X,*V¬ˆÁ`À`0äG¶%›Í†Íf#**Šððpú~+ŠmÎpsÛ5ÅéÁ‰‰‰ÁËË«È\g©¨ÝßE­½nnwTTeÊ”)è*‰ˆHN:ÅŠ+8p Ï>û,ëׯgÑ¢Eö÷r�«ÕŠÕj-àšŠHvò%Hsýúuû/©ºÙoÏh4R¦LÂÃËL¿Å6gÈh{dddAW¥H)S¦L‘ºÎ RQ»¿‹Z{3d´ûøñã]ÉÁñãÇi×®ñññôêÕ‹0iÒ$:DµjÕìA)¼òåéN×®]+2¿¤æ«ÕŠÁ` %%¥Èô[Qls†Œ¶ÃŸßFëu_@‘»Î RQ»¿‹Z{3d´»(Líù+:zô(­[·f̘1ôìÙ“/¾ø777† Â'Ÿ|òPøy˜äÛš4ºéïLF¥~+ŠmÎPÛ\ÐÔçVQ»¿‹Z{3µöŠˆü•´mÛ–þóŸ 4ˆÈÈH:tèÀsÏ=ÇСCiÚ´)ÑÑÑøûût5Eä6òe$ˆˆˆˆˆˆ £ÑÈ»ï¾Ë AƒHOO'00Ž;2cÆ |||hÖ¬;vìP°]ä/@àù ûæ›oèÓ§éééö©ß/¿ü2]»veË–-„……ñé§Ÿp-E$/¤ù ëÕ«ééé™¶U¬X‘•+WrñâEêÖ­K±bÅŠÌ:j"ešî$Rˆ}ûí·Fû«[·n|þùç$$$tÕDDDD¤«X±"M›6ÅÅÅE‘¿ˆÒ¤§§óË/¿ð¯ý‹.]ºÐ©S'ÆŽËŠ+HII)èêåêã?fܸq´Ì¹sçâëëkuéÒ…÷Þ{S§NÝQ>;vdþüù÷©–ùkîܹ´jÕ*Û}o¼ño¼ñF¾”“žžÎÕ«W±X,ù’_~ dË–-lÚ´‰2}útFŒAZZZAWí¡d³Ù0`�¾¾¾=z4Oi^yå>üðÃ÷‡……áëëË™3gò«š[ßÓÈ7ß|“/O*Œ÷ôO?ý„¯¯o¶¿(¿øâ‹|üñÇP+y˜ÝüôK)ü tºSll,ÿøÇ?8yò$¤uëÖ¤¥¥Å/¿üÂ#<RÕ+´Ê”)Ã_|Íf#!!E‹Ñ¿/^¬ÛïÁÙ³giР›7o¦jÕª];WWWš5k@‹-¦^½z<ûì³´nݺ€k÷ð‰ŒŒdݺu�lÙ²¥P] «Œ÷4«ÕJTTŸ}§ço­�� �IDATö;vì`Ú´i˜Lwÿ1UXïi‘œhfÊ”)8::²`ÁŠ+VUùKquu¥aÆöŸ7nLpp0Û¶m£oß¾X3yjÕª…¿¿?ǧuëÖ|üñÇ$$$P¶lY¾ÿþ{j×®ÍäÉ“IIIá»ï¾céÒ¥$$$ЫW/FŒ»»»=¯£G2uêTÖ­[G… øÛßþF¯^½�øí·ßøòË/Ù½{7:tàïÿ;�\¹r…3f°råJÒÓÓiÛ¶-#GޤlÙ²=z”éÓ§³~ýzÊ•+G³fÍ;v,NNNX­V.\Èܹs9þ<}ûöeäÈ‘ö:å–öAÙ°a½{÷¦AƒÌŸ?ŸÁƒãààðÀÊ/Šn~Okܸ1Õ«W§]»v 2Ä ) ,HÆçŸÎo¿ý–§�Mtt4_}õ7n¤zõêüío£~ýú�lÛ¶™3gÒ±cG–-[Æ¥K—xôÑG:t¨=o«ÕʲeËX´h/^¤{÷î <777bbb báÂ…üôÓOìÞ½›É“'ãèèÈ¢E‹Ø¹s'qqqtèÐgžy†R¥JÝ×¾¹S®®®”,YÒ>ýeñâÅLš4‰M›6Ùyÿý÷INNæí·ßÎ6ÜúnŒ.øöÛoÙºu+eÊ”¡aÆ<ûì³8::ÞÿÞ›ÍÆúõëY¸p!ááá´lÙ’gžy†€€��, ‹-bÉ’%DEEѼysúöíKéÒ¥iР�-[¶`ëÖ­T®\9_Û~§CM3Ž¿9]ZZqqqøûûÛ·¿ýöÛ <˜çŸÞ>•â•W^!**Š1cÆœœÌ|Àž={˜={6F£‘£GÒºukÞxã úõëÇñãÇ1Øl6¶oßΰaØ8q"#GŽdíÚµôë×_ýOOO^zé%RSSù÷¿ÿ Ü®¸»»KÏž=yâ‰'øüóω‹‹ãâÅ‹8::b³Ù˜>}:«W¯f̘1˜ÍffΜÉk¯½Æ”)S¸zõj®i‹Åœ9s3f +VäÍ7ß$<<œ   LÇ8p€o¿ý–]»vѨQ#Nž<‰}rr2³gÏfåÊ•�Ô¬Y3SúéÓ§“@@@� , ((ˆ÷ßÿžîí[·2gÎöíÛGHHíÚµ£OŸ>�$&&2{öl~þùgÜÜÜxì±ÇèÙ³§=ø”[Ú‚P£F |}}9uê”=HάY³Ø¶muêÔaðàÁöÏ€ìÞÃ'L˜À€€¬÷takoNr;ßwó™V£Flßÿ2ÞûDDDD¤àX&22___Ê•+wÛccbbèÛ·/Ï?ÿ<½zõâĉôïߟeË–Q£F �V¬XA`` £F"))ÉþÏ /¼�À¬Y³Ø°aÏ>û,üðüóÎ;|ðÁörúõëÇ|@«V­(W®{öìÁÍÍQ£F‘œœÌ’%K?~<³fÍÂ`0ÜŸŽ¹CÉÉɬ[·ŽK—.Ѽyó»Î'·þIHHà©§ž¢wïÞLœ8‘«W¯réÒ¥ ILLä÷ßϲýâÅ‹™‚fK–,aÒ¤I¼úê« 0€… Ò·o_V¬X ,`úôé¼õÖ[xxxpòäI<<<ðññaÞ¼y<ñÄÌš5‹òåË@|||·ýf üðÃѸqcûö:ðå—_b6›Ø·o3gÎ$""???�ªV­J5=z4Mš4aòäÉŒ=Ú~dü! ðá‡òúë¯Ó£G�êׯÏÿû_vïÞMëÖ­™?>Ó§O§M›6�ö>ÌÑ£GéÝ»7µk×ÎT÷+W®ðÊ+¯°uëVêÔ©€¿¿?UªTáÿø1119¦}PöïßOhh(5¢D‰TªT‰Í›7g Ò>|˜ž={2aÂüq8Àœ9sìçÃf³1aÂΟ?Ϙ1c°X,Ìœ93KYü1ýû÷gøðá”,Y¸ûûðÀôéӇɓ'3|øpÎ;g”žžÎË/¿Œ¿¿?o½õñññLœ8«ÕJß¾}sM[P, ñññök÷È‘#tïÞüãôìÙ“;vеkWV¬X‘)Àpó{xppp¶÷talovn÷Þs7Ÿi9½ÿ‰ˆˆˆHáQ`Ašèèh1ÿ\»xèСöožÞyçFŒÁâÅ‹iÖ¬O=õ�õêÕ#,,Œ5kÖØƒ4�ãÆ³ÿ²}åʦL™ÂèÑ£‰‹‹ãí·ßfÅŠöo´}}}iܸ1/½ô’ýÛäyóæÑ¶m[{~mÛ¶ÍôsÙ²eyä‘G¸téRަ Ç×××þsµjÕ˜;w.eË–½«übccsíŸ+W®I×®]³ŒxP¢¢¢èÚµk¶û†Üä¼ûî»|úé§ö€CÆ éÑ£?ýôC‡åÀ4iÒ„víÚa02M«\¹2�•*U²¯_qêÔ©o{hhh¦é6=zôàûï¿ÇËË˾ÍÛÛÛ ×Hƒ ìä MõêÕ‰ˆˆ Q£F,Y²„3fd)/99™eË–±lÙ2†ši_||<&“‰?üçž{Ž;wÒ¹sgÚ´iƒ««+•+WfÈ!ôêÕ‹!C†Ð±cG7nŒƒƒ§N"))‰,e^»v-×´ʯ¿þJ÷îÝík;õíÛ—yóæ1|øpûÚ(3gÎd̘1öë®iÓ¦9rÄžÇáÇ™={6û÷ï·÷¿ŸŸëׯÏTV«V­øä“OìùÞË}A™2eèÙ³'...™öíÞ½›õë׳oß>œçøË/¿¤oß¾¹¦-×®]cÁ‚T­ZÕ~­|ñÅŒ3†!C†�7ú<..ŽéÓ§óõ×_ÛÓÞúž±øüÍ÷takoNΞ=›ãù¾Ýµ’ÓgZnï""""R8XÆßߟ;w’’’b_obìØ±Œ5 €×^{Í~ìÁƒ™7osæÌÉ”ÇóÏ?ŸcþÞÞÞ;vŒôôt¢¢¢HJJ¢]»vYŽKLL´“xë7Š6›M›6±nÝ:Ž=JDD@?uª\¹r|õÕW�|÷Ýwœ={öž~Ù¾]ÿT¨P'žx‚§Ÿ~š'žx‚6mÚò@ÿx®V­Z¦é[n~²Stt4QQQT«V;ÍÙÙ™öíÛsøða�zöìɨQ£¸|ù2]ºt¡M›6öQ Ù) m/W®sæÌÁh4âççG™2en»˜jJJ ÉÉÉY¶»»»ãèèHJJ çÎË6­££#þþþ¼öÚköõi2x{{7î½>}ú°nÝ:¾ùæ^~ùe.\HÍš5™9s&aaa¬_¿žqãÆQ¢D ~øá\]]X½zu–E\ýüüìÓŸ²K{s@ê~¹víßÿ=´Úrss#44”ÐÐPj×®ÕjeíÚµ|öÙg™ÒÞ ;~ü8õêÕË ËîzñòòÊtïå>lÒ¤ ~~~ôë×Þ½{Ó¦M{ÐñøñãÄÆÆÚ×ÊP¢D €\Ó>(·žyä¦M›†§§'ééé¬Zµ*K@±yóæŒ1‚ÔÔTû¶¼Œ ) íÍ‹ÜÎ÷Ý~¦ÝéûŸˆˆˆˆ<x¤)_¾<p㛽Œ�ÃÍSnþåÒËˋǜ×_=Sß gçæéHëÒ,X° Ë/ã%K–$>>>Û<æÍ›ÇܹsyõÕWyê©§¸páB¡X·ÀÅÅ…ºuëÚÿߪU+Ö­[G§NìÇd÷xלܮÌf3Ÿ~ú)ÇŽcóæÍL˜0âÅ‹óÅ_ª¡òkò¤§§gÚîääd¿Vš6mÊæÍ›Ù¾};ëÖ­cüøñL:•îÝ»g›§““S·ÝÍÍíŽO­T©»víâĉT¨P¸ñÍü®]»¨V­...´nÝš={öd¡d4éÖ­{öìáùçŸÏqjŸ¿¿?ƒ ¢ÿþtëÖU«VQ³fM AAAÑ»woÊ–-˾}ûhذ!*Tàĉ´oß>Û<sJ›1ê~Ú¹s'—/_fòäÉLž<9Ó¾7R»vmÒÓÓ‰ÏrÝ,--í®¹÷r–.]š¥K—²wï^6mÚDŸ>}èß¿?o¼ñnnn¸»»³víÚLÁ¤Œóš[Ú5­3ãéNF£‘R¥JQºti{`Ëb±›¥ÏÍf3®®®w0-èözzz7‚‚·¾‡ÄÆÆÚ§oåöÞs·Ÿiwúþ'""""^i‚ƒƒyòÉ'™8q"Ÿþy®Ó‡jÖ¬É'Ÿ|‚«««ýÜ;áïïO¹rå8}ú4­ZµÊsºU«VÑ­[7ûZ/A€Â¤ZµjŒ1‚O>ù„V­Záì쌛›QQQ™F)å&/ýc0¨Zµ*U«V¥[·nÔ©S‡ƒÞÓ:8ù- �8`Ÿ®’žžÎ¶mÛxì±Çìǹ¹¹Ñ±cG:v숯¯/ß}÷Ý»w·ÿvëyþ+´ýV7¦_¿~Œ1‚—_~³ÙÌçŸ΋/¾h_fܸq<ñÄ”.]š"##quu¥sçÎŒ3†† H=ˆgÇŽŒ7Þxã Z´h¿¿?—/_fÏž=Œ=š“'Oòý÷ßÓ´iS¼½½Ù½{7...âêêÊÿýßÿ1bÄÌf3õêÕãÔ©S\¾|™¡C‡æšöAX¾|9dÒ¤I™¶üñÇÌ;—‘#GâèèHûöíÙ±c:t°sóÂÆ„††ròäI{0:/îõ>4™L4l؆ R¿~}ú÷ïÏÈ‘#©X±" ÄÆÆÚƒ»·Ê)mÆh›ûíÖ'ÖÝÌÉɉÎ;³cÇŽLSwöïßÏ#<’k&§{º Û[±bE�~ÿý÷Lí¹xñ"Û·oçÅ_ÌTÿìÎw½zõîê3 r~ÿ‘¡À‚4F£‘ñãÇ3vìXzöìɈ#ìßø>|˜°°0 @·nÝøöÛoyõÕW>|8f³™={öЮ]»<ýT¬X1&L˜À¸qã0›ÍÔªU‹3gÎpåÊûÓ?²“ñtŒŠ+âääÄìÙ³3íwww'""‚C‡Q­ZµLßR?HÆ cÆŒ¬X±‚¾}ûŒ³³3S§N¥uëÖœ:uŠåË—gú£Ò×חÇsìØ1ªT©’kÿœ9s†ùóçÓ A¼¼¼Ø·o...w½ÎýâååÅ›o¾Ék¯½Æõë×)]º4«W¯æÚµköÑ"³fÍÂÝÝòåË“––ÆÎ;íë^øøøP¦L–-[Fzz:þþþ$''ÿ%Ú~+“ÉÄW_}Å´iÓxçwðòòâÑGåé§Ÿ¶Ó­[7~üñGfÍšÅ|@Æ <x0pãé:»wïfúôé >ooo:vìHrr2f³™J•*ñí·ßràÀªT©ÂgŸ}F·nÝ8wîœ}Íš'NФIV¯^m¿·{ì1¼¼¼øá‡xï½÷¨Q£†}J•ÙlÎ5íýtñâEæÌ™Ã¼yó²ìkÛ¶-'NäàÁƒÔ¯_Ÿ#FгgO¼¼¼hÔ¨‘‘‘,X°€çž{¸±fV÷îÝ;v,£GÆÉɉ~øá¶u¸ÝûTn÷á¦M›ˆˆˆ (('''6lØ@½zõðòò¢D‰Œ1‚W^y…W_}???ÂÂÂðõõ¥eË–¹¦-,FeïóúõësäȦNÊŠ+rM—Ý=V í-[¶,o¿ý6£GfüøñTªT‰«W¯2kÖ,ž|òIûHšÜÎ÷Ý~¦åöþ'""""…CiàÆ:³fÍbéÒ¥ìܹ“Y³fQ®\9‚ƒƒY´h‘ý‰*®®®Ì™3‡ï¾ûŽ÷Þ{ÄÄDš4irGåíÙ³'žžž,X°€I“&Q¥Jºté’kšŒõþýïS©R%úôéÃÞ½{íû{õêÅÎ;ùç?ÿɧŸ~š§'UÝ*T`̘1|üñÇtìØæÌ™ÃÔ©Sùé§Ÿh×®ƒ âÂ… ö4/½ôo¼ñ.\`êÔ©¹öÉdÂÁÁ)S¦pêÔ)4hÀ‚ ¬½¹6lÞÞÞüïÿ#&&†öíÛ3gÎû#ŒýüüX±bû÷ïÇÝÝ.]ºØ...̘1ƒ>úˆ­[·òúë¯S¡B…mûСC³,Þ{«qãÆe»ÝÃÃ×^{-ÓúN73 tëÖnݺe»¿zõêYÖ^É0bÄFŒ‘e{@@�¯¿þz–©‰7ËøÿnÒÞ/[¶lÁ××—FeÙW«V-ªU«Æ† ¨_¿>7fÑ¢E|÷Ýwüïÿ£C‡Œ7ޤ¤$àÆú3“&MbæÌ™|øá‡øúúòØc±k×®ÛÖãnïø¸8Nž<É‚ HLL¤uëÖ̘1þæÍ„ X¸p!ß}÷„„„0pà@àFp3·´…AãÆYºt)³gÏfÞ¼y4kÖŒ•+WfYÛèVÙÝÓ…¡½Ï<ó åË—ç§Ÿ~bæÌ™Ô®]›ÁƒÓ¥KûèŸÛ½ïÞÍgZnï""""R8FeËX¬÷fyÎä÷ß§nݺ¹®Ó ™988°oß¾"ÕoE±ÍX¿~}¡XÓ¨(X´hmÛ¶-r×YA*j÷wQko†Œvׯ_¿ «""""ò—eÛ´iÓ v$HQs'£¿DDDDDD¤h1tDDDDDDDDDA‘B!ߦ;Y­V¬Vk~e÷ÐËX²(õ[Qls†Œ¶ËƒS¯³‚TÔîï¢ÖÞ z/¹¿ò-H“žž^¤OÌ/E±ßŠb›åÁÓuV0ŠZ¿µöŠˆˆˆÈý•¯A‹Å’_ÙE±ßŠb›åÁÓuV0ŠZ¿µöŠˆˆˆÈý•/AšØØX¶oßžY9E±ßŠb›ŒF-õ ”*UªÈ^g­¨õ{Qko†5kÖtDDDD :Üsù6’¦wïÞù••ˆÈ=iÞ¼yAWADDDDDŠÅ‹çK>új_DDDDDDD¤PFDDDDDDD¤PFDDDDDDD¤PFDDDDDDD¤PFDDDDDDD¤PFDDDDDDD¤PFDDDDDDD¤PFDDDDDDD¤PFDDDDDDD¤0D¡‡"..ŽK—.Ý—üK–,IñâÅ º/ù‹ˆˆˆˆˆˆˆä·¤9tè‹…–-[Þ×röìÙÃáÇ©Y³æ}-GDDDDDDD$?<ðéNqqqÔ«Wï¾—Bllì}/GDDDDDDD$?<ð‘4—.]Âf³=°²DDDDDDDDþ dMš; Ò?Ɇ ë(æêJ«­)]:à>ÕLDDDDDDD¤`ØÓl6[ž^G†³fí¯<öXê֮˺ kó”îa—––ÆÒ¥K9tèPAWEDDDDDDDòA¡~÷±cGÙ°i=ƒŸ‚k17L&Ì&s¾—Ó¾}{L&QQQùž÷Í.\¸€Éd¢iÓ¦¹’1™L”.]:ÇcvïÞMß¾}yòÉ'±Z­wTÿüç?˜L&~ùå—<Ÿ””D\\ÉÉÉwTNvzöìù@úZDDDDDD䯦@‚4yA“ 1›9{.Š5ëÖÒ¨aã<ÂÉ‹ .°qãFš4iB@ÀýFuøða�zôèÁ`¸§¼*UªÄ£>ʰaÃ0ïì4®Zµ €ààà<?oÞ<J”(Á¢E‹î¸ž"""""""’7¶&MnA”cÇŽ²iËÆLšå+–Ó¦U[Ê• Ì×éL¡¡¡�tïÞýž'·³gÏ�5jtÏy•*UŠ… ÞqºŒ T‹-r©#"""""""V¡I“[€¦rå*yE“×@NFà¤aÆ�Ô®]“ÉÄ… � Ãd21`À�{Ý—/_ÎÀ©V­ýúõãwÞ!55•½{÷b2™øïÿˤI“hÞ¼9&L°—µzõjàÏ,ûöícôèÑ„„„СC^ýuN:e?>99™÷ߟ† òØcñóÏ?Û÷ÝZ¯Ý»wc2™X°`ï¾û.M›6¥wïÞlÙ²%S{3‚R]»vÅ`00sæLÜÝÝÙ´i/¼ðuëÖåÙgŸåäÉ“�|ùå—<óÌ3�<ýôÓ˜L&¶mÛfŸ’5mÚ4¦L™B«V­²ÔàÀŒ9’àà`ž}öYMsÉAŒ¤ÉÉÑ£álܼžaCþv#@sö K—-¥m›öT®\…]»w²gïï„Ô«Oòü|7n œÜÎÏ?ÿÌ£>JPPÍš5ãĉìÝ»GGGû1O?ý´ýÿ={öàâÅ‹¬[·Ž6mÚàççÇÉ“'éÔ©�:uâêÕ«|ùå—¼òÊ+ö´ñññ|ôÑGøúú²xñb/^ÌŠ+xä‘Gr¬_FÐÆÃÃ]»v±fÍ>L¹r倬A)¸±æL»víððð >>žC‡ÁÊ•+)UªÍš5cÛ¶m´hÑ‚Š+âææfOûâ‹/P½zu¶mÛÆâÅ‹Y¾|9;w&,,ŒæÍ›“””D•*U˜9sfžúXDDDDDD¤(*4#i®Æ_å§eKhÒ¸ fó€Çª_~¦RÅÊT©\›ÍÆž½¿ó÷ƲgïïÙþ|§#i.^¼ÈÚµkiß¾=¾¾¾yª{F㥗^â믿fÆ ÌŸ??Ó1µk×fïÞ½$$$0räHàÏ,]ºt <<œ+W®0`À�¾þúk–-[ÆÅ‹ñöö¶çãááAdd$¡¡¡üøã�Lœ81×úuíÚ•+W®pæÌ† FRR»ví²ïÏJÕ¬Y3Sº·Þz‹ .M™2eذa'Ož¤wïÞ :€#FðÍ7ßP»víLuŒŽŽæÐ¡C,Y²€?ü€3f””Ä?ü@XXçÏŸ§^½z·íc‘¢¨ÐiÜÝÜéܹ+k×­%*ê4�OBhØaBÃc³Ù¨W7„ϦL¢^Ýl¾Ó MXX�;wÎsÝ[·n‹‹ Ç'$$„ÿû¿ÿãÌ™3™Žiذ!µjÕÂÅÅ///àÏàNƒ €œ   >ÿüs*W®ÌرcÙ»wo¦z;;;ãííÁ` G�lܸ‘„„„ëçï®®®´jÕ €Ë—/péÒ%Ö®]K§N(UªT¦tåË—Çl6ãëëk$ÅÆÆÞ¶?œí®Œ‘A›7o&>>žµk×бcG�J”(¿¿ÿmó)Š ÕÂÁ5ª æÌÃÀ)[&QÏŽæ³)Ÿb³Ú¨Ò€ú! ìyÜúóº5pr3‹Å’mš–-[ɺuëØ°a&L`Ò¤Iö¼r²fÍàÏiUþþþlß¾M›6±mÛ6¾þúk¦L™ÂÏ?ÿL³fͲ¤7¸¸¸””„É”·Óf6g~\yÆhžÜ¦K899å)ÿ[Ýüˆn³ÙL||<�éééw•ŸˆˆˆˆˆˆHQRhFÒd¼ªW¯A§Ž0{ölΜ9‰££a +V.çÐáƒùºppÆH   û¶Œµ[2—)ÍåË—±Ùl<ñÄ|ñÅŒ9’+W®püøñ˹|ù2«W¯¦{÷îøøø�7—.]â‘GáwÞá£>ÊT.@BB‚½üÍ›7“””D=pqq¹mÛ²³wï^�êׯGé2‚6111Yö%$$ ÀöíÛèÓ§...´k×€åË—c³ÙˆçÒ¥KwUw‘‡]in§fP0;wåëo¿áôé8::ñÒ¸—Y¶ü'ބɗ2._¾ÌªU«èÙ³§=p7Å 7Öv)_¾|–©P?þø#´iÓ†'Ÿ|’Ù³gS¦LªU«–cY#X:tè`ß¶gÏ*W®LíÚµ:t(o½õyTORR4lØÐ>ehüøñwÝæŒÑ<·®Gs;Ç¿õÖ[ 2„M›6eªcµjÕhРݺuàå—_°¯Ç3bÄêÔ©CåÊ•3­#"""""""*° ÍíFÁ× ¦[—îÌøê+N:ÙlfЀA,Yº(_FÑd¬Gssà`ðàÁ|ðÁÔ®]› °dÉz÷îmß_¥J†Nbb"àé§ŸfÅŠ¹.<œÝOOOÆOÉ’%Ù¸q#!!!,_¾œ-ZØùøãyá…HIIá©§žbóæÍÙN…Ê‹˜˜V­ZEïÞ½3-NœuêÔáÿû 4àðáÜ={Ö¾ÏÃÃW^y…ÔÔTž|òI¶lÙB£F7ž´Õ¸qcÖ®]Ëc=†‹‹ cÆŒaÁ‚wU‘‡aÔ¨Q¶Q£FeÙçLÖ¬Y“)‘›Å‹Ó¹sg¬VkžŽß`‹/â©Aƒ¸–xÍ›7ñÂè¿ß6Ñh´%²3yòdÆÇ–-[hÒ¤Ižêr·zöìÉÊ•+‰‰‰ÁÓÓó¾–•“-[¶Ð¦M¦NʳÏ>{Ïù%&&âééI©R¥2mDDDDDDDŠšÅ‹g’›[—V˜6mZáZ88;µkÕÁl2³lÅrÒÒÒèÛ§_žÒÞî˜uëÖ™×£¹bbbX¹r%?þxhàÏÑ<!!!VÉYiîTAÔ¨‘¿Á”Ù³gc0pwwÏ×|oåééÉ•+W²<iéA6lƒÆÍÍ­@ë!"‘‘ÈÝ�� �IDAT""""""Ù+ô#iÜxxxÜ×ò3˜L¦VVn\]]ó=¿œS."""""""wîi|||°Z­ †<¯Ks§ŒF#V«5ÓS›DDDDDDDD ³þt§âÅ‹jÔÜ—Õj%44”âÅ‹?èæ‰ˆˆˆˆˆˆÈÿ³w×qYdûÇ? Š˜X«¨ ¨+vbp-¬ÅÀ¼v€bc³k\k×ÎVWL,̵L tóª„"*ðüþà÷Ìå!ôû~½æ¥ÌÌ3'æL9çŒH“¯Þ’¦Zµj\¿~+W®ðúõë £H‘".\˜jÕªeÈö…B!„B!Ò[¦ŒIS½zõÌV!„B!„"ËúêÝ„B!„B!DBRI#„B!„B‘H%B!„B!D •4B!„B!„Y€TÒ!„B!„Bd_ýëN¾¾¾_;H!„B!„BˆT³µµýªáeÊ'¸›5k–Á ‘¨;wî`ff–ÙÑÈò$ŸROòLd4)cY“ì!„H›ïåüù½¤ó[päÈ‘¯¦twB!„B!„È2¥%J¥ÊŒ`…H@GG•J%e2’O©'y&2𔱬Iö‹B¤Í÷rþü^Ò)ÒNZÒ!„B!„BdÒ’F|÷¤&[;’O©'y&2𔱬Iö‹B¤Í÷rþü^Ò)ÒFZÒ!„B!„Bd™RI£®9”)ñiÔ¨Qèëë'9Íœ9“7oÞжm[Ž;–éñýÞ¦—/_¢¯¯O•*UˆŒŒLÕoCBBøðáƒÆ¼­[·Ò«W/>}ú”éiËèiûöíèëë`Ù€˜9sf†…íç燾¾>>ÔS}\Ò©S'f̘A```¦çÕט¾Fú£¢¢xûö-Ÿ?VæÅÄÄ0jÔ(–,Y’ná¼{÷ŽððpyßÓ±õµË‹±±1½zõb÷îÝDEEezÜd’é{Ÿ~ýõWôõõñòòÊô¸È$SfO‰Ýó©'///{QÚ´iCPP*•ŠÈÈHÞ¾}›éiÈìiýúõÏŸ666L™2…þù'UÛ±¶¶fÓ¦M™žž/™2ƒ´¤É‚ú÷ïϱcÇ8vì¿ÿþ;�[¶lQæuîÜ9S Í÷îäÉ“�Ü¿ÿTý¶qãÆìÞ½[cžìÇÌÕ«W/Ž;ÆÞ½{éÒ¥ ?ÆÒÒooïÌŽÚW‘Ñéòä %J” 00Pc~z—û àææ–¡aˆÿ•—Í›7ciiIïÞ½ñððÈìh ñ]ûôék×®`ÇŽ™!²•*öå‘Ú‘#G(Q¢„Ƽ‰‰òü9iÒ$ž<y‚OŸ>Íì¨}ódLš,¨J•*ÊÿsçÎ @íÚµ155ÕXOý°/ùùeR[áåééÉÒ¥K9pà�‡¢nݺi SÍÑÑGGÇó¿–—/_²dÉ\\\øá‡’\/=+Û–ú﯑qÃ266ÆÚÚZYÖ±cGÌÍÍqvv¦N:˜˜˜|Q8YùøÌèôÇ+n^,\¸P™ŸžÛÏJÇÖ×ð5ËXüòbccƒ¡¡!¤k×®-Zô«Ä#;ÈêǾø¶\¼x‘gÏž±}ûv:uêDPPPº»…øÚÒûü™Ô¶ÔáÔªU‹}ûö%X7£ÏãYý:¡R©È›7¯Æ=býúõ)S¦ 'NœPÙNVOkV$-i²©÷ïßc``À±cÇ�xõêÅ‹gÇŽôîÝ ÆÏ£Gرc={öÄÚÚšÿüç?„‡‡+Û‰‰‰aûöítéÒ…F±hÑ"åBÓýû÷Ù³g666´k׎ 6ðáÃu>þÌÆqppÀ‚ &ðêÕ+êÕ«Ç­[·èÝ»7ÊÛ®µk×Ò¢E å÷*•ŠC‡Ñ·o_¬­­™8q"?V–Ÿ<yGGG6n܈½½=õêÕcÑ¢EDDD¤:=/_¾dêÔ©0uêT^¾|™ÆœI'Nœ oß¾Ô¨Qƒþýûãåå¥,{÷î‹/¦Y³fØÛÛ³mÛ6¢££ØcA}l >+++®\¹¢u¸ºººôìÙcccvîÜ™îéÊê’JryÅæÍ›±··§N:Œ5ŠóçÏóäÉÌÌÌ�¨S§ܽ{€¾}û²`Áàç¯]»vѧOjժŘ1cVÂ?zô(...X[[Ó¢E <<<”coÖ¬YÌž= ?~<¹ÇÖ÷ÂÊÊ ˆm1`gg‡§§'3gÎÄÚÚOOO öMÿš5k°··§Y³füú민{÷Nc[Œ=š:uêбcGöìÙ£,»páƒÆÂ‚qãÆñèÑ#ß;+++˜7oŸ>}Jq™ߊ}ûöÑ«W/š4iBáÂ…9uê”Æò“'OÒ¸qcŽ;F=hÚ´©Ò=t×®]8::R¯^=~ùåÈþýû¤ÏíB|K._¾Œ/^¼ààÁƒtìØ€¼yób``�ȱ –/_>Š/ÎçÏŸضmuêÔÑXÇÍÍM¹KŒ<{jGZÒdqqßøÇ¯Ý??,,Œm۶ѧOråÊŲeË033cÀ€ôë×ÏŸ?3sæL>}úÄäÉ“X¹r%¾¾¾ >===Ö­[Ç”)SX´h:::_9µ™#5µ»GŽ¡^½zT¨P€àà`.]ºDÆ •u¦M›F@@�ƒÀßß}}}Ö®]KÇŽ0`�-[¶¤xñâa«ÿݶmsæÌÁÍÍ^½záééI«V­8~ü8E‹UnªLMM9r$¸»»͘1c´N÷Ë—/qssS*fÔ6Ó§OO´EMzÔ‚'Užã‡qåÊZµjÅÊ•+2dÁÁÁ(P�•JEtt4ÎÎΔ*UŠY³fÊÌ™3‰ŠŠÒ¨Õÿé§ŸX´hMš4¡L™2<|ø0ɰãÿ;wn4hÀÍ›7¿(ÍÙåÍAJéO)Ï·lÙÂâÅ‹™={6 àŸþ¡@-Z”]»vѾ}{¼¼¼(W®¥J•J´Ü‡……±uëVþýïÓ·o_V¯^³³3Û¶mCOO[·nQ¹reÚµkdzgÏXºt)QQQ 6Œž={òôéSÂÃÃ7nyóæÍÔcëkÊŒ27¼?(ù±p'NdâĉTªT‰˜˜&NœHpp0Ç'22’yóæáïïÏÚµkÑÕÕ%00[[[ÆO‡xð຺º¨T*Î;ÇàÁƒ™5k–Ò¸[·nÊ›OºtéÂâÅ‹ áåË—äÌ™“$—etže—c_d¡¡¡¬^½šM›6a``@=øóÏ?qttTîãT*çÏŸgöìÙôéӇܹs“/_>¶lÙÂüùó™:u*… fÏž=lÞ¼™: R©’<·KÙ)=ï7ýýýyöì™Æ2u÷ëø-<T*,\¸Ñ£Gsþüyå:”ÇBv¹N¨ãÉ¡C‡xñâ666‰ÞgÅýMR÷Úòì©L©¤‰ŠŠÊŒ`³%õ›êèèh|Sÿ?&&†¨¨(åïáÇcii €ŽŽ>>>¸ººR¢D �Þ¼yÃÔ©S7n¡¡¡Lš4 ___ªW¯@±bŨY³&ãÆ£xñâ_-™EWW7AÞ&%::š7Ò£Gbbb([¶,–––ìß¿_i À‚ ¸}û6%K– iÓ¦�T¨PܹsS²dI¥uATT111¨T±¬¾ÿž)S¦°|ùr5j€¥¥%Í›7gÛ¶m 8P)ãÆ#þü@lË¢E‹pqqÑê÷òåKÜÝÝyõê•Æü/^0eÊÜÝÝ5*jR“OɉÛòBWW³!ŸºOpTT·nÝÂØØ˜víÚ‘'O¥–>**гgÏâëëËíÛ·•î€ïß¿ç·ß~£S§NJwìØ­­m¢a«×‰f|Å‹çÎ;iNszåYFÒ6ý)åùåË—©_¿>Mš4AGG e;åË—ÀÔÔT£Ü«+âîåü•?~4h@PPeÊ”aРAñ‹ŒŒäÏ?ÿdÈ!Q´hQôôô2ýØúš¾v‹[^TªØAÔW®\IÿþýùᇔùS¦LÁÕÕUù¿¿?ëÖ­ãÚµkÊuÅÔÔsss „••K—.eðàÁÊ~VŸS£¢¢˜?>cÆŒ¡eË–�Ô¨QOOO.^¼HñâÅ ¤M›6T«VM 3**ŠG%¹,#e‡c_|;Nž<ÉçÏŸ©S§QQQ4nܘ¥K—rïÞ=¥›¼úܶjÕ*åÞ$44”©S§²bÅ å>ÅÒÒ¥ü&un—²-2Jzßo&×%G}/÷þ°H‘"”.]�333tuu3äXÈ׉èèhnݺEž<y”y•*UbÇŽ”*UЍ¨(eðå¸éˆ‰‰Ñ¸·Œ{¿÷öíÛïþÙS[Ò’&‹Ó¶%MbË ,ľéTÏ344$88˜?ćhРA‚pÃÃÓŸä[‘š~’W¯^åòåËôë׋/PµjU6oÞÌØ±cÉ—/wîÜ¡Zµj%»Í¤öåãÇyüø1fffÊ|}}}Z´hÁ7ÔZ«ÿ_¸paîÞ½KTTzz)ÖÅŠã·ß~KvÄúå¦÷ø!‰…§R©¨W¯FFF´kך6mJÅŠ¸wïoÞ¼IP>‹+¦±Ý‚ ¦Øú,¹t=þœ5j¤9ÍÙ¡®¶éO)Ï;tèÀÀyùò%vvv4kÖŒbÅŠ)aÄ +©8Äý @@l7+•JEDDàèÑ£<|øÛ·oS¤H‘oq2ûØúš¾vS©TÌ;—¹sç*óœ7nœ²bó,nœîÞ½K:uøá‡”ù*T bÅŠÜ¿sssöîÝËÒ¥K¤%22|||2dˆÆ²°°0¬­­éÑ£]»vUºqXXX#GLMM“\–‘²Ã±/¾ÞÞÞÔ¯__éJªîÎ÷÷ßS¶lYàÇf¾|ù”ÿ?yò„çÏŸkœÕ/OÔå7¹s»!½ÎŸêß_¿~=ÁøLýõýúõKò*±{ô>²ÃuB¥RQ¦LÖ¯_Äv!ÆÊÊ*É{øäæ©T*yöL…¬uÇ)ÒUbo}ãÎË›7/�»víâÇÔXO’„> Ä>”ÄwéÒ%7nLxxø¯¾¹R×ê«éëë+-²3uÅá»wpµ7oÞ(ã[”*UŠýû÷sùòeŽ?NÛ¶méÞ½;nnnäË— ð÷ß“3gNå÷éÙÊáãÇœ;w''§tÛfv?ý)åyƒ ¸pá§OŸæðáÃŒ9’•+WÒ®]»t‹Ó˜1cÈ•+}úô¡H‘"9r„U«ViýûoýØúZºwï®t©522RZi&çÓ§ODFF&˜Ÿ?~råÊŧOŸxþüy¢7y¹rå¢D‰Œ=;;;e D__Ÿß~û;wîpòäI&NœH‘"EX½z5 Lv™Ù]pp°2îÓ‘#G4–yyyѳgÏ$¯êsar÷,_ãÜ.Dvð½ Jkö<yòP·n]|}}iÕª•²Njž{äÙS{Ò’&‹KmKšÄú&5ÏÈȈ2eʤ4ÿO,ìo™¶5Ù¬[·ŽåË—Ó½{we~LL ­ZµâÀ4jÔcccxòä ¥J•J°]]]>}ú”dséÒ¥100àÊ•+±7R§N¢k×®IÖö'7/=¤W¹r倨/Q¨›WCl7«3gÎ0zôh%Œ9r`ee…••tìØ‘¡C‡R¾|yÂÂÂxóæ µk×N4žñÿ¯þ;±ùq—©ÿïééIΜ9ùé§Ÿ¾é–4j)¥?¥<‡Ø o‹-hÑ¢%J”`Íš5´mÛVYïóçÏI¶ÎJé\õöí[þøãöíÛ‡¹¹9[qw="##³Ý±õ%2£Œ•.]Z©LUÇ!~œâÏ/[¶,—/_æáÇ”)S€gÏžqùòe*T¨€¾¾> 6äÊ•+=Cì~mÙ²%W®\aàÀ 8Õᘙ™aff†•+WæêÕ«ØØØ¤¸,#d§c_do'Nœ dÉ’\ºtI£KÂáÇéܹ3wïÞUZ¡‚æyÌÈȈ àïï¯Ü¯$vÍLéÜ.DzJï–4ÉÝó%w±÷-¹råRþNÏc!;]'â^g‡ÊܹsiÔ¨¹sç&oÞ¼<~ü˜ÈÈHôõõ•õ“j¡$ÏžÚ“–4ß±<yòàîîΈ#ÐÓÓ£fÍš<zôˆ×¯_Ó³gÏÌŽ^–ráž>}JãÆ5æëêêâè舻»;&LÀÚÚš† 2aœÑÑÑáòåËôîÝ›|ùòQ·n]öíÛG¥J•È—/Ÿ2�±š¡¡!?ÿü3cÆŒ!""‚’%KràÀÞ½{÷MÜ™˜˜ðË/¿0xð`&L˜À?þHHHkÖ¬¡wïÞÊx$Ç'00ªU«¢¯¯ÏÑ£G©S§… ¢X±b :”Ñ£G3iÒ$J”(A@@�%J”Hô„ŸõàÏ111¼|ù’ãÇsþüy–/_®ÔøË´IµjÕ’Íóµk×’?~LMMùôéçÎSúk-ZcccvíÚEtt4%K–LuáüùóS»vmÖ¯_Ott4aaa,]ºTcråʱaÃΞ=‹µjÕÒXþ­[Y™¥¥%íÛ·ÇÅÅræÌ© ®î“>lØ0úöí‹‘‘5kÖäÁƒäÉ“‡æÍ›ãääDãÆ111¡uëÖ„‡‡sñâEœyþü9[·n¥nݺâçç‡&&&<zô(ÉeBdwê õ¾}ûjTÐ�Ô­[— pêÔ)Jš¸ ,ÈäÉ“;v,>|ÀØØ˜£Gj¬“ܹ]ˆo•zLooo*T¨@õêÕÙ¼y³ ÀÀñðð`Ïž=tîÜ™5j```À¯¿þJ“&M b÷îÝ/\J”(Á7”JcyöÔŽ´¤ÉâÒ£%Müõã®gooOÁ‚ñòòbÞ¼y˜™™}Qëì&±¼JÌÞ½{éÑ£G¢cÍüë_ÿ",,Œ .`kkËš5kX¹r%ãÇ'wîÜ4iÒ„?’7o^ÆŽËœ9spvv¦_¿~”/_>A @¡B…ðòòâÍ›7ØÚÚâéé™èk’k5•ž´Í'm 2„råÊáííÍï¿ÿNÍš5éÛ·/mÚ´QÂ044äÁƒxyyñîÝ;š4iÂÚµkÉ‘#*• www¼¼¼X»v-˜››Ó«W/­ZÒÄOÏüÁ–-[” sss&OžŒ¡¡á¥7=ó,£¤&ýÉåyñâÅÙ³gW®\!þü´iÓFéï;wnÖ®]Ëܹs9yò$S§N¥hÑ¢qHé—®®.¿ÿþ;óçÏgôèÑX[[3tèP1LÚ¶m˃5jVVV,\¸0[[_âk—1mßüÅ_GWW—%K–°zõjæÌ™ƒ¡¡!vvv8::*ëµhÑ‚uëÖñǰpáBêÔ©C÷îÝQ©TT¬X‘'N°zõjœœœ(T¨7æÃ‡èéé‘#G.\HPP–––x{{cbb³gÏ’\–‘y–Ž}‘ýݺu‹'N0}úôe-þüüûßÿV¾˜—Ôy¬ÿþÊWžÞ½{ÇO?ý \k“;· ‘Òëü™Ôý_ü0kíQ¥JV¬XÁŠ+022bîܹé~,d‡ëDbq455e̘1Ì;WiQäååů¿þÊÎ;±µµ¥W¯^¼xñBùݸqã˜0aÏŸ?gÅŠßý³§¶tœœœT‰»XW¤øúúÒ¡C­×­[·®ÖÛ"#éêêrçÎý"…&ɧԓ<MÊXÖ$ûEdWÏž=£J•*œ?>É8Bd¤ïåüù½¤ó[qþüy/Æ&ÇÛÛ[ëu!¶E{|Ë—/—–4âû–ú„f&ɧԓ<MÊXÖ$ûEd÷ïßçôéÓ”/_ž9r°uëV D… ¤üŠLñ½œ?¿—tŠ´“1i„B!„øÎ¨T*nß¾Íï¿ÿŽ‘‘‘Ò 1=¿–(„"õ2¥’æK>Q,Dz‹‰‰‘2©ɧԓ<MÊXÖ$ûEdåÊ•cæÌ™ æKÙ™é{9~/éi“)•4Ÿ?ÎŒ`…H GŽDEEI™LäSêIž‰Œ&e,k’ý"„ió½œ?¿—tŠ´Ë”JšS§NeF°B$éùóç™…lAò)õ$ÏDF“2–5É~Bˆ´ù^ΟßK:³»üùóõ03¥’ÆÞÞ>Uëïܹ“&­~âlÀc¤Q˜H/º€uecîݼ†µµufG'Ë;wîœäS*Iž‰Œ&e,k’ý"„ió½œ?¿—tfwçÎ#44ô«‡›m>ðËêf™ ñ9ý.E2;B!„B!± ²…d¤y‘þ¢3;B!„B!ÄÿË”–4iþ&¼|J^!„B!„ߨlÓÝ)¶†FjiDz“2%„B!„"kÈ6Ý„B!„B!¾eÙ¬»“´zB!„B!Ä·IZÒ!„B!„BdÙª%´£B!„B!Ä·*›µ¤Q)Ó•+þÌœ9ƒ6m~¢OŸó×_Û‰Šú¬±NXX(¿ý¶ŒN>|‡ÒXþþý;6mÚˆ³³-Z4ççŸÝxðàu@ÅéÓ§puGÛ¶vÌŸ?ׯÿ›`ä¦þ¹Ï/¿Ì¤M›Ÿ˜:u ·nh,ú4˜%KÓ«WO:t°ç·ß–òVëí?~üˆaÜY»vM‚e)åAü)*ê3ÞÞ;¨U«&oß¾Qæ?þLÉ«&Mãâ2œ¿ÿ>™bÜ.]ºH›6?qáÂyeÞ A)SÆ$Ñéüùs‰Æéܹ³LŸ>ìéÐÁžeË–&»<ø‡jÕªrèÐÁâ˜zû÷ïGGG‡?&ú÷÷&$$„Ù³gcccC·nÝØ½{wfG)ËK<Û²e U«VÕjÝW¯^¡££Ã¹sç’\Çßߟ–-[”ê¸d%ææælذ!Í¿ŽŽ&$$„¨¨¨tŒUæ˜4i£GÎ2á´lÙ$—§µ ÆÄÄPµjU¶mÛ¦Õú?~$$$$Ua$ÅÉÉ 77·TýFöKâ2{¿¤UjÓ™V»ví¢\¹r_íÜ”šršžçÍÔ\Û£MyýÒ02Chh(nnnØÙÙ%XöàÁY¼xq‚e¾¾¾tïÞ333ºuëÆáÇS ëôéÓ˜››ó÷ß+ó¢¢¢ðööfРAT­Z•nݺqâĉ¿}ÿþ=¿ýöŽŽŽT®\™~ýúqúôéT¦6¡ .0fÌÌÍÍiÓ¦ 7näóçÏ_¼Ýì$+=<~ü®^½ À›7ohݺ5GMñ·i9_„„„™æøfGY¢’æìÙ³Ô«W/ùƒ8ÎsõÕ+Wqꄉ± ã]ÇÓ¬i3~vûÏ­žÊ:ïß½g˜ó0îß»³“3–– è?€Þ;•u&MœÄµ«×°kcÇÄ yõòýûõçÍë7Ê:dð Á˜×1g䈑üsÿLhH¨Vu(ÿÜÿ‡ž=z’Ç ®ã\Éc‡Ý{p+à¨à]ø;ìÚØõ9Š~}ûѯo?|öù0Í}1Ñ1Én;:*šÝ»vÓÖ®-{vïI°\›<ˆ;? fÌè1Œ1’·oÞj,;|è0Aƒ°kcÇäI“)iT’ž=zrìè±D·õþÝ{ÿº‡Žܸ~Cc™ËpvüµCcrvrƨ„U«TMt[‹.¢œi9FŽI×.]ùûäߌ5š‘¬ÿéã'~™ù áaáÚÕs‰4 ÇÑё۷o3qâDlllhß¾=[¶lÉì¨eYY5ÏT*111™‡¬àÉ“'*Tˆ»wïfvT¾HLL {öì¡~ýúÙ&œ´–Á   ¨Q£†Vë>|˜B… eJy—ý’´ÌÜ/_"µéL«‹/Ò¡Côô2¾|jËOV:o~‹×²sçÎÑ¢E f̘¡1?::OOO,--ñòòJð»}ûöÑ·o_š5kÆÒ¥K©V­-Z´HòA:<<œéÓ§Ó°aCüüü4–­[·Ž¥K—Ò°aC-ZDÙ²eiܸ1çÏŸWÖyõê;väÈ‘#888°dÉ,,,xþüù¥ÿâÅ‹téÒ…råÊ1{ölìììpqqaÍš5_´]‘¾´í)“–óEƒ عsgZ£–-ejw§ððp–.]ªœtT*U ;8vYåʕؽ{E‹ýÿù6„‡‡±yó&zô莎ŽGŽøòöí~ÿ}�èëçbÑ¢…4onKÞ¼y?Þ###ttt�¨V­*\¹âOÓ¦Mùðáóæý‡9sf+5×––´mÛ_ßÃ888¤˜Ö ÖóÓO­9r:::ØØ4$$ä-6¬gΜ9äË——½{÷P²dIå7… Ò£GÆŽC©R¥’ÜvdävïÞÅêÕ«øÏþCüZmò ®‹/P¾|9–-[ʰaÃ4¶×³g%Ÿ�lm›Âþý>4iÒ8AÜž<ỹÿ°}û6:uꤱ­jÕ4ß^ÄÄİpáúõëK¾|y‰_sR°`U§¸ �� �IDAT<=·j„_³f ZµjÅýû÷¨R¥ŠÆú[·þAÞ¼yš˜Œ·oß>^¿~ÍŽ;È“'�úúú¸»»Ó¾}{òåË—É1Ìz²jžÕ©SG«7l"{xüø17nÜ zõêÙ&œ´–Á›7objjJùòå¿8MöË·çk¤S¥R±gϦNšaaÄõµÊiFø¯e^^^Œ1‚ÀÀ@.^¼¨Ìÿðá[·ne×®]Lš4)Ñß 4ˆþýûТE ‚ƒƒñõõ¥iÓ¦ Ö "00¿ÿþeöööôìÙSyž°µµÅÏÏêÖ­ ÀªU«È™3'[¶lQÖkÞ¼ù§¿Fœ?žâÅ‹+óBCCñðð`ðàÁÏ"s.\˜dv4¾)™Ú’æÑ£GÜ»w__ßTý.W®\q*hb/^œ°°0¥’gÇŽØÛÛ+' €ýë_<|øëׯP²dI»`Á‚�J³±k×®Hƒ ”u (€ƒƒ»víJ1žoß¾eýúõ4oÞ\ GWW—Ö­[³uëV^¿~­Ä#®"EŠ�ðéÓ§d·Ÿ7o^V®\‰……E¢'(mò ®víÚáââBáÂ…,‹¿}•JEdd$?üðƒ2oéÒ¥¸»»£R©033cáÂ…”-[6Ù4�\½z•S§NѺuk�"""prrÒ¨1¾:o ( 1? �Ʊ±qŠa'F}ÑëÖ­fff´oß>ÕÝQ>~üÈÊ•+iÓ¦ 666ÌŸ?Ÿððp�6lØ€¡¡!ïÞ½SÖŸ={6U«VåÍ›7ʼåË—ÿWÖµiÓ&zôè¡T6@lÓù{÷îqùòåLŒYÖ•Ö<{öìãǧfÍšØÛÛsðàAå111xyyaooµµ5óæÍSʜڟþI«V­°´´dúôé¼}ûVYvñâEttt”7^¾¾¾899Q»vmš4i²eËøðáû¶ÌÐÐ;vн{w*W®Ìˆ#xòäI’ñ?~ü8]ºtaË–-ØÙÙammͯ¿þªlbÏ+ gÏžÔ®]›±cÇòøñcí¼zõ wwwêÖ­‹½½=^^^©zkÚ²eKÖ¬YÈ#¨^½:]»vUnz?~¬œ³ªV­ŠŽŽwîÜQ~·iÓ&FMÍš5éÝ»7þþþɆ•R\[¶lÉ–-[pww§víÚJkªˆˆ/^L£F°µµeåÊ•üøã©ênrãÆ åÁ1&&öîÝ«,ÿçŸ4Ò÷æÍæÌ™ƒ õë×gòäÉJÞ/X°€Ÿþ™5kÖШQ#\\\ àîÝ»Œ9’š5kÒ®];æÌ™£ÑûìÙ³ôìÙ“ªU«âììL`` ²,~\°`îîîÌš5‹úõëÓ¼ys¼¼¼¼Ì‰Û ¥²¹ÿ~Ú¶m @Ž9”k‹úw>>>ôêÕ‹êÕ«3vìØo€ýüü0`�•+WfàÀܾ}[ë}"û%ëîµ´çêt~øðâÅ‹óÇh,Ÿ1cC† IñÞBÝ-õÈ‘# 2„š5k*a?zôˆk×®)•&ǧ~ýú9r„.]ºÐ°aCBBBR,OÇÇÁÁuëÖѦMÌÍÍ™7oï߿׈süò“–ó¦¶û.¥k[j÷Qüòš–0’¢R©Ø±c˜››3mÚ4úô飑çÚ@GGG㜾k×®$+,X@÷îÝÉ;·Æü|ùòáííMƒ ÐÕMøH—;wnüýý•{昘nÞ¼©ì¯÷ïß+×e€jÕª±aÃ*T¨`[ÅŠÓxžÐÕÕ¥L™2DDD�±Ï<“'OfÀ€ë¥}}} ˆ}v Mûƒ³ ìüüñþý{åü¥vìØ1zö쉙™½{÷æ?þHö>+)æææЭ[7tttضm7n¤dÉ’JùÖè‚•ÝeJ%ºÅL•*UXµj•rPÏ?ýï‡IOþ~þ4kÚ ]]¢£¢¹tñ¥J–ÒX§XÑb ‚W/_%Ù5 ”/WTðòÅK~(ö… i¬gbl‚ße?¢£¢“Óëÿ¾üPìù%Š—� º©§;·ïP¶LYJ•L±«Ž®Žn’]¡R›Im+îtõÊUþ>ù7sçÌåQÐ#zöè *PŨ¸p‹×o(ù¢ÍöPÁŽ¿vн[wŒKƒ "?DrÅÿв?ÔÓ«—¯¸xá"»vîÂmª3g̤t©ÒÝ¢Üvgšû4­ò.©6ººº\¾|™–-[²lÙ2š4iBûöí5úæ&G¥RáêêÊÁƒ5j®®®ìرƒÁƒC£F UN"QQQlÞ¼™€€�æ¥ÞÞÞZµÖÊ,ÑÑÑœ>}ù%J”�øâæ­ß¢´æYXX­Zµ"::šÅ‹ÓµkWNž<©±Ž‡‡üñ#GŽdþüùܸqƒ &hœCïß¿ÏèÑ£qwwçøñãtïÞ=É>ÝT©R…¹sçÒ§OV­ZÅÊ•+•å¡¡¡lÙ²…ž={âááÁ‹/<xp²}Œ·mÛÆñãÇ9r$&L`ݺuüüóÏÊr///ÆŒCÇŽ™?>oÞ¼¡iÓ¦¼zõ ˆm}iggGdd$ÿùÏèС...¬X±"É0³lÙ2êÕ«‡‡‡fffØÙÙL±bÅ”7AÞÞÞ\»vM£²wÒ¤IÔ¬Y“%K–Pºti6l¨ñ0—¶qíÙ³'111¸¹¹aaaJ¥bÔ¨Q9r„É“'3zôhvìØÁ½{÷R•ÆK—.¥ªkÄèÑ£¹ví3fÌà—_~ÁÄÄD£|úôéüý÷ß >œÎ;'NHHmÛ¶ÅÐІ BñâÅÑ××WÖ?þ<ööö,]º”èèh6l˜ ".®eË–‘?~æÎK=pttÔ¸ T©TøøøP¯^=e^reÓÊÊŠ¥K—påÊ®]»¦ñ»yóæÑ¹sg-ZÄ£GèØ±£R‘xõêUlll¨U«+V¬ J•*;vL«üM,¿´!ûåëì—¸RsœÇMgþüù2dˆÆ ¦?²nÝ::vì¨õ½…­­-5jÔ`êÔ©˜šš‰·Ö9{ö,3gΤ]»vŒ5*ÁK«¤ìرƒ€€�ÆŒôiÓØ¼y³’÷j‰•Ó´œ7SÚwÚ\Û“š}”Ö0³qãF&OžŒ££#óæÍãõë×_4š¶«€ÑfÙ°aørå :tÀÇLJQ£FQ¤HØJ .ht;In{qEDDpìØ1j×® ÀÓ§OØŠ£Y³fѰaCÒ\!–’sçÎagg§u|³ƒoéùÃÏϦM›Ò¼ys6lØ@ûöí)V¬XŠ÷Y‰Ù´i¦¦¦Ì;—k׮ѸqcZ´hÁ³gϸpႲÞñãÇiÖ¬Y¶l˜˜Léî—öWÒOÕׯ_gÓ¦øøø�*Þ½ '<< ƒÜ¿ÑÓËA¹r¦qåýŸ˜˜Ö¬YM×®]¨XñG@Åë×ÿ¥dI£ëæË——ðð0>|ˆH¶k‚:œÜ¹õ5¶¡îŠ–`Û¡¡¡,Y²˜aÜÑ×Ï•dšÒ¬yHK$¾-ÍuÆŒ­\;t耮® BG–,YLtt4zz9âü.ùZ‘'Ož°qãþúë/eyáÂ…8|øÐÿ×Äÿï7×®]¥_¿~ÊßC† F¥ŠQÞ<¬Y³3³Š4on›b¸šiÕ¤¯¯Ïüùó•¿›7oÎ¥K—8sæL‚柉¹rå «W¯æþýûÊÃwÅŠ©T©Ç§^½z´k׎S§NÑ Aî޽˧OŸ:t('OžÄÖÖ–GáëëËÚµkS /³„……š Ûœžž+VÔ¨•±ÒšgÞÞÞ+VŒ9sæ(7ÍŸ>}úÿnޱoÛ]]]9sæ 5kÖÀÈȈ *0uêTräÈÀĉ±¶¶ zõê”)S†'N`kk› ̸oåÕáýñÇŒ1B™7nÜ8e{†††Ô®]›Ç+‰™?¾Òj±hÑ¢ØØØàììL‘"E˜0ak×®Ušaׯ_Ÿ† ²mÛ6œœœøë¯¿(\¸0³fÍBWW—F‘7o^ @÷îÝ144L2ܸ ¤Ü¤ZXX°`ÁüüühÛ¶-•*UbÙø])'NœÈ¿ÿýo ¶UâíÛ·Ù¼y3Ó¦MK†¶q1cS¦LQ~çïïÏÊ•+ VZY–,Y2U7ºêÇqãÆiµ~TTÛ¶mÃÃÃÆФIulmm•¦ìI…ÌÝ»wéСƒRã9r¤ò†ÎÆÆ†;wî°eË&L˜èúÍš5cøðáÊú§NâøñãJ™ æÒ¥K nÊ’+›ê·xÕ«WOp2kÖ,¥b¡V­ZTªT‰£GÒ¦M–.]ÊäÉ“ÿ¿;04jÔˆ›7o&‘« É~Éšû%¾ÔçñÓiggÇôéÓ ¦T©RøûûIƒ ´¾·8pà�-[¶ÔçòåˉVîmÚ´‰Ò¥K¤ªEáÔ©S•Jׯ_3gÎÆŽŽN’å4µçMuëœäö]J×¶¤¤f¥5ŒøÞ¿ÏĉY¿~=-Z´�bÇɈ_Á••T«V¡C‡âêêúÿÏHpôèQeß-Z”7nh´ìÕÖÖ­[ÑÕÕU†ƒP¿LiÞ¼9Ë—/ççŸæÄ‰´jÕŠãÇÓ¨Q£tJUìñàáá‘`Üœìî[zþ¸}û6eÊ”¡sçÎ ÊWr÷Y‰©R¥ ˜˜˜h\SœñññQ®[·n¥oß¾ßLÅ]¦¤"***Á±o™“Z–”gÏž1aÂ\]]•‘ÚÕ@q›@AììÙ³gJw¢¸6mÚÄÙ³g5@ ,ÈãÇ4¥‹ˆˆ H‘"J¡{ðàÊ·6ÐhÎ(£S«TÔ>~üÈÌ™3©P¡‚ÒìàÅ‹Û׿jZò@¾¾¾Üºu ¥[’:}zzzoæ´qàÀêÖ­K­ZµÄ?þAÖ¬Y3î߿ϥK—˜7oC† áСC@ì¨ï;wîdäÈ‘érp>}ú”+Vàè舵µ5ÞÞÞ š'åîÝ»XXX('H�333*Uª¤ì»®]»òçŸÃÅ‹éÞ½;íÛ·ÇËˋϟ?+ͧÓÚeëkP—ï¸Í&!¶Œ=yò„bÅŠeF´²´´æÙÕ«Wiܸ±Æ zÜÿA­ZµÐÑÑAGGGi®¿Ë“š‰‰ ¶¶¶IÜ——  iÓ¦L™2…—/_&™6u¥Cü´Å·9·úœýèÑ#?~LPPÆÛÀÀ€Ÿ~úIyëãïïO£F4Žq BCCyôèQ²á&%wîܘ˜˜–âºq㮣£C“&M’좦m\㟋±²²Òè«®dÓVpp0.\Ðúm’žžsçÎeèС 6Œ½{÷&8ß.\X£" ±p*T¨@ß¾}iß¾=îîîœ9s†èèè$ÃÍ™3'mÚ´áÒ¥KZ§ÍÈÈH£2S›ñ@´-› ¹‹-JÆ ¹wï111øøø`nn®±~®\¹´Ž»ìM™µ_ P¦øçŽÔçñÓY»vm,,,8sæ �GŽaðàÁÊ=™6÷ñï U*ûöíÓh•¤¦më™ä.\˜Û·o+eB›ršÖófÜ})_Û´ÙfJû(µa<}úT£|ܺu ˆíÖõìÙ3kTJçæ”ÊZF›7oû÷ïçÒ¥K1þ|ìììX¾|¹²N¾|ùR}ß|êÔ)\\\X¶l™RóçÏÄŽ»7tèPš7oÎŒ3èÖ­7nL·4=yò„Aƒ1kÖ,¥Ï·$«=¤µ ÛØØP²dIlmmY²d‰V]PSV§NX»v-aaaÜ¿Ÿ}ûö%úÒ1»ÊÔîNñ»3¥ØÝ)^ëˆ7o^3jÔHLMË2pà�e¾ž^êÖµâÑ£ ëøÁ?Ó˜ïí½7·©ÌŸ?R¥J*ó‹+Êë×ÿMð©çgÏžbn^GiEâæ6[ÛfÊ´~ý:@E‘"…ØÏWÇýý«W/5–Cìg¦çÎÃ¥K™6Í=Në»wïÒØ~—.äEü)µyš)OªV­Âøñ®\¼x€€›iÚÎû÷ïX½z½zõŒÓú&åt+V”.]:Ó¿?vìø‹¨¨Ï¸¹Måþý{Ô©S[ùœ÷ãÇ8p�eʘàïï—Ä6zóæ mÚ´!""WWWþüóOºvíšèº‰‰ŒŒLô3q Pnÿõ¯áççG`` ¤Q£FXZZr÷î]nß¾ÍáÃÚ L™ræÌÉ¿þõ/þùçù¯^½"""##£LŠYÖ•Ö<‹ŒŒL¶ÂZ]a|øða=z¤1%7.Tž<y’|˜qrrâÈ‘# 8U«VáîîžlÚÒ2pŸúÆP___é3?úúúJÿöˆˆˆËÕ7ÛñTÓÔÊ‘#‡rc_Zãúùóç/þÌd@@�*THð€œ\ßýaÆqïÞ=êÕ«ÇÚµk©S§NŠ­⇣¯¯Ïš5kðññ¡hÑ¢Œ5ŠvíÚ%ûYå\¹r¥jü‚øåìòåËtìØ1Ù‡®/TROOܹsMhhh²•)‘ý’üoRãKö˰aèZµª2-[¶,Ùõ“;Îã§SOOþýû³k×.>~üÈúõëiÓ¦ ö{‹gÏžqþüy­¿•Ú1:âÊi|i=oª÷¤|mÓVrû(µalݺU£|¨ßÒ«·‘šK)•µŒOåÅ‹L˜0‘#Gbnnމ‰ cÆŒaùòå8;;§¹µó•+Wppp`úô郫ǨŒ{ÌëèèP·n]¥¢ëKý÷¿ÿ¥wïÞüøãZ>;ÉŠÏ©=_ªsâÄ æÏŸOHHM›6eâĉ)^ïRV½zõ(V¬gÏžåèÑ£ 6,ÁØEÙYöiïÙú]ø;&ŒŸ€~.}fΘI®œ¹4–;ttà¯íñþÝ{eÞ¹³ç¨\©2Õ«UWæñ=ÂÈ#ñXîu]kmÔªY‹R%KqòÄIeÞ‡ˆìÞµ›öíÚ+ó6mÜDÐà eš4q¨bÇ騡#û}ö+ŸÓVŨ8â{„þýúS°@APALt ¿-û Ÿ}>¬ðXQ #x 0Pcû~—ý´ªÑ6´™¢>G%˜—C7¨ AeO?i½Í“'NMãF,ûñUŒ T±ãë$ö9òœz9)dX]]-\Äý4¦Šý€ÛT7ì? Œ3¤M=Í­[·¸rå ½{÷¦N:˜˜˜¤êmi… ¸pá<Pæ=}ú” .(MüJ—.M×®]ñööf×®]˜››S¸paú÷ïϾ}ûðôô¤aÆZ‡™Yz÷îÍÆ5Zkœ8q‚5j$x³)bi›gqßœT«Vh ô÷BWºtiLMMyøð!ÆÆÆSÜ ¸2oÞ¼áôéÓ˜™™%ˆãÛ·oÙ°a=zô nݺ”/_>Éàä$öö'î`ÅwïÞ%Ož<”-[cccòäÉ£Ñ|9**Š'N(oËjÔ¨‘ (S¦L‚q~ân#îúÚJl¬ž/^hü}æÌ,--•¿ã¦7-q(_¾<×®]ãþýû©Ž³šŸŸíÛ·Wn–uuu©V­ZŠ7åFFFôèу?ÿüöïߟªp ö¦¼J•* 6 ooo|||4õŒû°¤R©8uêiI&;àlb- ’£~(MìÁM= ?Äv?>sæ •*U"gΜ´nÝ:ÁX©y`“ý’¼¯µ_<¨ñ"0~·—”Žó¸KgóæÍÙ²e {÷îÅÐÐPi)œÖ{ u¥I¹rå’]OÛò”’Äʶ;o&µï åk$~OÍ>Ò&Œ¸ÆŒ£Q>Ôa•.]š‚ j\£RªdIª¬©[V…††&ûû/¡¾ÞǯL+S¦ ð¿}¡ueQ`` ;w¦_¿~= 6عs§Æö=z„••UšÓ¡Æ AƒÐ××gùòå©n¹ŸdÅç”ΗÉÉ™3'õë×ÇÍÍõëc¿j¬î ÏÉ…¥««›àƒ:úúú 2ooo6nܘ`°ã¸÷dIý?+ËÔOpÇÿ;¥Op«—|üø·ŸæÀÁƒ¬X±‚âÆ’%KR¼xqZ´lÉVOO&LœHÇŽyýú53fÌ`îܹäÒ×GEl'ggZµnM ##üâܰԮ]›¼ùò1zÌ&NšÄ‡ÈHŒÙ³g…‹¡q“&I´ÃÐÔÀ�zôèA‚±¶¶ÆßߟýûÙ¼y³òûõ6°`áBÆÏûˆ%ùóçOt„õ¸®_¿NLL áïÞñ$8˜+W¯R¸paŒµÊƒ¸^½zÅÓ§O¹wÿ>*àÆÍ›äÏŸŸÊ•+3qâDŠ)BíÚµ)T¨¡¡¡lݺ•®ŽŽTøñGbT*FŽÅ«W¯Ø¼y3Ÿ>}"00·oߢïÝ#—¾>&&&*TˆèèhÖoØ@ß~ýÈ—?¿F\Þ¾}KÛ¶méܹ3#FŒàf@�3gΤ}ûö”*UŠœ9sróæMÖoØ€§§':ººTª\9AÞäÒ×§´±±²LÛ÷êÏž/]º”æÍ›À¦M›3f ð¿.+çÏŸ§jÕª þ®[·.;wfàÀŒ7Žœ9s²lÙ2FŒ¡1€ƒƒ]ºtaÀ€JóæÖ­[Ó©S'Ú¶m«\H³²:°fÍLïÞ½yùò%£GfõêÕ ¾B bi“g+V¬àçŸæüùó”-[fÏž‹‹ Ý»w',,Œ (ÛÌ›7/sæÌaРAäÌ™“ÚµkÄ«W¯4Æqš2e ®®®äΛU«VÑ¢E ¯×©(P� V­Z¥¼©ž7o^ªÒ? jNNN899¡§§ÇŒ3˜5k–ònÖ¬Y8;;AéÒ¥Ù³gááátìØ€.]º°`ÁFM—.]”¯IÌ;W¹é522âÚµkܺu‹J•*Ñ«W/^¼xÁÁƒµjmS¬X1Ê”)ÃöíÛ‰ŽŽ¦T©RÊ›™iÓ¦Q @¬¬¬8uê§NRúŸ;wŽfÍš±}ûvZ·n­U\cii©œ\]]Ñ××gÕªU©Êûƒ*ãs¨õèу%K–P¢D rçÎͶmÛ”eÑÑÑLœ8‘† R²dI^½z…¿¿‚q‰R çáÇlܸ‘úõëS¨P!.^¼¨T©͞=›"EŠ`bbÂáÇñ÷÷ç·ß~KUúÔž?ΩS§X³fMª~§®$ûóÏ?©X±¢FwÛáÇJéÒ¥Ù´iõë×WŽ‘‘#GbccCáÂ…iРwîÜѸ6¤DöKò2k¿Ä—Üq®M:Ë—/ƒƒ:ubùòåJeGJ÷I¹té’Ö•&É•'m%VNS’ØyS]9ܾKéÚ¦R©=‡k»´ C[†††LŸ>'''Þ¿O™2e”.÷©UªT)ZµjÅܹsqrr",,,Ùð áÉ“'„„„pñâEråÊEÍš5¹|ù2111„……ÄÅ‹)Z´(eË–eĈŒ5Š—/_bjjÊãÇY·n£G¦xñâ¼~ýKKKúô郛›ïß¿' @©X»uë¹sç¦\¹r|øðGGG"##iÕª•FwȪU«’?~\]]iÒ¤ … ¦qãÆÜ»w… j ž‘‘‘¸¸¸àííÍöíÛ5ºi›˜˜$øJnv•ž?Ô-¸ýüü(T¨P‚®Û¾¾¾Ü¹s‡5j ¯¯Ï¡C‡°²²¢P¡BäË—/Éû¬¤4hЀ]»v)e­bÅŠ�üôÓOŒ9’ *(ãæ=Y¡B…ý¿ú«ÂYU¦¶¤‰ˆˆÀÏÏO9xïÞ½‹ŸŸ_oþ×ô!0ð.Û·oT 2{ûöÊtéÒE@…¡aAV­Z‰±qi,˜Ï™3§Y¶l)-[¶P¶ãååɇìß {ûöÊ:Y¼øWΞ=ÃÂ… 06.ÍÒ¥K”ÁSšªV­Â–-› á—_fò–Í›7Q®œ) "2òîî?*æÎ£‡­[ÿHqûC‡¡]»¶Ü¸qÕ«WÑ®][دuÄüýýh×®-S§NTôèÑvíÚF¿~}É›7^^žŒ;oï´oßw÷ŸÑÓËŽüøcÌÌ*’#‡.ÿýï+ÚµkË¿ÿÝP1qâÚµkË;·W¯^áìÙ3´nÝ*A<ôõsQ­ZUŒK*Ê”1¡W¯žøûû1{ö,æÍû/_¾`÷î]ÔªUS«ýü¤©lÙ²8p€sçÎ1|øp<xÀÔ©S•åuëÖÅÕÕ•Q£FáïïŸào===V¯^M³f͘6m ,à§Ÿ~bîܹᨋ;@ ú÷+YYáÂ…ñööÆÔÔ”©S§rôèQ<==±··Ïì¨eYÚäYéÒ¥±´´Tæ‹/ÎñãÇÑÓÓÃÉÉ oooFŽ©±Ý.]º°mÛ6Ž9‚ƒƒ«V­Ò¨ô.X° öööÌš5 777ÌÍÍYµjU¢MÖsäÈÁ¦M›ÐÑÑaèСìÝ»—Q£F¥*ñÓ fmmÍ/¿ü¯¿þÊàÁƒ5œ™={6›7ofìØ±äÉ“‡½{÷*­xŠ/αcÇÐÓÓcذalß¾… j4vssãôéÓLŸ>¨¨(ªT©Bµj‰Iy�� �IDATÕ´~+œ'O<==¹té...ý¨{õêŵk×:t(OŸ>娱cJßïB… ѰaCe\!m⚘9r°råJš5k†››óæÍSºh36Í‹/8~üx‚ñ$†N§NpuueÁ‚Ô«WOé>ñéÓ'Ê—/ÏÚµkéÒ¥ ‹/fñâÅJ7 mÃÉ™3'zzzÌ™3‡Î;sòäI>¬1´‹‹ žžž8;;óþý{Ž=šæñ«´ma_õêÕÙ¸q#‹/fÖ¬Yoå;vìÈ–-[7n¦¦¦¬]»V); 6äèÑ£øùùáääD`` Æµ!9²_R–û%1ÉçÚ¦³{÷î�ÝARº·HÊ¡C‡´n•”\yÒFRå4%É7“Ûw)]ÛÔ-ÀâŸÃµÝGÚ„‘NNNÌš5‹õë×+×(H}w[===V®\I¡B…8p ^^^tëÖ-Éõ×­[‡••Ë–-ãôéÓXYYáêê ÄÞ/ZYYáççÇ¢E‹°²²bÇŽèèè0{ölÜÜÜØ»w/ƒ bçÎôë×_~ùˆ[¨N:ʹàÅ‹XYY)¯ƒÆÊÊŠëׯsèÐ!üüüxòä 7¦^½zʤ‹ÓÊÊŠ“'Oòßÿþ—Q£Fqùòeüýý¿øk;ÊW´:uê¤öéÓ§¿hÛYIvzþ(R¤ëׯgáÂ…ÿÿñM… æþýûŒ7޾}û§§'9sæLö|‘”)S¦P¨P!úõë§ñ%Áü‘ºuëâìì¬Ñº*î=YRÿÏêtœœœTNNN ¨kó´áëëK‡´^W]>|˜`ÀX€½{÷j4µ:|ø0¹JWÁ²rê.øB¤äâ­ø0ÚW‘¸sçÎI>¥’äÙÿ?~œ&Mšš.ƒ]~m-[¶ÄÞÞž¡C‡~õ°:DŸ>}xôèQ‚ʦøeìØ±c 4ˆ[·n¥©»‚¶¾V8É™?>ÏŸ?Oò zj¼zõŠ~ø³gϦË1+û%kî—”¤æ8OÏt&ååË—/^œ;wî(oŽ3Rz–ŸŒÚw™y.ŽïéÓ§”*UŠ€€�*'Ò¢[¤Í÷rïô½¤3#=|øSSSnÞ¼©Õ—¢Òâܹs„††j=(±··wª0N0oùòå™ÛÝ©L™2ã$¶N¼¹+!„"ó9r„çÏŸS®\9˜5kóçÏ×ê¡éKÆ“H¯Nr>Ì Aƒ2-üÔýòíùéLk«¤´Ê å'+»{÷.'Nœ bÅŠäÈ‘ƒ 60|øpe¼!Ä×åãロ­í7YI*ga!„" ÑÑÑáÀ\¸pÚµk3~üx­ûN2$ÕŸìN‹¯Nr¶mÛ–ª/e&Ù/ßž¯‘Îzõê)ݾ†¬P~²2•JÅÍ›7Y²d ¥J•¢C‡ôîÝû‹¾T&„H›?âáá««ë7y f‰ƒ…B|Û7nœ­Ïýüja5mÚ”¦M›¦é·É Jœž¾V8ÉIÏnsÅŠËÐò)û%m2z¿Ä—šãüktÛÔ××ÿª_±IÏò“Qûîkž‹ã333ã×_Í´ð…ÿ£¯¯Ïõë×3;&{µ¤ÉÆ7øB!„B!„ÉÉÔ¯; !„B!„BˆXÙ¬»“´¤B!„B!Ä·)S*i¢££SýTRG#ÒNŒJÚ“ !„B!„È2¥’&***Õ¿©Wµ,çnóí Þ,2‰Ž*†ÕËs÷æÕÌŽŠB!„B‘9•4§OŸNõoîݼNQ© éI¥‚æÜ¹s™™ìAò)õ$ÏDF“2–5É~Bˆ´ù^ΟßK:EêeJ%½½}ªÖß¹s'ÖÖÖñ½;w/-H>¥žä™ÈhRƲ&Ù/B‘6ßËùó{Igv—Yi2‡B!„B!D •4B!„B!„Y@6û·B!„B!Ä·IZÒ!„B!„BdRI#„B!„B‘Hw'!„B!„Bˆ,@ZÒ!„B!„BdÒ’F!„B!„" ȶ-i.\¸À˜1c077§M›6lܸ‘ÏŸ?k¬ÂìÙ³±±±¡[·nìÞ½[cyxx8tíÚ•êÕ«ãââB```‚°Ž9€°´´dêÔ©¼|ù2Uq½sçãÆÃÜÜœaÆqõêUå?fÆŒ´lÙ’úõë3{ölÞ¼y£õö<x€££#‹/N°,¥<ˆ/**ŠÍ›7S´hQ^¿~­ÌVòÊÌÌŒîÝ»søðáãvúôiÌÍÍùûï¿•yaaalß¾~ýúQµjUÚ´iÃúõ뉌ŒLr;ïß¿GGG'Á4pà@õ|}}ù?öî<®¦üÿøëÒ6YR„(Ëh„†©¡Å6¢ìÛ YR‘Œ!ëX¢¤Œ5ÛØ5¶”ìc_²–Ý—²GT´Pß=îùuëv»…¹¯çãq:ÛçsÎùœs?ç}?ŸÏéÛ·/ÌÌÌЧO¥òX\û÷ï‡D"Aff¦Üÿ¿5Å-c$ÿºPƵk× ‘HððáC¥– B¯^½ Ÿ””„:àðáÃÅʇº™1cF{Aq½yóFá=èKôûï¿cìØ±j“Ž‹‹ BCC éÒ%¸¸¸(]¾¥rrr`nnŽmÛ¶)µ|ff&Þ¼yS¬4 3räHL™2¥Xëð¼È§êóRRÅÝÏ’Úµk¾ÿþ{dee}Öt¤ŠSN³³³ñæÍ›O’·7ÂÜܼÄë+S^?6 UHNNÆ”)Sйsçó=”¤^,¯Ž’••…ððp 6 æææèÓ§Ž;V`ÝwïÞaéÒ¥èÝ»7êׯÁƒãäÉ“ÅÜÛ‚”yîûÚ©ÓóGBB$‰øL[œúdIî_c­(*Òœ?&L€ ºvíŠ7yÑ?={öÄ÷ß9sæ sçÎ3f V­Z%.“ššŠÞ½{ãæÍ›˜8q"ìííáêêŠ7ŠËŒ1.\@Ïž=€gÏž¡K—.xùò¥¸Ì?ÿüwwwØØØ`êÔ©¸uëÜÝÝñúõk¥öïÖ­[pqqA™2e0{öl”)SÎÎÎb¡NIIA³fÍ••///xyyaûöíðööFNNŽÂmgggcóæÍhÞ¼9¶lÙR`¾2Ç ¯‡bРA0`€L€�vïÞ{÷î¡gÏž D5àì쌈ˆ¹ÛJMMÅŒ3`gg‡˜˜™yׯ_ÇŽ;Ю];,X°�mÛ¶Å´iÓPè¾Jo@+W®ÄÉ“'ŧ§§¸Ì¾}ûðË/¿ ]»vX²d ~üñG8;;ñ ê¬¸eì[§èºP¶lÌÕ²eKüóÏ?ªÎÆ'“““ƒ={öÀÖÖö‹IG„"¿÷äyøð!âââШQ#¥–ŽŽ†¾¾~‰ÒúX</…SåyùÅÝÏ’:þ<ºuë Ïß¾¸åçÑ£GÐ××ÇíÛ·?sΊVÒòªÎΜ9gggÌœ9SfzQÏÅ­+ª£¬Y³K–,.\ˆZµjÁÁÁgÏž—ILL„››:www,^¼Íš5ógÏ>jÿ•yî#ÕS¶>Y’ûÅ×VGS†J»;]¼xýúõƒñ¿ÿý>>>HKKÃСC ]¿Q£F8{ö,ªT©"NKNNFhh(†‰D‚}ûöáÕ«Wعs'tuu�ÚÚÚ˜6m\]]Q¶lYÌ™3ÆÆÆH$��KKKáìÙ³èÔ©ÒÒÒ0iÒ$,_¾={ö�ØÙÙ¡y󿨳gXä¾.]ºîîî˜:u*$ œœœ””„¥K—bùòå(_¾<Ο?5jˆëT¬XNNN˜9s&jÖ¬Yè¶ÓÓÓ±iÓ&ìÚµ ¿ÿþ{ùʃ¼Nœ8333lÞ¼½{÷–™çáá!'�øé§Ÿðúõkìܹ;v,öÇqçÎüûï¿°··—™gcc#óÅïââ‚Ê•+ÃÓÓ&L€ŽŽNíI£§VVVhذ¡Üã±eË 6 C† �8;;ãñãÇ8xð Ú¶m+wú8Å-cß:E×…* 22RÕÙ Ï !!ׯ_/ô~©ŽéXZZ–¨õcll,j×®:uê|t>7ž—¯Ï±Ÿ‚ `Ïž=ð÷÷ÿliäõ_•ÓÏ¡¤åUmÙ²^^^¸sçΟ?/N/ê9 ¸õbEu”®]»¢ÿþøî»ï��ŽŽŽˆ‰‰ADDZ´h�X±b455±qãFq9''§ÞežûHµXŸüôTÚ’æÇÄ¿ÿþ 8::â×_Åøñã±|ùr…Ñ8mmm™ �ªU«†äädq½õë×£_¿~âƒ# ¸{÷..^¼�011‘¹°õõõü@àÂ… ˆ‹‹C»víÄe*T¨€âï¿ÿ.rÿ^½z…%K–ÀÕÕUL§T©RpwwÇŠ+˜˜�2�044�¼ÿ^áöË–-‹ððp´lÙ¥J<•ʃ¼úôéTªT©À¼ü7@Ažž###qÚ¬Y³àååAðã?bíÚµ055-r[@î1¯]»6´´´�ä¶@jß¾½˜OiKy)\ºtI<n999ˆE­Zµ ]Gé—^Ÿ>}`ffWW×bwáÉÌÌÄòåËÑ©S'ØÛÛ#00©©©�€µk×¢B… xûö­¸üœ9s`nn.ÓÍ-$$Ý»w/Vºÿµâ–±o¢ëBžôôt,Z´­[·FëÖ­±råÊËœ9sƒFÆ áããS ‰wBBÆŽ+6O>tè8OÚP:-66“'OF›6m`aa‰'Êü„iÓ¦aöìÙ°µµ…““¶lÙRèý:11*T@DD €† Â××·À¯j±±±=z46lˆAƒáÌ™32ó]OÊ(*ßM›6E\\úôé‰D"v[ ¬Y³0þ|ØÙÙ¡sçÎØ¹s§Âï'A‰þýû£I“&ðõõEBB‚8ÿèÑ£°µµÅ¡C‡Ð³gOØÙÙ!%%‚ `çÎpwwGÓ¦M1}út 4{÷îUz?óº~ýºøà˜““‰D"³­û÷ïC"‘àÖ­[�r›*Ï;ööö°µµÅ¤I“Ä|aêÔ©XµjZ·n1cÆÈM�nß¾ oooXXX K—.˜;w®LSìÓ§O£ÿþ077ǨQ£dºŸ?‰D,Ê–·¼- ¤ençÎèÛ·/êׯ///<zô@nóðŸ~ú �PºtiñûHÙ²ƒ¡C‡¢~ýúøõ×_qóæMž—¯à¼H¹¸¸`ýúõ;v,,,,0pà@\ºtIá:ÒýLOOG•*U ÔgΜ "뉉‰âýØÃÃbÚñññ¸zõª4‘wyóæM‘åéèÑ£pwwÇš5kЩS'4mÚóçÏÇ»wïdòœ¿ü¸¸¸`ÕªUðòòBÆ Ñ«W/1X Ö³ÌÍÍÅô”=wOŸ>Å„ `aa®]»"**ê£ÎQþòZ’4 ó©îÓqqqº.ïÚµ«Ð`CPPúöí[ \Ôs@QõâwïÞ¡gÏžbëgEuCCC1ðä>ÏÔ¬Yiii�€×¯_cÒ¤I:t¨ÌrŸ‚2Ï}_ƒ/ùù#}�Ž9‚þýûÃÌÌL|v.ì~¡ˆ¼:ÚºuëP­Z5±ü¹Csäí‚õ¥SIF‚�---Šÿ ‚�###$''#''Gœ¦Œ3gΠsçÎ(Uª²³³qòäÉÁªU«@¡Íî¤Í®êÕ« ÷¦ndd„Š+Ê,÷ý÷ßãÌ™3ÈÎÎV˜'éØ5y�P½zu�éV•×õë×ajj …Û ÷¦  DÇ °måuþüyDGGcâĉ¸ÿ><<<�äžÓ+W® &&FìcXÔönß¾£G"$$3fÌÀÒ¥KÅu^½z…¨¨(1ŸÒÀ™´¹cß¾}.Ó5nôèѸ|ù2ºu놈ˆøøø bÅŠZ¥T©R¸xñ"\\\Œ6mÚÀÕÕUéñCA€ŸŸ¢¢¢àãã???ìܹÇGNNZ·näädñ&"(..N¦yixx8ÜÝÝ‹•÷ÿRI¯³o2×[Ž|||pèÐ!Lš4 &L(ðEvêÔ)üüóÏèÚµ+V®\ CCCtïÞÉÉÉâ2§OŸF:u‚&MšÀÑѱÐ_=z„²eËÂ××þþþ¸sç<<<dîÃÁÁÁ(W®Я_?ôîÝ[æK9¿äädÌŸ?=zôÀÂ… 777¤§§È дlÙæææ†©©)lllpúôiñ8(ºž”¥(ßëׯGíÚµ€«W¯ÂÁÁA\oòäÉÈÊʬY³Ð¥K 0�ááá…¦³eËŒ7nnn DRRÚ¶m+åÜsòÇ K—.ðññAùòå±nÝ:Lš4 ½{÷ÆüùóñêÕ+¬]»VéýËïÂ… Åê1vìX\½z3gÎĬY³P£F ”/_^œ?cÆ üûï¿ðôôD=ä¦óæÍüôÓO¨P¡BCCááá*Uª@[[[\þìÙ³èÚµ+–,Y‚ììlØÙÙɱò+ª¼ ‚€ˆˆØØØˆÓ’““±qãFôïß¡¡¡xþü9†ެ¬,XYYaÉ’%�€Ë—/ãêÕ«2ë)*«W®\½½=7nŒeË–¡Aƒ8räˆRÇWÞñRÏËs^òúý÷ßaaaÅ‹ÃØØvvvrÇ+Ì¿ŸåÊ•ƒ‡‡‡L“üÌÌL¬Y³nnnJ×-ѨQ#øûû£víÚ�ä·Ö‘wQÆÎ;‡qãÆaúô騰aƒxì¥ä•Óàà`ØØØ 44fffèܹ3?~ CCCñôððp\½zU¬¿uîRRRо}{dggcÑ¢EèÕ«Ž?^ä>ç•4 y>õ}ZYŠê ŠæU/NOOǹsçdº([GIKKÑ#GФI�À“'O�äŽfÏž ;;;¸»»—8 V”¼Ï}_‹¯éù#&&mÛ¶…““Ö®] WWW*¼_F^ÍÙÙOŸ>ŹsçÄåŽ=ŠvíÚ}‘-�å9r¤pýúõŸ×¯_+ýÙ¶m›••¥Ô'22RHKK+ôó믿 Ç—™¶mÛ6A‘ .�„˜˜A!))I� DFFX¶nݺBHHHéÙÙÙÂàÁƒ…!C†ˆÓ/^,XYYXv÷îÝ�!99Ya¾Nœ8!�d¦ß¿_� œ>}ºÀ:IIIB½zõ„5kÖ(Üv~Ÿþ)³â©ƒ �„—/_˜× A€�@èß¿¿ðäÉqÞû÷ï…ôôt™åŸ>}*�Ž?^`[þþþâ¶,--… .ÈÌÏ{|ããã…5kÖ‘‘‘BTT”0wî\AOOO˜?¾¸Lvv¶0oÞ<q›�„ǺŸRòÎC~ýúõæÎ+‚ DDD�„ŒŒ ¹ÿÇÄĺººÂÓ§OÅõoÞ¼)�N:%‚ téÒEÜ^ll¬`jj*Œ1Bð÷÷A>|(�âãã‹ÌÛ%ÿqú˜2ö­(¬l)º.¤.]º$�?~\`ÚƒAÈ-Gaaaâü¬¬,¡AƒBtt´ ‚(ôèÑCf»Ó¦MZµj%‚ ¼}ûV� <xPnÎ;'�Ër`` Ð³gO™e† "Lš4Iîú/^¼)÷‚ ‰‰‰BÅŠ…½{÷ ‚ ƒdÖ;v¬àîî.‚r×ÓôéÓ…¡C‡Ê̓²ùnР°iÓ¦ëIó!õçŸ 7rrr ¤“šš*Ô¬YS8tè8---M°´´–.]*‚ 9r¤À÷ÂÛ·o###!**Jœ–‘‘!�öìÙSè~ ‚ü2–““#XYY‰ßÙÙÙ¶uïÞ=€póæMáÇ‚®®®°víZ¹i ŽŽŽÂû÷ï¦sýúu€pùòe¹Ûqvv–¹/¼ÿ^pppæÌ™#BÉÊ[BB‚¸‚ðÿe.ïq‘^7÷ïßAöìÙ#�²³³Åe”)«C† fÍšU ?Òûv^</êy^Šâìì,^«‚{,»uë&L™2Eîòù÷Sz¬=z$Bn9022Þ¾}+wý¼u é¾ÊûN1c†0vìXñy÷‘¢ÊSÞõò֯„zõê‰÷´üåGz\ò–‘ôôtAWWWؽ{· ‚ðàÁ€+.£Ì¹ Úµk'|øðA&? 4{¼¤yQtŽò—×’¤!ÏÇܧó‹•ù.AøçŸ� ×›;w®Ð©S'¹óò?‚rõâÔÔT™kN”«£¬\¹R¨[·®X–¤e €"8p@˜4i’�@8zô¨Âýʯ¨zyþç¾/Õ—üü/ó½’¿>¹qãF¡f͚»wï ¬+ï~Qyu´Q£F ãÇÿïÔ©“°aÃ¥·©¬Ó§O ‘‘‘JÇ:¶mÛV¬8м8ÌÈ‘#•„³²²ä~.\¸€+V`À€2Óyôè† †Ù³g‹Ñ\é8y›s¹Íü=z$v'Ê+$$GŽ‘é﫯¯hÍóîÝ;T®\YLçÎ;ˆ‹‹?ñññ� þª‘·)�ñi÷*©ŒŒ Œ7õë×—y#Ë“'Od¶ãÆ …Ǥ¤Ç@±±±xûö-bbbÄf’ÒýÓÔÔTØ)¿3f ==wïÞ…³³3Zµj%óKHÞ_…LLL0hÐ ¸¸¸ÀÙÙ&LÀŸþ‰ñãÇ‹MôæÏŸýû÷ãÂ… xøð!ѹsg„„„{?Ÿ<y‚eË–¡wïÞ°¶¶FxxxæÀ…¹}û6š5k&¶(�333Ô«WwïÞ�ôêÕ [·nENNΟ?¾}ûÂÕÕ[¶lÁ‡ÄæÓÊ´¦R•ÏUÆ(×;w`ee…jÕª‰ÓJ—.-þ‘‘Ý»wcРAâÛÎ444‡””q¹üͧ[µj…ãÇË´¶‘ĸqãСCñ>¤èÍFFFE¾.o*Uª;;;ܽ{YYYصk,--e–wppÀþýû‘™™©ÔõTÊä=æ�ЬY3\¾|Yîº xøð!4h Nûî»ïбcÇÍoóÞßðôéS™õò§[?ƹsç”þ5ICC1bF½{÷¸ß@SSSa:¦¦¦øå—_àêêŠiÓ¦áÔ©S [œjjj¢S§N¸pá‚Òû–ÿ¼)3H… �¼WÉSXYÍÉÉADDš6m*³¼´‹®2x^d©ê¼Vg“—–D"A›6m í¾›?›4i‚fÍšáÔ©S�rß:|øp”)S€ru ===™ÿAÀ¾}ûdZ%I)ÛzFܼyS,Ê”SÔ¨QC滦0…; ·”ƒƒƒL‹eZ™ç7ÂêÜ%¹OUÖ>7eêÅeË–-vk”'N`̘1Ë`¹rå�äŽU8bÄqlÍ>}ú`ݺuŸlŸä=÷}MÔíù£¤eØÞÞÕªUƒ££#/^¬TÔâ¦Õ½{w¬^½)))¸wïöíÛGGG¥ò÷%Piw§¼ŸÇÃÓÓS¦LAÆ eææåË—8p ~øá™×jjj¢U«V¸ÿ¾Ìò‰‰‰HKK+ÐýhÆ ðôôÄêÕ«eê­Zµ*^¼xQà•Û °±±oj£G†¹¹¹ø ðÿcËHû\KI»T®\YœöáÃLœ8'OžÄâÅ‹eúsnÚ´Ifûy›â¦¸Ç 8Ê”)ƒ&Mš`öìÙ8qâ._¾\âméèè N:˜1cªU«†(½®ô朘˜ˆçÏŸã·ß~ƒ··7š6mŠ5j`ܸq Á¨Q£ŠõJó¤¤$qàh???lݺUákŒóËÈÈûš¸òåË‹ÇV­Z!&&wîÜATTZ·næÍ›ãöíÛ¸yó&¢££Õº«ðyËåŽI¥èuƒZZZ022ÂâÅ‹/óQ4PŸôN^%uÍš5˜>}::w *õæ„’ ا¡¡dggãÕ«Wµ´´P¶lY”.]Z©ë©$J:Рô¾//mi¿ÿü?.hkk+ì£/]þS½‘$..¦¦¦}ŸŽ=wïÞ… V¯^ KKKÄÆÆ+mmm¬Zµ ¨T©|||Ð¥K…¯UÖÒÒ*ÖøùÏÛÅ‹áææ¦ð¡ëc•Ì[V“““‹ìæ¬Ï‹âuŠãcÎKau¶Â”.]Z|�Í/ÿ~jhh`È!صk233†N:(yÝâéÓ§8{ö¬ÒoRTžäÉ +§ù•´›‰ôܹõ¥OñÚnE稸iVç.É}º¨²VÜsUŸ²^œ×åË—áîîŽ3fÈÔ5¤Ï4y¯y‰D‚-Z(õã²2 {îûZ¨ãóGqï—R&&&8vìñæÍ´mÛ'N,òû®8iÙØØÀÐЧOŸÆáÇ1zôèc}ÉÔ¢#ß«W¯0lØ0Ô©S£GVj”” 6 ÚÚÚ ‘égܤ„�� �IDAT[ �ĺuëd—<vì5j$ó‹ËÞ½{1`À�lÛ¶­@ðÃÊÊ 5kÖ” ¤¥¥aóæÍèÛ·¯8-**J&¨4oÞ<�¹Až`ÇŽâM]ìÝ»ÞÞÞbKšœœÌ™3Û·oÇŽ;`ll,“qãÆÉlÿùóçJ#e2ä½]z#–þB”••¥ðwEÛ’H$ÐÑÑ‘ù’Í9–÷¥xïÞ=èêêÂØØX¬ å¯4HƒnE½Ö=¯7nàòåË8p ,--Q£Fb= šššâܹsøßÿþ'N{òä Î;'ŽwdllŒ^½z!<<»víBÓ¦Ma``€!C†`ß¾}ؼy3ììì”NSU>e£Üû‹ô ìûï¿ÇÕ«WqïÞ=¹Ë–*U ;wÆÅ‹all ñ“÷:ÊM^¾|:u¯Û¼vïÞ 777´iÓõêÕ“;ˆxqöAêÕ«WâßÉÉÉ8uêêÕ«mmm¸ººàâÅ‹èÒ¥ 444”ºžäQö—'©R¥JɬýÅ‹2Ó¯]»ñçÝ_èêêÊôíÎÊʱcÇþâgll ===™õ>¦òWWWñ]ªT)üøãEVÊŒŒÐ¯_?lݺ5jÔÀþýû‹•{/oРFððpDDDÈ ê™÷aIœ8qÍš5+ÉnÈpV^ E¤¥òÜ +«šššèСC²Zœ6žÅþ«óRXM*ëÔ©ShÞ¼¹ÜmÉÛO'''lܸ{÷îE… иqc�%¯[Hƒ&ßÿ½Âå”-OE‘W~”%¯®UعrªŒŒ”ùžÊï“W·,Î9R&¼ «s—ä>]XY“~ÿÊkÑú©([/–÷]˜;wî G<x0¼¼¼dæÃÝÝÿüóÌöâããaeeUâý*ê¹ïk ŽÏEÝ/ÑÔÔ„­­-¦L™‚°°0Ì;Wf|¾ü÷ EiÉ«£ikkÃÃÃáááX·n]ÁŽóÖ û[©ôÜ�ššŠ1cÆ@[[ ,€––V‘7‹ŒŒ Œ3áááØ¾}»Ì€W5jÔ@µjÕЭ[7¬Zµ ÇÇÀñâÅ Œ;+W®#øÿþû/zõêwww˼QÄÚÚåË—ÇŒ3ÄQùk×®-[¶ÀÐÐ:tPj_}||àää}}}888àìٳعs§Ì@ZÁÁÁ˜:u*æÌ™ƒ·oߊùÐÓÓCýúõnÿâÅ‹ÈÉÉAJJ >|ˆóçÏ£R¥J¨]»¶RÇ ¯gÏž!!!AœôÒ¥KÐÓÓC£F0|øpT®\-Z´@ÅŠñæÍ¬X±C‡Eƒ  €çÏŸ#** ïß¿G\\œøå|ãÆ èèèàûï¿Ç?ÿüƒ#GŽ C‡022‡°gÏèèèÀÅÅ@î€`íÚµÃöíÛÑ¡C,_¾wïÞ…ôôôpÿþ}Ìœ9Ë–-C™2e «« ///øøøàÅ‹¨]»6°fÍŒ;¶X‘UéÀÎK–,““âââ°~ýzŒ7Àÿwó9{ö,ÌÍÍ üߢE ôèÑC|[™¦¦&‚ƒƒáåå 1wwwôìÙC‡›7wèÐÝ»wÇO?ý¤ðõëꢸeì[÷îÝ»B¯ �hÞ¼9 „)S¦ÀÊÊ îîî:t(üüü ­­+VÈlÏÇÇÍš5C­ZµÐ¥K¤¤¤àÌ™3;v¬øÅ¾k×.Ì›7¶¶¶¸}û6&Ož\èC^£F°aÃüðÃÐÑÑÁòåË‹µ¯^½’Ù)OOO$''ÃØØëׯ‡­­-Z¶l �ðõõ…½½= `mmëׯcÞ¼y8yò$�(u=ééé!::—/_Fƒ °zõjL:gÏžUúín-[¶Ä®]»`nnŽråÊ¡nݺ�r¢óõõE×®]ñìÙ3øûûcûöír÷×ÀÀ�³gÏÆ¨Q£––cccìÙ³©©©pss+4í *`ÆŒ9r$Þ½{WàÇ⊊Š*ðƒG¿~ý°xñbT­Z:::⬀ÜÊüĉagg‡jÕª!11—.]’y[2é<xð�ëÖ­ƒ­­-ôõõqþüyèêêÊœƒ9sæ bÅŠ¨Q£¢££qéÒ%,]º´DûùìÙ3œ8qB©_yI;ߺu+êÖ­+>DŠËª···XV[¶l‰[·nÉ|7…çE1U—ü¦OŸŽòåËÃÊÊ 'NœÀ‰'¨ô~Ö©SîîîèÞ½;BBBÄ`GQu‹Â\¸pAé ‰¢ò¤,yå´(†††¨Y³&¶oߎììlT¯^] (:wîîî˜3gÆŒƒ¾}û"%%AAAâvó×-¥-A•=Gʤ¡¬OyŸ®^½:Ú·o€€�Œ9)))X¶lY¡Ëß¹soÞ¼Á£GðæÍœ?ZZZ°°°(ô9 V­ZEÖ‹ó‡)ª£¤§§£wïÞÈÈÈ@ûöíeºCJ¿7ýüüЦMÀÁÁwïÞÅ‚ d/ ežû¾_Òó‡ô­®111Ð××/ð’ƒâÖ­[hÔ¨´µµqàÀXYYA__eË–-p¿(ê9­°:ZÇŽáíí SSSX[[‹Ëç}ŽÔ××—û·²Ïòª¢’ TFF&L˜€½{÷bíÚµ2ã T¯^½Ðîqqqâhêù£f[·nE=```€ððp,^¼þþþ077ÇæÍ›eúª­Zµ iiiرcvìØ!³i hàÀ(_¾<¶lÙ‚°°0tìØóçÏ/´Ye~Mš4Att4V®\‰ñãÇ£M›6ˆŠŠ‚™™€Üñi¤Ñè‰'ʬëãム(Ü~=ĈiLL .\ˆÀÀ@Œ7N©c—ôíRÒfŒ/^¼€——öìÙƒÕ«WãÆ°´´DŸ>}àææ&~i6hÐ�†††ÐÐÐ@BB‚Lä|øðá�r_Çæää„ÌÌLìÞ½111¨S§Úµk‡É“'‹©¾¾>ìììÄ.c­ZµBbb"–.]ŠGÁÖÖaaahݺ5€Ü_àæÌ™ƒ¦M›"<<×®]CÆ 1xð`™7^(£V­ZˆŒŒÄ‚ °{÷n´oßþþþâØ;-Z´€ŸŸ|||€V­ZÉüïè舕+WbéÒ¥˜>}:ôõõÑ­[7 0@&{{{�S�ÄLqó¬*Å-cߺçÏŸz]4oÞ–––âÛ<J—.•+WbÉ’%˜2e ŒŒŒÐ¿ñ­G�P¿~}\¼x!!!øå—_```�'''dddˆAܾ}kÖ¬ ¢¢¢Ð¢E ¹ùóööFff&Æ333ôéÓGfäü¢èèèÈ샔››6n܈—/_ÂÕÕAAA⇎?Ž¿þúK¼¦OŸ>-¨544мžzôè“'ObìØ±XµjŒÑ¼ys¹­… 3yòdL›6 ƒ†‡‡‡X°´´D™2eÄJÎöíÛÅkWÞþŽ5 +VĆ ðòåKtèÐ{÷î-ò;cäÈ‘(_¾<šš*Þ‹ó7R”çÏŸãèÑ£øë¯¿d¦{zzŠo~¨Y³&zõê%¾1âýû÷¨S§V¯^«W¯¢nݺX´h‘ØMCÙt455¡¡¡¹sçâþýû°¶¶Ftt´Ìñ3f 6oÞŒ‡ÂÉÉ ‡.ñøUʶ0ȯaÆX·n-Z„êÕ«cÉ’%bP¹¨²zøðaüõ×_ظq#:vì(óÝ ÏKÑTq^ä0`�®^½Š5kÖ M›68räˆÌÊìgß¾}±cÇ™î EÕ- sàÀŒ5J©¼+*OÊ(¬œEWW›7oÆôéÓqäÈÌš5K»Eѹ«R¥ Ž=Š `äÈ‘hÑ¢¼½½Å_Ð¥-À¤uK)eÏ‘2iǧºOkhh`ùòå˜;w.~ýõWXYY¡OŸ>â[oò[³f æÌ™#þoeegggDEE)|(ª^œÿ;LQåþýûb+¢ü=nÞ¼ 333XYYáøñãX±b|||`mmK—.}ôÛv”yîû|IÏ+VDXX˜Øiذa2ó pïÞ=¬_¿©©©pvvÆæÍ›¡©© MMÍ÷‹¢‚4…ÕÑ~øá´hѽ{÷–i]•÷9²\¹rrÿVw’‘#G #GŽ,0CÍSÆÁƒÑ­[7¥—•„+W®ˆÙù………‰7¿èèèb¿¯HYgΜ‘‰¾’|<NÅ÷­³ÄÄDT®\§OŸþ"CPPÎ;‡-[¶üçi?yòÕ«WG\\œÂ•ùËØ‘#G0lØ0ܸq£DÝ”õ_¥£H`` ž={Vè/èÅñ©Ë*Ï‹zž—¢¸¸¸ k×®1bD‘Ë~Êý,Ì‹/P¥JܺuK|(ùœ>eùù\ç®8çèsSö>MÅó­Ô¾•ýüœ<x€Úµk#66VfPïOéÌ™3HNNVúèðððbýXýøñãÓBBBTÛÝ©Q£Fxýúu‘Ë}Ínß¾cÇŽ¡nݺ(]º4Ö®] OOO…ãîÈó1ãI¨c:ŠDGGøõN]ñ¼|}þ‹ý,i«¤’R‡ò£Î>Õ}šˆ>ˆˆ8::~•ARÞ…‰ˆˆTLÄÆÆbñâŨ^½:ºuë†û 8õúnuKG‘mÛ¶ë DªÄóòõù/öÓÆÆ—.]úÏ‚&êP~ÔÙ§ºOÑÇËÌÌDhh(üüü¾ÊkPåÑ×ÉÐÐð‹¾ß—tàÑ’033ßþùÑÛ)Î8<_B:Š”/_þ“mës—Už—’ù¯ï!y_êP”O¹Ÿ…ÑÖÖþOßbó)ËÏç:wÅ9GŸÚ§ºOÑÇÓÖÖÆµk×TÏF-^ÁMDDDDDDDô­c†ˆˆˆˆˆˆˆH °»‘PI&;;[É©-•i²²²T‘,‘ÚRIæäÉ“Å^çÌ™3Ÿ!'D¹X¾”ÃãT|<fô¹±Œ©'ž"¢’ùVîŸßÊ~Rñ©$HÓ­[·b-kkëÏ”úÖ9s†åK <NÅÇcFŸ˘zây!"*™oåþù­ìç—NU4¾Ý‰ˆˆˆˆˆˆˆH 0HCDDDDDDD¤¤!"""""""R Ò©• ,‚*’%"""""""R[lICDDDDDDD¤¤!"""""""R Ò©iˆˆˆˆˆˆˆˆÔ�ƒ4DDDDDDDDj€A""""""""5À ‘`†ˆˆˆˆˆˆˆH 0HCDDDDDDD¤¤!"""""""R Ò©iˆˆˆˆˆˆˆˆÔ�ƒ4DDDDDDDDj€A""""""""5À ‘`†ˆˆˆˆˆˆˆH 0HCDDDDDDD¤¤!"""""""R Ò©iˆˆˆˆˆˆˆˆÔ�ƒ4DDDDDDDDj€A""""""""5À ‘`†ˆˆˆˆˆˆˆH 0HCDDDDDDD¤¤!"""""""R Ò©iˆˆˆˆˆˆˆˆÔ€†*AÉ©-¶¤!""""""RSû÷ï‡D"Aff¦ÜÿÿK H$¸rå � )) :tÀáÇ‹\7;;oÞ¼AVVÖçÎæA"""""""*e{Ê<zôúúú¸}ûögÎÑ—M%݈ˆˆˆˆˆˆèËf``€ÈÈHUgã«Â–4DDDDDDD*’žžŽM›6¡OŸ>033ƒ««+vïÞ]¬mdffbùòåèÔ©ìííˆÔÔT�ÀÚµkQ¡B¼}ûV\~Μ9077GRR’8-$$Ý»w/VºïÞ½ƒD"Á¡C‡ÄiGŽAÿþýaff†âï¿ÿFBBjÕª�077‡D"Á­[·Š•Ö·‚A""""""")Uª.^¼£M›6puuÅ¿ÿþ«Ôú‚ ÀÏÏQQQðññŸŸvî܉áÇ#''­[·Frr²8ŽLVV6lØ€¸¸8ÄÄÄˆÛ ‡»»ûGíKLL Ú¶m '''¬]»®®®044„¡¡¡Øâ&<<W¯^…‰‰ÉG¥õµbw'"""""""ÑÖÖF`` ø¿““.\¸€S§NÁÞÞ¾Èõ/_¾Œ•+WâÞ½{¨Zµ*� nݺ¨W¯<==accƒ.]ºàĉhÙ²%nß¾÷ïßcĈ8~ü8ƒbõêÕµ/7oÞDÍš5Ñ£GèêêÊÌ«W¯ž˜· |T:_3¶¤!""""""R¡'Ož`Ù²eèÝ»7¬­­ŽwïÞ)µîí۷ѬY31@�fff¨W¯îÞ½ �èÕ«¶nÝŠœœœ?}ûö…««+¶lÙ‚>àüùóèÖ­›ØºåÎ;ˆ‹‹?ñññJåÅÞÞÕªUƒ££#/^Œ›7o¹NIÓúZ1HCDDDDDD¤"IIIèÔ©ÒÒÒàç燭[·¢W¯^J¯Ÿ‘‘ŒŒŒÓË—/---�@«V­ƒ;wî ** ­[·FóæÍqûömܼyÑÑÑ2]F sssñ¬T^LLLpìØ1âÍ›7hÛ¶-&Nœ¨ð P%MëkÅîNDDDDDDD*rãÆ \¾|ÑÑѨT©�ˆÁe˜ššâܹsøßÿþ‡ÚµkÈm™sîÜ9±‹‘±±1zõê…ððpìÚµ ¡¡¡ÐÓÓÃ!C°oß>lÞ¼'N·UâýÑÔÔ„­­-lmmamm øøøˆó?|ø ³üǤõ5b†ˆˆˆˆˆˆHEªW¯�X²d œœœ‡õë×cܸq�€²eË�Ξ= ssóÿ·hÑ=zôÀ¯¿þŠñãÇCSSÁÁÁðòò‚………˜Ž»»;zö쉡C‡BOO�СCtïÞ?ýôjÖ¬Yd^¥ãÌÄÄÄ@__+V”™ðàAܺu 5‚¶¶68�+++èëë£lÙ²¨Y³&¶oߎììlT¯^UªTùÈ£÷õaw'"""""""©U«"##qæÌxzzâÿûüýýÅù-Z´€ŸŸ|||péÒ¥ÿkhh`åÊ•h×®¦OŸŽ   tìØ2éH!vqq§Y[[�zôè¡T^+V¬ˆ°°0,X°�;vì(0ßÀÀ�÷îÝÃøñãñË/¿ ;;›7o†¦¦&tuu±yóf\¸pcÆŒQj¼šo[Ò©‹‹‹Lð$/--­—üÿ—/_'N”é²”_ÕªU Œ S½zu…ãÅÈóóÏ?ãçŸÿÏ»¾¥¥%,-- ]×ÚÚû÷ï/Vzß¶¤!"""""""R Ò©iˆˆˆˆˆˆˆˆÔ�ƒ4DDDDDDDDj€A""""""""5À ‘`†ˆˆˆˆˆˆˆH 0HCDDDDDD¤BïÞ½Cjjªª³Q¤¤¤$tèЇþ¬éìß¿‰™™™Ÿ5uÄ ‘ `Ò¤IªÎ†RAPu¾jªÎ�©?DFFª:_5¶¤!""""""R‘3f`æÌ™X²d $ ÆŽ‹uëÖ¡ZµjHKK—{üø1$ ®\¹‚£G¢gϞظq#:wî kkküùçŸHOO—ÏÉÉÁ–-[еkWX[[cþüùJw©:räú÷ï333 8ÿý7€ÜnY‰‡�=zÝ»wÇÊ•+Ñ¡CØÚÚ"((Ïž=âE‹àää„6mÚ`Æ ÈÉÉ�$&&¢B… ˆˆˆÀ€аaCøúúâÙ³g ó”�___XXX oß¾8sæL±Žó—‚A"""""""4h† †>}úàêÕ«ðôô„³³3ž>}ŠsçΉË=zíÚµCÆ �Û¶mÃÑ£Gáííß~û kÖ¬ÁÔ©SÅåCCCñ÷ßÃÛÛ¸~ý:~ûí·"»+ÅÄÄ mÛ¶prrÂÚµkáêê CCÃB—ß±cîܹ???øùùaÙ²e022BFF&OžŒ¡C‡bøðá2-p’““1þ|ôèÑ .D||<ÜÜÜd‚Ly%&&¢mÛ¶¨[·.V¬XÎ;£}ûö¸víšRÇøKÂîNDDDDDDD*R£F T©RÚÚÚb��F…ˆˆ888��6mÚ„_~ù¥Jý[‹ÀÀ@èéé�*Uª{{{Œ5 åÊ•ƒŸŸN: �€‘‘LMMáïïªU«šŸ›7o¢fÍšèÑ£tuu•Ú‡I“&¡|ùò�€`ÇŽ˜0a‚8ÿÌ™38tè:vì(N›={6lll��7F½zõpøðatêÔ©Àö7mÚ 6 �`ee…k×®aß¾}2ÇìkÀ–4DDDDDDDj¦{÷îX½z5RRRpïÞ=ìÛ·ŽŽŽ2ËH$ñosss�@||<>|ˆ´´44n܉‰¦¦¦� vyºsçâââÄO||<�ÀÞÞÕªUƒ££#/^Œ›7o+ß•*UBRR’Ì´ªU«âÕ«W…æ½R¥J°³³ÃÝ»wånóòåËX¹r¥¸/‰sçÎÓyòä‰Ì¾Ü¸q£XyV'lICDDDDDD¤flll`hhˆÓ§O#>>£GF•*U ]^ÚÂF[[[l 333™å¤­hFˆÓÇyóæÁÄÄÇŽÃùóçqðàA´mÛ?ÿü3fÏž­T¾ó_MËOCC:::rçéëëcàÀøã?d¦÷Ýw�r[ÚøúúŠÓ+W®ŒçÏŸ+•_uà ‘ I$dddÈLÓÖÖ†‡‡ÂÃÃ[ @�¯_¿»ݾ}ººº¨U«Ê”)ƒÚµkãÁƒZßHEEEšMMMØÚÚÂÖÖÖÖÖpqqÊ”)ó{)+oËšäädœ:u žžžr—µ°°ÀÌ™3Q®\9T¨P¡ÀüqãÆaܸqŸ,oª¤’ ß«NDDDDDD”ë‡~ÀŠ+pâÄ |÷ÝwhÚ´)� cÇŽðöö†©©)¬­­ ¬7räHŒ9˜9s&fϞʕ+�æÎ‹aÆASSMš4ÁÇ‘˜˜ˆÁƒ+ÌËÁƒqëÖ-4jÔÚÚÚ8pà�¬¬¬ ¯¯÷ïß²}öôôDrr2Œ±~ýzØÚÚ¢eË–�€²eË�Ξ= sss¸¹¹!$$#FŒ€§§'455qîÜ9´oßuêÔùdyRlICDDDDDD¤Bnnn¸wï†[[[,[¶ ¥K—Æ?ü€-Z wïÞÐÖÖ.°žµµ5fÍš=== >}ûöçõìÙúúúX¿~=þøãÔ¯_®®®EæÅÀÀ�÷îÝÃúõ둚š ggglÞ¼šššŸ4Hãææ†7âåË—puuEPP44rC-Z´€ŸŸ|||�GGGìÛ·¡¡¡˜8q"Þ¾}‹V­Z}• @¤!""""""R!]]]L:UæÚ@Ξ=‹Õ«WË]ÏËË þþþ…n×ÉÉ NNNÅÊ‹¥¥%,--åÎ+S¦ŒL`ÄÁÁ¡@ ¤_¿~èׯŸÌ´ßÿ½À¶ºwï.·u�hii! @fZÅŠ1yòdLž<Y©ýøRñíNDDDDDDDj(""ŽŽŽ¨_¿¾ª³BÿiˆˆˆˆˆˆˆÔLff&BCC1pà@¥ÞŽD_vw"""""""R3ÚÚÚ¸víšÜyòº}) ¿Ø¼ÿØ’†ˆˆˆˆˆˆˆH 0HCDDDDDDD¤¤!"""""""R Ò©iˆˆˆˆˆˆˆè“ظq#ÌÍÍU/ƒ4DDDDDDDDj€A""""""""5 ¡ê }ËA@TT6lØ€ØØX´k×^^^011�!##ZZZصk*T¨€Áƒ£[·nH$�€œœlÛ¶ ›6m³gÏàîî”+W‰‰‰¨\¹2<ˆmÛ¶áôéÓ ƒ––6mÚ„“'O")) ;v„——ªV­ �pqqAÏž=qõêU>| 4€¯¯/š7o.æýéÓ§øóÏ?‰Úµk£|ùòÿýüа% ‘ mÙ²ãÆƒ››‘””„¶mÛ"11Q\fòäÉÈÊʬY³Ð¥K 0�áááâüÐÐPüý÷ßðööF`` ®_¿Žß~û ‚ ˆË8::¢Q£Fð÷÷GíÚµñèÑ#”-[¾¾¾ð÷÷Ç;wàáá!³Npp0lll 333tîÜ?�¤¤¤ }ûöÈÎÎÆ¢E‹Ð«W/?~ü?8b_/¶¤!""""""R‘·oßâ·ß~ÃêիѶm[�€­­-ììì°mÛ6Œ9�àî'�Z·nôôtÌœ9ݺuÃëׯáçç‡S§NÁÂÂ�`ddSSSøûû£téÒ�€ÈÈH¸¸¸ˆi»¸¸Èü_³fMXYYáùóçbkùn¡ö�� �IDATšaÆ¡wïÞ�€fÍš!((111¨^½:ÂÃÃahhˆ¹sçBC#7¼ðþý{Ì›7ïs²¯ƒ4DDDDDDD*’€‡¢Aƒâ´ï¾û;vÄ•+WÄiÒ@‹T³fÍàíí¤¤$ÄÇÇ#-- 7.°ýÔÔTT¨P� §§'3O:tû÷ïG\\nݺ�ÈÌÌ”›WÔ¨Q)))�€+W®ÀÁÁA Ð�ù�ž<y‚7oÞˆÿK$Ô¯_¿ðòc†ˆˆˆˆˆˆHEÞ¿�ÈÊÊ’™®­­ï¾û®ÐõJ•ʽDKK ººº�€èèh˜™™É,WµjU™ I^kÖ¬Áš5k0cÆ üúë¯xúô©Øš§¨t ##£@¾óÛ´i|}}Åÿ+W®ŒçÏŸ+\ç[Æ1iˆˆˆˆˆˆˆTÄÄĺººˆ‰‰§eeeáØ±chÒ¤‰8íÅ‹b@�®]»”+WÆÆÆ¨]»6<x�™¦¦f¡iïÞ½nnnhÓ¦ êÕ«‡J•*+ï?þø#"##eZÞäÏ�ÆAÄ4б% ‘Š`öìÙ5jÒÒÒ`llŒ={ö 55nnnârG…¯¯/ºvíŠgÏžÁßßÛ·o�”)SsçÎŰaà©©‰&MšàáÇHLLÄàÁƒ M»Q£Fذa~øáèèè`ùòåÅÊ»»»;æÌ™ƒ1cÆ oß¾HIIAPPPÉ`†ˆˆˆˆˆˆH¥F…Š+bÆ xùò%:t耽{÷¢\¹râ2–––(S¦ ¼¼¼`aaíÛ·ÃÞÞ^œß³gOèëëcýúõøã?P¿~}¸ºº*L×ÛÛ™™™?~<ÌÌÌЧOœ;wNé|W©RGÅ‚ 0räH´hÑÞÞÞ8ø#0HCDDDDDD¤Bèß¿?ú÷ï_è2¦¦¦˜3gæÌ™Sè2NNNprr*0ÝÐа@7$ ·O@@�Äi½zõÿŽŠŠ*°Nll¬ÌÿuêÔÁÒ¥Ke¦ýüóÏ…æ‘ã˜4DDDDDDDDj€A""""""""5ÀîNDDDDDDDjlܸqªÎýGØ’†ˆˆˆˆˆˆˆH 0HCDDDDDDD¤¤!"""""""R Ò©•iA(Ö‡ˆˆˆˆˆˆè[‘••‰D‚½{÷~ô¶233ñæÍ›O+õLïkÖ4DDDDDDD_©èèhèëë#''ç«Lïkà ‘`†ˆˆˆˆˆˆH…AÀÎ;áîî Lž<¹À2 ðõõ………úöí‹3gΈ󒒒0wî\ØÛÛÃÖÖ“&MBBBöïߟ~ú �PºtiH$�@bb"*T¨€ˆˆ 0� 6„¯¯/ž={&“fll,F† bРA2i&&&B"‘àСCððð€……‚ƒƒå¦GÊc†ˆˆˆˆˆˆH…Ö¯_I“&¡wïÞX¸p!ÒÒÒdæ'&&¢mÛ¶¨[·.V¬XÎ;£}ûö¸ví�`ìØ±¸zõ*fΜ‰Y³f¡F(_¾<¬¬¬°dÉ�ÀåË—qõêUq›ÉÉɘ?>zôè… ">>nnnHOO iÙ²%ÌÍÍ SSSØØØàôéÓ2ysttD£FàïïŽ;š)GCÕ """"""úV½{÷“&MªU«àìì �hÙ²¥ì�€M›6ÁÁÁÆ �XYYáÚµkØ·oêׯmÛ¶!44�€6mÚˆëÖªU �аaC”*%ÛNcöìÙ°±±�4nÜõêÕÃáÇѩS',X°�¿ÿþ;FŒ�hݺ5^¿~   lß¾]ÜFdd$\\\”JŠÆ#FDDDDDD¤"=£GРAqZéÒ¥e–¹|ù2V®\ ‰D"~æÎ‹¤¤$hhh �#FŒÀèÑ£±wï^¼{÷N©´óvGªT©ìììp÷î]deea×®]°´´”YÞÁÁû÷ïGff¦8MOO¯Èt>|ø€¸¸8™Ï«W¯”Êã·†A"""""""yÿþ=�(|’¾¾>ˆøøx™ŸŸ�`ôèѸ{÷.lll°zõjXZZ"66¶ØyÑÐÐ€ŽŽ²³³ñêÕ+dggËÌ×ÒÒBÙ²e ‘ŠòæÍ˜››Ë|Ž;Vìü} ¤!""""""Rèêê"&&Fœ&‚Ì28uêÊ•+ñS©R%q###ôë×[·nE5°ÿ~�ÿßZ&++«@Úy[³$''ãÔ©S¨W¯´µµáêêŠãÇË,ñâEtéÒ…œ"/=CCC‚ óqss+òØ|‹8& ‘ŠT¨P³gÏÆÈ‘#ñîÝ;Ô¬YYÆÍÍ !!!1b<==¡©©‰sçΡ}ûö¨U«&Nœ;;;T«V ‰‰‰¸téÆŒ�¨Q£�`ëÖ­¨[·.7n,n×ÓÓÉÉÉ066Æúõëakk‹–-[�|}}aooX[[ãúõë˜7oNž<©p䥧¥¥õÉŽ×׎A"""""""5jôôô†ŒŒ ôìÙ?ÿü³8¿lٲطoBCC1qâD¼}û­Zµ‚ xÿþ=êÔ©ƒÕ«WãêÕ«¨[·.-Z„N:ÈÀwݺuX´hªW¯Ž%K–@GG@nðgãÆxùò%\]]$¶’±³³ÃñãÇñ×_!,, ­[·ÆéÓ§Q¿~}…û"/=“Ïtä¾> Ò©†† „AƒºLÅŠ1yòdLž<¹À¼áÇcøðá…®;`À� 0@ü?11�н{wX[[ºž½½=ìííåΓvaR&=RǤ!"""""""R Ò©vw"""""""ú†(êªDªÅ–4DDDDDDDDj€A""""""""5À ‘`†ˆˆˆˆˆˆˆH 0HCDDDDDD¤F²²² ‘H°wï^¥–÷îRSS?iNž<‰¦M›âßÿ•ÉWxx8† sssôéÓÇŽ秤¤`ûöí<x0ÌÍÍÑ©S'„……!##ã“æíkÆ Ñ, �“&Mú$ÛJMMÅŒ3`gg‡˜˜™ykÖ¬Á’%K`gg‡… ¢V­ZpppÀÙ³g�ׯ_ÇŽ;Ю];,X°�mÛ¶Å´iÓðIòö-à+¸‰ˆˆˆˆˆˆ�ððáCܹsÿþûìÝwTGÿøñ÷UÄ^ÑĆ]Q‰ 1‰ÅBŒ‰¢ØÑ'(ˆ%‚Dybǯ½ F¬ØK vEcÁ‚1öFDAA°°¿?ø±—z±ñó:çžÃÝÙ™ÝöîÎNùƒV­Zi­ëÒ¥ }úô¡`Á‚�´mÛ–ððpBCCiÖ¬-Z´ÀÂÂB occCÙ²e6lÿùÏ044ü ûò1’–4B!„B!D.R… 6ààà€™™^^^Zë/\¸€——ÖÖÖ˜™™áééÉýû÷˜4i“'OföìÙh4ÜÝݳ“•zõê±|ùrªW¯žn]™2eÔ €|ùòQ¹reâããÐh4éâ$$$`bbBt? Ÿ0iI#„B!„B䢕+WâççǤI“(Uª7nÔZûöm .Œ‡‡ÏŸ?gÍš5 4ˆúõëÇ;wxúô)žžž.\8Û8U¦¤–/Ÿní9âããÙ¿?'NÔZ~åÊîÞ½KDD~~~¬]»Vçm~ꤒF!„B!„È%qqqŒ7ŽÅ‹Ó¾}{�Z¶lÉìÙ³Õ0666ØØØ¨ß+W®Œ¹¹9< R¥Jc``@ýúõuŠS®\¹w’÷Õ«W“/_>ììì´–¯ZµŠÉ“'ШQ# ÞIzŸ©¤B!„B!rÉíÛ·¹}û6uëÖU—åÏŸ_+Œ¢(ìÝ»—íÛ·ÁåË—HLLÌt»ÙŹ{÷.Ož<QÃk4êÔ©£s¾ÃÂÂ>|87n¤hÑ¢Zë&MšÄرc¹sç‹-âË/¿äÌ™3Ô¨Qƒ'Ožp÷î]­ð&&&Zݨ>eÒÞH!„B!„È%/^¼� )))Ó0K—.eâĉØÙÙñË/¿°xñâl·›]œÕ«Wcjjª~¬¬¬tÎó™3gppp`Ò¤I´k×.Ã0†††T«VI“&ñÙgŸ±k×.5nêtMMM¹}û¶ÎiÿÛIK!„B!„"—T¬X###ÂÃéT©Ü &µÍ›7cooµµ5�/_¾ÔZ¯ÑhHHHÈQœQ£F1jÔ¨ç722’îݻӿFŒ¡µîåË—èëë§Ë›¡¡!EŠÀÊÊ*Ýþ‰ÿ‘J!„B!„"—/^___\]]‰‹‹£råÊj«“ 4`ÕªUÔ¨QCCC,X µ¾F,\¸°°0 ,HãÆ³“™¸¸8"""xôè�/^ÄÐЪU«òüùszöìIBB¶¶¶œ<yRgjjʺuëØ¿?:t |ùò¼|ù’-[¶`hh¨5>ŽÈ\®TÒH­™B!„B‘lÈ!+VŒeË–‘€££#ß~û­ºÞÍÍÄÄDFM­Zµprrâøñãêz{{{þúë/\\\°°°`þüùÙÆÉ̃077W¿»¸¸�°ÿ~®]»Fxx8@ºîQ—.]¢]»v$&&²yófÂÃéV­mÚ´ÁËË ccã·9DŸ iI#„B!„Bä"===úõëG¿~ý2\_²dIüýýñ÷÷W—õèÑCýÛÈÈ|||tŽ“™ªU«fÚ°ÂÊÊŠþýûgßÕÕWW×lÓ“ƒ…B!„B!ò�©¤B!„B!„Ȥ’F!„B!„"J!„B!„Bˆ<@*i„B!„B!ò�©¤B!„B!„Ȥ’F!„B!„"J!„B!„"ÅÅÅñôéÓÜÎF¶¢££éСûöí{¯élß¾FCbbâ{M'/’J!„B!„"ùûû3nܸÜΆNEÉí,ü«éåv„B!„B‘÷•,Y’;väv6þÕ¤%B!„B‘K&MšÄäÉ“™={6wwwV¬XÁgŸ}F||¼îÎ;h4þüóO8€££#AAAØÙÙѼysf̘ÁóçÏÕðIII¬]»–.]ºÐ¼ys¦M›¦s—ªýû÷Ó§OjÕª…³³3¿þú+Ü-K£Ñ°wï^�8@·nÝX´h:tÀ‚€€�îß¿ÏÌ™3i×®ÖÖÖ¬ZµŠ¤¤$�¢¢¢(^¼8¡¡¡ôíÛ—úõëãááÁýû÷³ÌÓ­[·ðððÀÌÌŒ^½zqôèÑç…TÒ!„B!„¹¤_¿~ 8'''Ξ=˰aÃhß¾=÷îÝãøñãj¸ЦMêׯ@pp0ÀÍÍü‘¥K—âã㣆 ä×_ÅÍÍéÓ§sþüy~üñÇl»+…‡‡ÓºukÚµkÇòåËéܹ3eÊ”É4üo¿ýFdd$cÆŒa̘1ÌŸ?ŸòåË“€——?üð...Z-pbbb˜6mÝ»wç—_~áæÍ›ØÛÛkU2¥EëÖ­©Y³& .ÄÎÎ[[[Î;§Ó1þ˜Hw'!„B!„"—TªT ccc Ô €!C†Š••�«W¯æ»ï¾#_¾ÿµµ˜>}:ÅŠ téÒ´jÕŠ!C†P¤HÆŒÃáÇ133 |ùòT¯^oooÊ•+—i~.]ºDåÊ•éÞ½;FFF:íøqã(Z´(�ׯ_ç·ß~ã?ÿùºþèÑ£ìÝ»—¯¿þZ]æëëK‹-�øâ‹/¨]»6ûöí£cÇŽé¶¿zõj¬¬¬8p �æææœ;wŽmÛ¶i³iI#„B!„Bä1ݺucÉ’%ÄÆÆò×_±mÛ6Ú¶m«F£Ñ¨›ššpóæMnܸA||<_|ñFCõêÕÔ.O‘‘‘DDD¨Ÿ›7oЪU+>ûì3Ú¶mˬY³¸téRŽò]ºti¢££µ–•+WŽGeš÷Ò¥KciiÉÕ«W3Üæ™3gX´h‘º/†©S§ªéܽ{Wk_.^¼˜£<ç%Ò’F!„B!„ÈcZ´hA™2e8rä7oÞdèСg>¥…Úf÷îÝÔªUK+\J+š¡C‡²k×.uùèÑ£ùù矩X±"¿ÿþ;'Nœ`Ïž=´nÝšo¿ý___òºò%«eiéééahh˜áº%JàììÌO?ý¤µ¼`Á‚@rKuyÙ²eyðàNùÍk¤’F!„B!„ÈE†„„­e 4ˆ.\¸®‚àñãÇj7£+W®`ddD•*U(T¨&&&\¿~=]ë›;wîÌ4?úúúXXX`aaAóæÍ±±±aäÈ‘*Tè-öR[ê–5111>|˜aÆeÖÌ̌ɓ'S¤HŠ/žný¨Q£5jÔ;Ë[n’J!„B!„"Õ¨Qƒ… FÁ‚iܸ1�_ý5nnnT¯^æÍ›§‹çêꊫ«+zzzLž<___Ê–- ÀÔ©S8p úúú4lØ7nEÿþý³ÌËž={¸|ù2 4ÀÀÀ€]»vannN‰%xñâÅ;ÛçaÆC… X¹r%´lـ… pìØ1LMM±··gÞ¼y <˜aÆ¡¯¯ÏñãDZµµ¥Zµjï,OyTÒ!„B!„¹ÈÞÞž¿þú ,,,˜?>ùóç§F4kÖŒž={b``.^óæÍ™2e ÅŠÃÅÅ…^½z©ë)Q¢+W®ä§Ÿ~¢N:tîÜ9Û¼”,Y’¿þú‹•+WòôéSÚ·oÏš5kÐ××§•4öööñÏ?ÿйsgÐÓK®¢hÖ¬cÆŒaäÈ‘øûûÓ¶m[¶mÛF`` žžž<{öŒ/¿ü2Û™ª>FRI#„B!„Bä"###|||´¦Ð†ä™’Ž;Æ’%K2Œ7bļ½½3Ýn»víh×®]ŽòÒ¨Q#5j”áºB… iUŒXYY¥«(éÝ»7½{÷ÖZ6vìØtÛêÖ­[†­ƒ� (€¿¿¿Ö²R¥Jáåå…———Nûñ±’Ù„B!„Bˆ<(44”¶mÛR§NÜΊø@¤’F!„B!„Èc ÄÙÙY§Ù‘Ä¿ƒtwB!„B!òÎ;—Ẍº},Ê”)óÑæýC–4B!„B!„y€TÒ!„B!„BäRI#„B!„B‘H%B!„B!D •4B!„B!„x'‚‚‚055Ííl|´rev'ÉY!„B!„B›´¤B!„B!„Èr¥%B!„B!’)ŠÂÎ;Yµj.\ M›6Œ1‚Š+@BB `Ó¦M/^œþýûÓµkW4 �III³zõjîß¿ƒƒƒ ¢H‘"DEEQ¶lYöìÙCpp0GŽaÙ²e(P€Õ«WsèÐ!¢££ùúë¯1båÊ•ÀÆÆGGGΞ=˾}û¨[·.4mÚTÍû½{÷˜1c;vìÀÄÄ„¢E‹~øø/"-i„B!„Bˆ\´víZF…½½=Ó§O'::šÖ­[¥†ñòòâÕ«WL™2…N:Ñ·o_BBBÔõüú미¹¹1}útΟ?Ï?þ¨5ÜHÛ¶miРÞÞÞ˜˜˜pûöm .Œ‡‡ÞÞÞDFF2hÐ ­8sæÌ¡E‹R«V-ììì¸sç�±±±ØÚÚòúõkfΜI=8xðà8bÿ^Ò’F!„B!„È%Ïž=ãÇdÉ’%´nÝ� ,-- ÆÕÕ�<==øê«¯xþü9“'O¦k×®<~ü˜1cÆpøðaÌÌÌ�(_¾<Õ«WÇÛÛ›üùó°cÇlllÔ´mll´¾W®\sss<x ¶¦8p ={ö I“&ÎçŸNHHeÊ”aêÔ©èé%W/¼xñ‚Ÿþù}²5©¤B!„B!rÉ­[·¸qãuëÖU—,X¯¿þš?ÿüS]–RÑ’¢I“&¸¹¹ÍÍ›7‰ç‹/¾H·ý§OŸR¼xq�Š+¦µNQöîÝËöíÛ‰ˆˆàòåË�$&&f˜WCCC*UªDll,�þù'VVVj  õ7ÀÝ»wyòä‰ú]£ÑP§NÌÈ'N*i„B!„Bˆ\òâÅ �^½z¥µÜÀÀ€‚ f/_¾äÑK (€‘‘�»wï¦V­ZZáÊ•+§UI’ÚÒ¥KYºt)“&MbÀ€Ü»wOmÍ“]º� éòÖêÕ«ñððP¿—-[–dçS&cÒ!„B!„¹¤bÅŠ®.{õê¿ÿþ; 6T—=|øP­Ð8wî666)R„ *`bbÂõëשX±¢ÖG__?Ó´7oÞŒ½½=ÖÖÖÔ®]›Ò¥Kç(ïõêÕcÇŽZ-oRg0jÔ(EQ?RA“5iI#„B!„Bä’’%KâëëË!Cˆ§B… lÙ²…§OŸboo¯†;pà�téÒ…û÷ïãííÍúõë(T¨S§NeàÀèëëÓ°aCnܸATTýû÷Ï4í °jÕ*jÔ¨¡¡! ,ÈQÞðóócøðáôêÕ‹ØØXÞì@ —[Ò¼|ù’ƒ2f̬­­±¶¶æçŸÖÁ:3ÇgÔ¨Q4nܘŽ;²bÅ ^¾|©æÉ“'øùùѪU+œœœØ¼y³Öú§OŸH=¨_¿>Ç'222]Z{÷îå‡~ iÓ¦x{{óðáÃíçåË—=z47fèСZý !¹âäÉ“±±±ÁÂÂ???¢££uÞþßÿMÏž=™9sfºuÙƒ´^½zŪU«(]º4=R—ß¹sG=VµjÕ¢W¯^ìÞ½;Û¼é’~TT¾¾¾´jÕ ¦L™’eÐ%/o{L³²}ûv4Z[œöû§&§eìSöòåK8ÀÈ‘#±°°ÀÂÂ__ß_S‚‚‚055Õ9¼ ™®?}ú4666ܸq#GùÈk7nÌòåËßjOž<!!!áå(w;ww÷<“Îû*‡III˜šš¬SøÄÄÄL›|甫«+ãÇÏQ9/Ëíóò¦rºŸojÓ¦MT­Z5Û.ïJNÊéëׯyòäÉ;É[NßÒÒ¥¼¾m¹!&&†ñãÇcgg—n]VÏ{öì¡W¯^ÔªU '''îÝ:DãÆùã?Ôe¯^½"$$„bjjŠ““¿ÿþ{º¸qqqÌ;—ž={R§Nú÷ïÏ¡C‡r¸·ééòÜ÷o0dÈüüüXµj±uëVŠ)¢†iÔ¨… bĈ„††²~ýzZµj¥®wtt$88˜½{÷âààÀÂ… ÓµjIËÍͶmÛ2zôhæÌ™ƒƒƒ•+WÖ9ßÆÆÆ8p�===\]] ÁÍÍ-ç@¨rµ’&..Ž)S¦P³fMƇ³³3{÷îeÀ€YÞ$Ÿ8qGGGªV­ŠŸŸvvv >œÅ‹«až>}JÏž=¹téžžž´jÕŠÎ;¤†<x0'OžÄÑÑîß¿O§NøçŸÔ07nÄÁÁ-ZàããÃåË—qppàñãÇ:íãåË—±±±¡P¡BøúúR¨P!Ú·o¯VÔÄÆÆÒ¤I^½zň#1bëׯÇÍͤ¤¤,·ýúõkÖ¬YCÓ¦MY»vmºõºƒÔnܸA¿~ýèÛ·¯V $7ƒû믿pttdúôéTªT‰öíÛšiþtIÿùóçôíÛ—ëׯãééÉСC ãûï¿ÏôÇ>»¼¼Í19“Ó2ö©{öì'N¤V­ZøøøÐ¿vïÞÍ·ß~›«Š¢ÈÿÆÿײeK6nܘÛÙxkIIIlÙ² ‹&7-‡7nÜ ""‚ è~÷îÝ”(Q"Wʼœ—Ìåæyy9ÝÏ7uâÄ ºvíšn0Î÷!§åçöíÛ”(Q‚+W®¼çœeïßø{vôèQÚ·oÏäÉ“µ–g÷°mÛ6¾ûî;Ú´iÃìÙ³©W¯íÛ·gß¾}¦óôéS&Mš„¥¥¥V—H³döìÙXZZòË/¿P¥J¬¬¬8vì˜&** {{{µr`Ö¬Y4iÒ„û÷ï¿ÕþëòÜ÷o¡§§GŸ>}غu+GÅÇÇ']×£êÕ«ãççǹsçXµj•VMŠvíÚ±bÅ "##Ù¼y3ßÿ=�eÊ”AQš7o®¾dÉ’øûûsñâE6nÜH=¸~ýºZQ³sçN¬çÂ… ôîÝ[ý^­Z5æÎË… X²d ß~û-.\x'Çô„–@�� �IDATåS”«ÝŠ/ÎŽ;Ðh4겦M›bnnΕ+W2ýÁkРÇŽÃØØX]C`` ...h4¶mÛÆ£Gذaƒ:ˆ’&L sçÎ.\???*T¨ ¦ß¨Q#Ê—/ϱcÇèØ±#ñññŒ7Ž àè耥¥%M›6eË–-8;;g»sçÎÅÁÁ4 íÚµ#::š¹sç²`ÁŠ-ʉ'¨T©’§T©R´k׎ɓ'gY‹ùüùsV¯^ͦM›;vlºõºƒÔ¨U«kÖ¬Q§XK1hÐ ­óôÍ7ßðøñc6lØÀ×_aþtI?""‚;wrëÖ-*T¨�@ýúõiР7oÞ¤jժ鶛]^Þæ˜ŠœÉiûÔ•(Q‚}ûöi•_sssÌÌ̸téR†£ñ5Òéíšøxܺu‹óçÏS¿~ý&7-‡.\ÀÄÄ„jÕª½uÞ79/ÿ>b?EaË–-x{{¿·4RûPåô}ø7þž­]»–#Fɉ'ÔåÙ=¬]»–ªèíÛ·çÎ;ìÙ³'ÃAaoܸAdd$üñGºÿ.]ºÐ§Ou�Û¶mÛNhh(Íš5`áÂ…èë뤆k×®Ý[ï¿.Ï}BüÛäúÀÁiÿ±RºŒ¤,5­T€Ï>ûŒ˜˜µ9×Ê•+éÝ»·úàÉMz¯^½Ê©S§€äšR§_¢D �õöÉ“'‰ˆˆ M›6j˜âÅ‹ãìì̯¿þší¾=zôˆÙ³gÓ¹sg5|ùò©MÏRºô¤®L€äZN@kP¨Œ.\˜Z¶l©5Âv ]ŽAjNNNx{{g8XTÚó¤( ÏŸ?§|ùòê²)S¦0bĈƒ”7B©ß¾Ü»wòåËSªT) ¹5’­­­G—¼¼é1…ÿýè999Q«V-:wîœã.<‰‰‰,X°€Ž;ÒªU+¦OŸÎÓ§OX¾|9Å‹çÙ³gjx???LMMµºdÍ›7nݺå(Ý-§eLd~ÍK™1#÷îÝã?ÿùffftéÒ…;wj­OJJbíÚµtéÒ…æÍ›3mÚ4µ¼¥8rä}úôÁÔÔ”!C†huí<qâF}ÛµgÏ\]]iذ!ÖÖÖÌ™3‡çÏŸ«ámllX¼x1#FŒ ~ýúôèÑCëÆ1­àèèHPPvvv4oÞœ3fhm’v†JýúõéׯGÕZÅ„ hÖ¬]ºtaíÚµ9zcÀ„ ðõõÅ‚víÚ±víZõšÕ¸qc"""prrB£Ñ¨Ý˜2e Ó¦MÃÒÒ;;;6lØeóᬮ)ÛôññañâÅ|õÕW >H¾žmذ7nÌĉéׯ[·nÕy?Ο?¯>8&%%¡Ñh´¶qíÚ54:Íftt4S§NU»Ž7Ž[·ne™×´é@òµÜÍÍ 333:uêÄÔ©Sµº‚æ¤fw¾RÇKiaEñâÅÙ°a½zõ¢N:Œ1‚Û·oÉÝS¿ùæ yÓ”ÿÇ”x¡¡¡ôíÛ—úõëãáá‘î pxx8?üðuêÔaÀ€\ºtIÎ˿༤°±±aåÊ•¸»»cff†³³3§OŸÎ2NÊ~>þccãt÷‡“'OfРAÙÞ[DEE¡ÑhØ»w/ƒ ÂÌÌLMûæÍ›œ={V­49pà�ìÝ»GGG,--yòäI¶åéÀ888°téR:vìHãÆ™6mqqqZyN[~²ºæßºu‹*Uª�`jjª¦§ë¹Ëî÷-§ç(my}“42ó®®Ïh4­.Y›6mÊ´²! €^½zahh¨µ<»ç�CCCNŸ>­Þû&%%qáÂõ|ÅÅÅ©¿Í<øëòåË©^½zºm•)SFk†¡|ùòQ¹reâããxüø1ãÆã‡~Èr&¢7¡ËsŸÿ6¹^IðàÁ>̺uë9r$3gÎÌqk‡£GbggG¾|ùxýú5‡J÷ ^®\9€L›Ý¥TÔ®]H_Y¢jÕª=z”ׯ_g™§”q&RW�|þùç�ZݪR;þ<Õ«W§bÅŠYnÈ𢠼Ñ1Èl[©8q‚Ý»wãééɵk×4hüÃõçŸΫW¯tN¿~ýúŒ5 {{{¦OŸNpp0£FbÁ‚jEÝ£Gعsgº<g–—Œäô˜ž:u æÌ™ƒµµ5;wÖê››EQ3f ;wîdäÈ‘Œ3† 6àââBRR_}õ111j—·”q€"""´š—†„„ààà Sš¹áMÿÏDò± cõêÕ :”yóæ©7MiÅÆÆbkkËëׯ™9s&=zôààÁƒZaùõ×_qsscúôéœ?žüQëæåرctéÒ…Ù³góúõk,--Õ‡½´"""¨[·.þþþôë×… ¦DnΜ9´hÑ‚ÀÀ@jÕª…wîÜÉtŸƒƒƒ9pà�nnnüøã,]ºuý… hÙ²%¦¦¦Ì™3‡êÕ«Ó¢E Ž9$7ö³³#!!Ÿþ™®]»2|øpæÏŸŸå±NkΜ9)Rz÷îMÏž=Ù»w/\éhbb‚¿¿?gÏžÅÊÊJçååÅ«W¯˜2e :u¢oß¾„„„d˜Fv×€“&Mâ?þ`ذatïÞ€+V0nÜ8zöìÉ´iÓxôèѳsòäÉupwwçìÙ³Lž<™)S¦P©R%Š-še^Ó¦óäɾùæŠ/N`` ƒ ÂØØ5|NÊ!d}¾ ùX‡††Ò¢E uYLL AAAôéÓ‡ÀÀ@<x€‹‹ ¯^½ÂÜܜٳgpæÌΞ=«oÚ´itïÞ_~ù…›7oboo¯V&þù矴jÕŠ/¾ø‚ùóçS·n]öï߯ÓñÍèxéBΡ9/©;333fÍšE… °´´Ìp¼Â´ûY¤H ¤Õ]211‘¥K—boo¯ó½EÛ¶miРÞÞÞ˜˜˜�·Ö9rä?ýô:ubäÈ‘Zå"+6l ""‚Q£F1qâDV­Z¥û•ÓÌ®ùeÊ”aÇŽ@ò}ËÙ³gÕ{­ìÎ.¿oÉÉ9zÓ42ò®®Ï9•ÕýyVë†Ê™3gèÚµ+¡¡¡Œ9’R¥J©­åŸ?Îñãǵ^”êò,�ÏþýûÕY‡îÞ½ $Wùúúbii‰ƒƒÃWˆe'õsß§dÔ¨QvmÿB®®®ÊùóçÓ}?~¬ó'88Xyõê•NŸ;v(±±±ZŸuëÖ)€úYµj•£®V²ròäIPÂÃÃEQ”èèhPvìØ‘.lÍš5•yóæ¥[þúõk¥ÿþÊ÷߯.›5k–bnnž.ìæÍ›@‰‰‰É2_aaa  ÜºuKkùµk×@9räHº8ÑÑÑJíÚµ•¥K—f¹í´¬¬¬”3fhm'§Ç Åž={@ùçŸÒ­«[·®zžúôé£Ü½{W]÷âÅ åùóç9Nÿï¿ÿV*W®¬n·wïÞéŽmFÇ:«¼¤–Ý1Íè<¤Õ»woeêÔ©Š¢(Jhh¨( ~WŒŒŒ”{÷î©ñ/]º¤�ÊáÇEQ”N:©Û»pá‚R½zueðàÁŠ···¢(ŠrãÆ PnÞ¼™mÞ>”´ÇémÊØ§"³²µeË­kÞúõ땤¤¤ Ã.[¶LiÓ¦òòåK­euëÖUEQ=z¤)gΜQ×_½zUÔ2ؾ}{­óñâÅ ÅÊÊJñóóSEQŽ?®>­ (VVVê÷´Û{þü¹bdd¤lÞ¼9Ãøû÷ïW�åÉ“'ê²?þøC”ëׯ+Š¢(ýû÷Wüýýµâ¹»»+Š¢(ÊÒ¥K[[[åõë×êúàà`¥X±bÊãÇEQ”F)Ë–-Ë0Š¢(Ó§OWµ–}ÿý÷ʸqãÔïuëÖUV¯^.^J>R̘1Cùâ‹/2<oº\¦OŸ®´mÛVyñâ…æÙ³gJùòå•;wªË@Ù²eK†û”QKJJRÌÍÍÕßÎׯ_§ÛÆ_ý¥�Ê¥K—”—/_*FFFÊòåË3L#£¼f”Îùóç@«,¦–Ór¨Ëùºuë–ºŠ¢(>L÷ûzúôiP®]»¦(ÊÿþÿR—¥”x)çGQ%**J)Uª”²uëV5í)S¦¤ËOÊu;59/yó¼d§}ûöÊܹsÕïIIIJ×®]•ñãÇg>í~¦«Û·o+Š’\Ê—/¯<{ö,Ãø©ï-Rö5£ßÔI“&)îîîê÷”kjêûËìÊSêx©ï©–-[¦Ô®][½–¥-?)Ç%«kþõë×@¹pá‚F—s—Ýï[F²;GiË뛤‘‘7¹>gæÂ… Z¿Š¢(7nT€,ãM:U騱c†ëÒ>(Jr™øù矵î7öíÛ§æéÓ§ZÿsŠ¢(÷îÝS�åàÁƒ™æeÑ¢EJÍš5Õ²”R¶�eÞ¼yÊ®]»”qãÆ)€ràÀ,÷+­ìîËÓ>÷}¬tyþ¹ïÈ‘#ÊŽ;t®ëÎQ=JFõ0®®®Jž¨~´µµ%::šÈÈHæÍ›§˜¤‹Û·o3pà@|}}ÕÚÜ”q0Rw'äf~·oßV»¾¤6oÞ<öï߯Õß·D‰\¿~=]Sº¸¸8Ê–-«¦IDD„ú¹yó&€úV#¥)`Š”7)Ý«R$$$0jÔ(êÔ©C=ÔåwïÞÕÚþÅ‹³=.or tqáž={Fxx¸ÚL2eÿôõõÕ¦˜º¦ãÆ lllpss#**ŠÝ»wóäɬ­­¹wïž/£7DYå%EfÇ4;wïÞeþüùôìÙ“æÍ›’®9pf®\¹B“&MÔ%�µjÕ¢víÚ\½z€=z°nÝ:’’’8qâ½zõ¢sçά]»–—/_ªÍ§uiù“[ÞWûØÙÙñòåKîݻǒ%KèÖ­[¦ƒÕþùçŸXYYi½ÑLý÷7ˆç‹/¾@£Ñ ÑhÔ¦Êi»<¥Ð××§cÇŽœ<y2Ãõñññ¬]»–~øÖ­[ãåå•å T†††TªT‰ØØØ,÷;uSî”™5n޼ɫW¯Ø´i5Ò oeeÅöíÛILLäôéÓ|õÕWZoÍš4iBLLŒzÍ}åË—×iæ·üùók}oÒ¤ gΜÉ0®.×�H¨O___ý~ëÖ-îÝ»Gݺu3MWwîÜáøñã:'¡§§‡¿¿?ƒfèСlݺ5Ýõ.m^3J§zõê|÷ÝwtîÜ™ &pøðá,[œfW3’ö|é2HJW´תŒ¤.£¥K—ÆÒÒ’«W¯’””Dhh(7Ö _ @ó.çE[n—ÌîÙ2JK£Ñ`mmi÷Ý´ûÙ°aCš4iÂáÇäÙA]\\(T¨ Û½EÚîþŠ¢°mÛ6­VI)tm=“•’%KréÒ%µLèRNu½æCæç²ÿ}Óe›Ù£œ¦‘Ù=÷›\Ÿ³+kïÛ´iÓØ¾};'OžäÆLŸ>;;;æÍ›§†)\¸pŽ[£„……1|øpæÌ™£–Á”Ù‡¶mÛÆàÁƒÕq œœX±bÅ;Û§Œžû„ø7Ê•4|Á466¦OŸ>¸ººêÔ”ëŸþÁÙÙ™5jhM¨¯¯Ï—_~ɵk×´ÂGEEŸ®ûѪU«6lK–,ÑêfU®\9>|˜îáäÖ­[´hÑB½¨ :SSSõ3gÎàã ¤ô¹N‘Ò ¤lٲ겗/_âééÉ¡C‡˜5k–VÎÕ«Wkm?uüÌäôäD¡B…hذ!¾¾¾„……qæÌ™7Nݺu-Z”¡C‡RºtiÚ¶mËš5kˆeݺuo•—¬ŽiV¢££Õ£ÇŒúuërTÁ“áL=E‹Uo¿üòKÂÃÉŒŒdçÎ|õÕW4mÚ”+W®péÒ%vïÞ§»:Áû-cŸ===Ê•+Çwß}‡››«V­Ê0\BBB–Óš¦Œ´{÷nnÞ¼©õɬ $?Ädö?áêêªÎ´·páB&L˜íþäô&/%¼¯_¿æÑ£Gé (@áÂ…ÉŸ??ñññéŽCÊvÚ‡ÔœxÓSòŸÑà.×€Œ¤ìßÛÎLAõêÕÓ= §}áÚСC¹zõ*-Z´`É’%4jÔ(ÛYÒ¦c``ÀâÅ‹ ¥téÒŒ9’N:e9­rVå0#iÏשS§°··Ïò¡ëm•ÔÓÓÃÐÐׯ_“m7ç¬ÈyÉ:NN¼ÍyÉìž-3ùóçךþ6µ´û©§§Ç÷ßϦM›HLLdÙ²etìØxó{‹{÷îqìØ1gʪ<e$íyȬœ¦õ¦ÝLRÎdÿû¦«¬ÎQNÓÈìžûM®ÏÙ•µœž«œxðà?þø#nnn4nܘJ•*1jÔ(æÍ›Ç!Ctz9‘‘3gÎàààÀ¤I“´Ny¦Iý?¯ÑhhÖ¬™N/—u‘ÙsŸÿF¹ZIóúõë /v  dÉ’YÆeàÀ0oÞ<­¾Õ�ÎÎάX±BëMòï¿ÿNƒ ´Þ¸lݺ•¾}ûœ®òÃÜܜʕ+³k×.uY||<kÖ¬¡W¯^ê²;w¢(ŠúùùçŸäJž¾}ûòÛo¿©û©( [·nÅÍÍMmI“””„ŸŸëׯç·ß~Sg9J1jÔ(­í?xð Ëc“Óc ‹—/_¦[–r!NyCôêÕ+­uIÿåË—èëëkýP.\˜jÕªi ò›úM“.yÉî˜fåâÅ‹œ9sggg5jD¥J•rô¶´zõê?~œ¿ÿþ[]v÷î]Ž?®ŽwT¡BzôèAHH›6m¢qãÆ”,Y’ï¿ÿžmÛ¶±fÍ,--uN3·¼Ë2ö)Èêš—zì«Ôå½^½zìØ±Cë+õÿK… 011áúõëT¬XQ듺ò"õMª¢(„……ѤI“tyyüø1Ë—/§wïÞ4kÖŒjÕªezó›•ŒZž=~üXýûÊ•+Q¥J èܹsºqN:E§NÐÓÓ£AƒéŽCDD•+WN7.RvùÈJ¾|ù2`üáÇZËÏ;‡zlâããÕó¢Ë5 #*T X±bZcS½ÉM|xx8;wV¯‹ùòå£^½zÙÞ”—/_žÞ½{³nÝ:*UªÄöíÛs”$ß”×­[—¡C‡Bhh¨Ö žº–C]mß¾=ÃYIy(ÍèÁíÑ£Gêß111>|˜Úµk£¯¯O‡Ò•Ñœ<°ÉyÉÚ‡:/™Ý³¥H{uøðaš6mšá¶2ÚÏvíÚÄÖ­[)^¼¸:kß›Þ[¤Tšd4Ûejº–§ìdT~t•ÑýYfç²ÿ}ƒô÷–³s¤K©evÏý&×çÌÊZʽjLLL–ñßFJÅeÚÊ´”—Ñ)ç*õoWv"##éÞ½;ýû÷gĈZë*T¨€ƒƒ7nÔÚÞÍ›7177ãýH‘ÝsŸÿ6¹:÷Ù³g7nÝ»wW(Ξ=Ëÿýßÿši¼„„†NHHëׯ×ðªR¥J|öÙgtíÚ•Å‹ãâ₳³3>ÄÝÝE‹©5øüñ=zôÀÁÁ *hÍ$Ò¼ysŠ-ʤI“ÔQùMLLX»v-eÊ”¡C‡:íãÈ‘#i×®%J”ÀÊÊŠcÇŽ±aí´æÌ™ƒ~~~<{öLÍG±bŨS§N–Û?uêIIIÄÆÆrãÆ Nœ8AéÒ¥111Ñé¤vÿþ}nݺ¥ÎpúôiŠ+Fƒ pqq¡lÙ²4kÖŒR¥JñäÉ.\È?ü@ݺuQ…¾}ûòàÁvî܉¾¾¾NéwêÔ‰ÿþ÷¿ 2„®]»É?¶'Ožä—_~’kÓ¦ ëׯ§C‡ 0 ˼¼í1MØyöìÙ´k׎ˆˆV®\ɨQ£€ÿuó9v즦¦é¾7kÖŒîÝ»3`À�F¾¾>sæÌaĈ˜™™©é888àèèÈ?ü 6oîСݺuã›o¾ù(¦ ÏiûÔ9sœœœ¨\¹2úúúœ>}š9sæ¨]¦-ïøùù1|øpzõêEll,ê6 *ÄÔ©S8p úúú4lØ7nEÿþýÕp~~~”*UŠJ•*±{÷nNŸ>ÍܹsÓå±hÑ¢4iÒ„… ªo©§M›–£ýL»)\]]quuEOOÉ“'ãë뫾}óðð U«V”,Y’æÍ›sþüy~þùg:€££#¸»»ãèè¨Î$áïï¯Þð–/_ž³gÏrñâEêÔ©ÃüùóñññáØ±cY¶*J­eË–lÚ´ SSSŠ)BÍš5äYQ<<<èÒ¥ ÷ïßÇÛÛ›õë×É!M›6¥_¿~Œ?^çk@ZÅ‹gÒ¤I¸ºº—î%®vîÜÉСCµ–õîÝ›Y³fQ®\9 Õ™« ùfÞÓÓKKK>ûì3¢¢¢8}ú´ÖlAº¤sýúuV¬X……%J”àĉjE\ ]Ë¡.Rà^¼xqŽâ¥Tê­[·Žš5kªÑ�Æ #&&† *°råJ,,,hÙ²%�nnnjmÙ²%—/_ÖúmÈŽœ—¬åÖyIkâĉ-Zsss cúôé:ïgµjÕppp [·nÌ›7O­ìÈîÞ"3'OžÔ¹Ò$«ò¤«ŒÊivÊ”)CåÊ•Y¿~=¯_¿æóÏ?W+²:wÙý¾eto ºŸ#]ÒÐÕ»º>CrY°µµÅßßWWWbcc³?22’'Ožpûömž<y‰'(P �fff™>T©R…#F0räH>|ˆ‰‰ ·nÝbéÒ¥¸»»cllœî·+..ŽˆˆµbíâÅ‹RµjUž?NÏž=IHHÀÖÖV«;dÊïå˜1c°¶¶¦dÉ’XYYqõêUþûßÿj þ&tyîo&::šÞ½{3zôè §e Â××7ÛVœâÝÊÕ–4U«VeÀ€œ8q‚ñãÇ3yòd<xÀ²| ¡Ž¦Þ­[7Z´h¡~RnèK–,IHH&&&x{{³oß>Ö¬YC—.]Ôí,^¼˜øøx~ûí7­m¤~+âììLPPû÷ïÇÇLJ*Uª°zõjß,7lØÝ»wóøñcF­ÎTT«V- y|š”ÚhOOO­<,\¸0ÛíwïÞsssÂÃÃùå—_077gÆ :ƒÔŽ;†¹¹9C† ’ß™››ˈ#(\¸0K–,¡ÿþ¬Zµ '''fΜ‰¾¾¾ú–®^½zê„.é׫WƒR¤H&L˜À„ ˆçàÁƒjeJ‰%°´´T»e——·=¦UªTaÇŽ=z”aÆñ÷ßkUÔ¬Y3ÆŒÃÈ‘#9}útºïzzz,Z´ˆ6mÚ0qâDøúë¯ñ÷÷×J§U«V@òt’)š7o®ž×ANËØ§®Zµj <˜£G2f̼¼¼¸ÿ>ÇWß4¥-ïÆÆÆ8p�===\]] ÁÍÍMk»ŽŽŽ³wï^X¸paº7cÇgÍš5 2„¸¸8öíÛ—á¸AùóçgåÊ•h4ÌÖ­[9rdŽö3í>¤hÞ¼9S¦LaÆŒ¸¸¸h=XZZrðàANŸ>Í Aƒ8wîGŽQÇ®166fÿþýèéé1tèPÖ¯_Ïÿû_­îãÇçСCLš4‰—/_R¡Bš6mªVâèÂËË‹%Jп­Ùj5jD¡B…1b¡¡¡¬_¿^ý644¤Q£FêL,º^2âêꊯ¯/Ë–-ÃÃÃCíΦk—®”ßдãI 6Œnݺ1fÌhÑ¢…Ú}âÅ‹T«V%K–àèèÈÌ™3™9s¦ÚMC×tôõõÑÓÓcêÔ©tïÞƒ²{÷nõ¸€îåPº¶0H«~ýú¬X±‚™3gâëë«õVÞÞÞž   F‰‰ K–,QÓ,--Ù·oááḺº©õÛ9/ÙËó’‘¾}ûröìYÌÝ»wÙ¿¿ÖøRºìgJkëÔÝA²»·ÈÌ®]»tn•”UyÒEfå4;FFF¬Y³†“'O2|øp­)г:wÙý¾eto ºŸ#]Òȉ·½>§ÐÓÓcÁ‚”(Q‚°víZœœœ2 ¿téRÌÍÍ™3g‡ÂÜÜœ1cÆ�™?h4üüü?~<[·neàÀlܸ‘þýû3eÊ ýo׃077W_®¸¸¸`nnιsçØµkáááܾ}+++­{ë”™ÌÍÍ9xð ÿüó#GŽäÔ©Sœ>}:Çå)-]žûÄ›{ŸÝîěӸºº*®®®éV¤ÔøëbÏž=j+]ÂZXXè¼mH®ÕïÖ­[Žâ¡«£Gª•#"srœrNŽY²`mmMLLÌ;èòC àøñã¹2íåÝ»wùüóωˆˆÈ°`Ú2¶ÿ~ÈÅ‹ߨ»‚®>T:Y™>}:÷ïßÏô zNDEEQ¶lYŽ9òNþgå¼äÍó’ºtéÂàÁƒ³ û.÷33>ÄØØ˜Ë—/«­úÞ§wY~Þ×¹ËÉ9zß²»>‹7ó©Ü;},ûù©·¤9zô(111´mÛV§ð!!!:‡…äÁÚÓš7o^Þ8XÍ+q%�� �IDAT!„øÔ]¹r…… òûï¿†Æ Ër,›ÔÞf<‰œøPéde÷îÝ9÷$·Èyù÷ùûù¦­’ÞT^(?yÙÛ^Ÿ…ÈJRRFk†ãk×®¡ÑhÔ¡(¢££™:u*­ZµÂ‚qãÆqëÖ-5üÑ£Géß¿?õë×gäȑܸqC]gccCPP&L aƇF£Ñj9|ïÞ=þóŸÿ`ffF—.]´†èŽ\……Bˆ<BQ.\¸À¬Y³øüóÏéÚµ+ÎÎÎ:Ï„3hР7š¶;§>T:Y ÎÑ D¹IÎ˿χØÏ-Z¨Ý¨?„¼P~ò²·½> ñ¶ÜÝÝyñâ“'OFQ®\¹¢¶P>|ø0ß}÷Ó¦MÃÅÅ…½{÷Ò­[7öìÙ£Ž}Ù§O¼½½?~¼Ötò)bcc±µµ¥]»vÌœ9“{÷îáé陣nãâÝJ!„ï•••ÕGÝçùM }µjÕbÆŒoÿCÝHå…¶wÙu®L™2﵌Êyy3ïû¼¤•“7Æ¢ë¦ÁÅæ]–Ÿ÷uîró­þÛ^Ÿ…x¯^½"88˜ÀÀ@uFbkkku½¿¿?cÇŽ¥S§N�4iÒ„   Nœ8¡v¿™<y2^^^jœ´3`†„„P¦L¦NªV¿xñ"Ý,xâý“J!„B!„"ÒÓÓÃßߟÁƒsüøqlmm±¶¶¦P¡B$$$°yóf6oÞL¿~ý´âÅÆÆª—*U*Ë4þüóO¬¬¬´Zï¥mÉ©NáɳަÌÎ'Þ“F!„B!„ÈeYµ@:t(W¯^¥E‹,Y²„Fqá (@ùòå™5k7oÞÔú¤že.; ¼zõ*Ë0C‡ÅÔÔTýÌ™3Gçí ÝI%B!„B‘KòåËG½zõˆŽŽÎ2\ùòåéÝ»7ëÖ­£R¥Jlß¾|ùòaggÇ©S§¨P¡+VT?EŠÑ9õêÕcÇŽ$&&ªËÒVíܹEQÔt…z?¤»“B!„B‘‹z÷îͬY³(W®†††«ë^¿~§§'–––|öÙgDEEqúôi†ÀÈ‘#iÒ¤ UªT¡S§NÄÆÆrôèQÜÝÝ)P €Né;88àççÇðáÃéÕ«±±±¼—}Y“–4B!„B!D.6lݺuc̘1ТE 4h�$à[­Z5–,Y‚££#3gÎdæÌ™tìØ€:uêpêÔ)¢££ùî»ï˜8q"III$$$蜾±±1@OOWWWBBBpss{/û*²–+-i>æY>„B!„Bˆw©P¡Bxzzâéé©.ëÝ»·ú·‹‹ ...™Æ¯]»6³fÍÊp]F3£*T(ÝsyµjÕ˜;w®Ö²o¿ýV§ü‹wGZÒ!„B!„BäRI#„B!„B‘H%B!„B!D •4B!„B!„y€TÒ!„B!„BäRI#„B!„B‘H%B!„B!D •4B!„B!DòêÕ+4 [·nÕ)|\\OŸ>}§yxòä ~~~´jÕ '''6oÞ¬sÜÄÄDž<yòNóó©J!„B!„â#æïïϸqãÞÙöž>}JÏž=¹téžžž´jÕŠÎ;¤SüÝ»wS¢D ’’’ÞYž>z¹!„B!„BäÛ¶mãÑ£Glذ###� ˜0a;w¦pṜÃ/iI#„B!„Bä"EQذa˜™™áå奵þÂ… xyyamm™™žžžÜ¿€I“&1yòdfÏžF£ÁÝÝ=Û8ÙY¹r%½{÷V+h�lll¸zõ*§NÊ2îöíÛùæ›o�ÈŸ??†¸¸8ªT©Âòå˵ÂN™2…ªÛ_¹r%îî™áììÌéÓ§µÂߺu ÌÌÌèÕ«GÕi>&RI#„B!„B䢕+W2nÜ8zöìÉ/¿üB||¼ÖúÛ·oS¸pa<<<ðöö&22’Aƒ¡( ýúõcàÀ899qöìY† –mœ¬¼~ýšC‡Q©R%­ååÊ•ȶ¢ÇÜܜٳgpæÌΞ=K¡B…4h«W¯VÓOHH`ñâÅ8::ªqÇŽ‹™™³fÍ¢B… XZZ @TT­[·¦fÍš,\¸;;;lmm9wî\v‡÷£"Ý„B!„Bˆ\ǸqãX¼x1íÛ· eË–jE$·2±±±Q¿W®\sss<x@¥J•066ÆÀÀ€úõëë'¥Â%#±±±ÄÄÄP¨P!­åzzzÔ¬Y“èèè,÷§T©RT©R€úõë“/_rÛo¾ùOOO®^½J58~ü8�j\OOO¾ýö[�¾üòK.]ºÄªU«˜8q"«W¯ÆÊÊJmycnnιsçØ¶m›Ö~줒F!„B!„È%·oßæöíÛÔ­[W]–?~­0Š¢°wï^¶oßNDD—/_’gQÊLvqîÞ½«5“F£¡N:êx3Ïž=ÓÚ^RR·oߦL™2@òìOwïÞÕ cbbBÁ‚3ÌOݺu±µµeÿþýÔ¨QƒÐÐP¬Õ¥J£ÑhýmmmÍÎ;äV9K—.eÑ¢EZÛ=zt–ûó±‘J!„B!„"—¼xñ Ë™–.]ÊÒ¥K™4i àÞ½{´nÝ:ËífgõêÕxxx¨ßË–-˃Ð××çË/¿äÚµkZÛ‹ŠŠ">>žòåËÉ•&ÖÖÖZa®\¹B52ÌF£á»ï¾cöìÙ8::²hÑ"~ÿý÷,÷!þü)R€%JàììÌO?ý¤&¥R(³ýùØÈ˜4B!„B!D.©X±"FFF„‡‡«ËÒŽ³yófìíí±¶¶¦víÚ”.]Zk½F£!!!!GqF…¢(ê'u…†³³3+V¬àéÓ§ê²ßÿ иqc�¬¬¬´â+Š¢VФ´ˆyõê•VšmÚ´!<<œ3fдiS­ÖC@ºJ•ÇÓ´iS�ÌÌÌ8|ø0EŠ¡bÅŠê'e¿²ÚŸ‰´¤B!„B!rIñâÅñõõÅÕÕ•¸¸8*W®Ì®]»´Â4hЀU«VQ£F Y°`Öú5j°páBÂÂÂ(X° 7Î6NVºvíÊâÅ‹qqqÁÙÙ™‡âîî΢E‹044Ì6~Ê ÃëÖ­£fÍš|ñÅ(P€R¥J1dÈ&NœÈš5k´º7Lœ8‘¢E‹bnnNXXaaaLŸ>�{{{æÍ›ÇàÁƒ6lúúú?~[[[ªU«¦ó¾åuRI#„B!„Bä¢!C†P¬X1–-[FBBŽŽŽê�º�nnn$&&2zôhjÕª…“““:ð.$W`üõ×_¸¸¸`aaÁüù󳓕’%K¬Y³ðööÆÔÔ”5kÖжm[âׯ_Ÿ+V0sæL>ÿüsfÏžMÅŠäÖ4sçÎͰ»Vß¾}9{ö,K—.ÅÚÚšýû÷«ƒ.\˜mÛ¶ˆ§§'Ïž=ãË/¿Ìv¶ªTÒ!„B!„¹HOO~ýúѯ_¿ ×—,YüýýÕe=zôPÿ622ÂÇÇãdÇØØ˜)S¦0eÊ”ìÉÿôíÛ—¾}û¦[Æ÷߯@œZ‹-<xp¦Û,Uª^^^xyy½Qž>2&B!„B!Þ»ÇH—.]r;+y–TÒ!„B!„â½Û·o… ¦y󿹕<Kº; !„B!„â½sppÀÁÁ!Ãu;wîüÀ¹É›¤%B!„B!D •4B!„B!„y€TÒ!„B!„BäRI#„B!„B‘H%B!„B‘EGGÓ¡CöíÛ÷^ÓÙ¾};†ÄÄÄ÷šŽÈžTÒ!„B!„y”¢(¹ñåÊÜRÈ„B!„Bˆ¬•,Y’;väv6Ä$-i„B!„Bˆ\tåÊÜÜÜ033£S§NL:•ÄÄDâââÐh4ìÝ»€¨¨(Š/Îúõëqrr¢~ýú¸»»sãÆ ‚ƒƒéÑ£ 6Ä××—§OŸªÛ·±±aåÊ•¸»»cff†³³3§OŸÎ2O·nÝÂÃÃ333zõêÅÑ£Gßë1ɤ’F!„B!„È%Ož<á›o¾¡xñâ2hÐ Œ100È0|LL kÖ¬ÁÙÙ™™3grõêUªT©ÂÞ½{0`�¾¾¾lÚ´‰€€�­xcÇŽÅÌÌŒY³fQ¡B,--‰ŒŒÌ0¨¨(Z·nMÍš5Y¸p!vvvØÚÚrîܹw¾ÿB[®twB!„B!ܹs‡+W®ÐµkWÌÌÌtŠãááAóæÍÐÓÓcË–-ÿ¯½{ʺÌÿ?þºä Ác* ©‘²•’’`U(QHȳÛLÀ3D’_! šÒVÝÈÃF…+­éˆ–©å)+4]]µÜ­ÓH2QTDùýáìýëðPêý)Ÿ™{†ûó¾®ësÝg|Íu]·æÌ™£{î¹GÒÕà'11QÏ?ÿ¼5ºº6#))IcÇŽ•$õéÓG‡R~~¾æÎ[gü‚‚küøñ’¤ÀÀ@íß¿_ëÖ­“¿¿ÿoþÌh+i�����°’N:é/ù‹"""ôÿ÷Ú±c‡._¾|ÃýÝÝÝ%IÕÕÕæk*--µø¶&“ÉdñsHHˆ>ÿüózÇÜ»w¯^ýu™L&ó+33S’®†@´x?þ¦>7êÇJ�����¬ÄÞÞ^K—.Õ¿ÿýo}øá‡š1c†š7o®•+WÊÎÎîWùó@¦!666rqq©·æîî®1cÆè…^°¸îèè(éjˆbQ;räˆ:wîü«æ‹ÿ�����+2™Lòõõ•¯¯¯"##Õ¶m[íÙ³G·ì'Ož´x¿cÇuïÞ½Þ¶JKK“‹‹‹š6mZ§Ì·6ß&„4�����XÉ×_­7ÞxCAAArww×§Ÿ~*'''µoßþ–ÞgîܹruuU`` ¶mÛ¦mÛ¶);;[’äìì,Iúä“Oäçç§ÈÈH-Z´H“&MÒ”)Sdgg§Ý»w+<<\;v¼¥ó‚%Τ����ÀJìììdkk«ÌÌL 6L}ô‘Þÿ}uèÐá–ÞgôèÑÚ·oŸ&Mš¤ãÇkóæÍjݺµ$é‘GQbb¢f̘¡={öÈÙÙYëÖ­“ŸŸŸ’’’4qâD}õÕW¬ž¹XI����€•´mÛVÏ=÷œž{î¹:µ&MšX#-Z´¨”øûû×¹6`À€:×zöì©I“&Õ;‡Æ+++ËâZ³fÍ”œœ¬äää›ú<ømXI�����`�„4������Àv'�����þÀ6lØ`í)à±’�����À�i������ €�����À�i������ €�����Ü+W®”ŸŸŸµ§ñ»EH�����`�„4������`kí �����p7«­­Õ† ”ŸŸ¯èÏþ³¦M›&OOOIRNNŽ.\¸ ÆëŸÿü§š6mª§žzJC‡•Éd’$]¹rE………*((Љ'¥‰'ÊÅÅEåååjÙ²¥>øàjçÎZ±b…7n¬‚‚mß¾]züñÇ5mÚ4µnÝZ’¦èèhíÛ·O~ø¡|}}• îÝ»›çþÝwßéå—_Vqq±:tè WW×;ÿ�ÿ@XI����€½ýöÛŠWdd¤²³³UQQ¡ÐÐP•——›Û$''«¦¦Fééé<x°F­¢¢"s}ñâÅzóÍ75}úteggë_ÿú—fÍš¥ÚÚZs›~ýú©k×®JIIQ‡TZZ*ggg%$$(%%E_~ù¥&NœhÑçÕW_UÏž=µxñbùøøhРA*++“$9sFáááº|ù²^yåÅÄÄè£>ºOì‹•4�����XÉÙ³g5kÖ,-[¶L¡¡¡’¤   õêÕK………Š•$EEE)))I’Ô·o_?^iii:t¨~üñG%&&jÇŽ $ÝsÏ=êÔ©“RRRdcc#I*..VXX˜ùÞaaaï½½½¨“'OšWÓŒ?^O>ù¤$©[·nÊÉÉQII‰Ú¶m«¢¢"µhÑB™™™²µ½/TWWkþüù·ó‘ý¡Ò�����`%ÇŽÓ7ß|#___ó5GGG=þøãúâ‹/Ì×þ´üO·nÝ4}útUTTèÛo¿UUU•þô§?Õ¿²²RM›6•$¹¹¹YÔjkkµiÓ&­_¿^ÔáÇ%I/^¬w®òòòÒ™3g$I_|ñ…‚ƒƒÍ$‹Ÿ%éøñã:}ú´ù½ÉdR—.]~ w9B�����¬¤ººZ’TSScqÝÞÞ^ŽŽŽ ökÔèêé%7–“““$éý÷ß—E»Ö­[[„$?·|ùr-_¾\©©©zúé§õÝwß™Wó\ï¾’táÂ…:óþ¥‚‚%$$˜ß·lÙR'Ož¼fŸ»gÒ�����`%žžžrrrRII‰ùZMM¶nݪ|Ð|íûï¿7:’´ÿ~………ÉÅÅEíÚµS‡ôõ×_ËÓÓÓâegg×à½×¬Y£ÈÈH…„„èþûïWóæÍojî<ð€Š‹‹-VÞüü<IŠWmm­ùE@sm¬¤����ÀJ<<<4oÞ<ÅÅÅ©ªªJíÚµÓ{ï½§ÊÊJEFFšÛmÙ²E 2dˆNœ8¡””­ZµJ’Ô¤Ieffjüøñ²³³Óƒ>¨o¾ùFåååzê©§¼w×®]•ŸŸ¯Î;ËÁÁA¯½öÚMÍ=**Jš:uªFŒ¡3gÎ(''ç×=H"¤����ÀªâââÔ¬Y3åççë‡~Ðc=¦µk×ÊÅÅÅÜæ¡‡R“&M4mÚ4hÕªUêÝ»·¹-wwwåååé…^P—.]qÍûNŸ>]/^Ô3Ï<# >\»wï¾áy·jÕJ[¶lÑK/½¤ØØX=òÈ#š>}:ÿ„4�����X‘­­­F¥Q£F5ئS§NÊÈÈPFFFƒmú÷ï¯þýû×¹Þ¢E‹:Û¤««x²²²”••e¾cþyÆ uú8pÀâ}ÇŽõ׿þÕâÚØ±cœ#®Í*!M}������w3�����0�¶;����``ñññÖžîVÒ�������! �����€Ò�������! �����€Ò������C¸|ù²NŸ>­šš‹ëaaaZ¼x±•fuçÒ������C(--•»»»Ž9bí©X! �����€Ò�����`EaaaÊËËÓÌ™3 1cÆhÏž=æú–-[¤M›6)::Z½zõÒ™3gT[[«ââb5J>ø tìØ1‹±+**”žž®   õêÕKYYYºråŠ$騱cJHHP@@€FŒ¡]»v™ûÕÔÔèïÿ»(???M™2E;wî¼n-''GééézñÅÕ«W/ 4H«W¯Vmm­$éüùó*((ÐðáÃåã㣈ˆ­Y³Æ<ŸöíÛK’üüüd2™tøðaóœöï߯©S§ÊÏÏO111úôÓOoñoÂúi�����°²çž{NZ°`Úµk§^½zéË/¿4×wîÜ©^xAƒÖŒ3äêꪷß~[ñññŠŒŒTvv¶***ªòòrIRUU•†ªS§N)==]3fÌ£££5j¤òòr…††ê¾ûîSnn® ¤ððpíß¿_’ôÆohþüùš<y²rss(77·ëÖ$)99Y555JOO×àÁƒ5zôhI’5j¤Ï?ÿ\aaazõÕW¢ˆˆ}üñÇjÑ¢…Š‹‹%IEEEÚ·oŸ<==ÍãnÞ¼Y=zôÐßþö7ùøøhРA*++»½¿˜;ÌÖÚ�����àn—””¤±cÇJ’úôé£C‡)??_sçÎ5·ÉËËS»ví$IgÏžÕ¬Y³´lÙ2…††J’yµLaa¡bccUTT$ÍŸ?_¶¶–ÿý/((Ppp°Æ/I ÔþýûµnÝ:ùûû«¤¤D}ûöUxx¸L&“‚‚‚Ì}¯U“¤¨¨(%%%I’úöí«óçÏ+--MC‡•½½½²³³Ímû÷ï¯Ï>ûL;vìPïÞ½uÿý÷K’î»ï>ùúúZŒ;uêT1B’Ô­[7åä䨤¤DmÛ¶ý•OÝxXI����€•™L&‹ŸCBBôùçŸ[´quu5ÿ|ìØ1}óÍ7A†£££üq}ñÅ’®†)}úô©ÐHÒÞ½{õúë¯Ëd2™_™™™ª¨¨$ 6Lï½÷žžxâ åååéäÉ“æ¾×ªI’ÅûnݺiïÞ½æ±?®%K–èÉ'ŸT=TTT¤sçÎÝÔórpp———Μ9#I:}ú´<hñ:þüMi„4�����Œ\\\¬WWWKR¯ª¶··—£££$éÔ©Sæ³`~ÉÝÝ]cƌѷß~kñJLL”tuÌÁƒõÔSOé“O>ѽ÷Þ«üã×­Õ§Q£«ÑCãÆUQQ¡ªªªJ‰‰‰zçwsƒO¥þq¥«¡“ŸŸŸÅ«´´ôWkMlw����ÀÊ~¹eÇŽêÞ½{ƒí===åä䤒’󨚚mݺU£F’$uéÒEÅÅŪ©©©³š& @iiirqqQÓ¦M뽇‹‹‹¨ªM›6Z²d‰¢¢¢®[ûþûïU]]­ÆKºzàoXX˜\\\´}ûvíÝ»Wï¿ÿ¾š7o.Iæv?wéÒ¥ë>³Ÿ n0ú=!¤����ÀÊæÎ+WWWjÛ¶mÚ¶m›ÅÙ-¿äáá¡yóæ)..NUUUj×®Þ{ï=UVV*22R’­ÔÔTÍ™3G‘‘‘:uê”þûßÿj„ ŠŒŒÔ¢E‹4iÒ$M™2EvvvÚ½{·ÂÃÃÕ±cG-^¼X®®®êر£.]º¤íÛ·ë‘G‘¤kÖ¤«ßF• !C†èĉJIIѪU«$É|~ÌÂ… Õ¿<xPyyyŠ—$µhÑBÞÞÞZµj•._¾¬¶mÛªU«V·å™! �����V6zôhíÛ·OË—/WHHˆ6oÞ¬Ö­[_³O\\œš5k¦üü|ýðÃzì±Ç´víZó6©:h×®]Z°`FŒ¡{ï½WƒÖ•+Wäìì¬uëÖiñâÅJJJÒÙ³gÕ§Oój”6mÚhõêÕúì³Ïäææ¦ˆˆMš4éº5Iz衇ԤIM›6MZµj•z÷î-Ijß¾½Š‹‹õÒK/iÍš5 WJJŠªªª$INNNzë­·4wî\mÞ¼Yééé„4�����àÎéÙ³§EÐñs må±µµÕ¨Q£ÌÛ›êãïï¯ÜÜÜzkÍš5Srr²’““ëÔ"""Qo¿kÕ$©S§NÊÈÈPFFF½õ°°0………5Ø¿GZ¿~½Åµ 6ÔiwàÀÇø½âà`������ ¤�����0�¶;����`Eõmåù½úßÀøuXI�����`�„4������@H�����`�„4������@H����€ÔÔÔÈd2iíÚµ7Ôþܹsª¬¬¼Í³Â@H����ÀïXVV–fÏžmíià ¤�����0�«„4µµµ7õ����શ¶V«W¯VTT””œœlQ?pà€’““¢€€�%%%éĉ’¤ÔÔT¥¥¥iáÂ…2™Lš9sæuûÀ¸l­=�����îfyyyÊÈÈPjjªš5k¦wß}×¢^ZZ*ggg%$$èüùózë­·4qâDiܸq*++See¥’’’äìì|Ý>&“É7€�����+9wîœfÏž­¥K—jÀ€’¤G}T .4· SXX˜ù½···uòäIyyy©U«V²··—¿¿ÿ õiݺõødø5i�����°’ÒÒR•––Ê×××|ÍÆÆÆ¢Mmm­6mÚ¤õë×ëàÁƒ:|ø°$éâÅ‹ Ž{½>Ç×éÓ§ÍíM&“ºtérË>~B�����¬¤ººZ’tåÊ•Û,_¾\Ë—/Wjjªž~úi}÷Ýw ½æ¸×ëSPP „„óû–-[êäÉ“¿ñÓà·âÛ�����°OOO999©¤¤Ä|í—_ ³fÍEFF*$$D÷ß¿š7onQ7™LºpáÂMõ‰·øÂc`% �����VÒ´iSÍ›7O±±±:w½½µqãF‹6]»vU~~¾:wî,½öÚkõÎ;+77WÛ¶m“£££~øáëö1Ò�����`EqqqrssÓŠ+táÂEGGkìØ±æúôéÓuñâE=óÌ3òññÑðáõ{÷ns=22RGÕ„ ¤%K–\·Œ‰�3Ñ][�� mIDAT����+²µµÕ¸qã4nܸzëÊÊÊRVV–ùZLLŒùg'''Í™3GsæÌ¹á>0&Τ�����0�B������ ¤�����0�B������ ¤�����0�B������ ¤�����0�B�����îååå2™LÚµkWƒmV®\©áÇëÒ¥K·ì¾çÎSeeåo§¦¦F&“Ik×®½³2B�����p[eeeiöìÙÖž†áÙZ{�����À8FŽ©‘#GZ{w%VÒ�����`%W®\©³}ç?ÿùL&“>,Iª¨¨Pff¦z÷î­   Íž=[ÇŽ“$åäähΜ9Zºt©úöí«©S§J’6oÞ¬Q£FÉÇÇGcƌћo¾iqßwÞyGáááêÞ½»RSSõã?šk¹¹¹ 1¿ ÓÒ¥K5mÚ4ùûû+&&FŸ~ú©¹¾eËEEEiùòå8p ~øa½øâ‹:wîœ$)55UiiiZ¸p¡L&“fΜiî­•+WjРAêÑ£‡^~ùe?Þ<vmm­V¯^­¨¨((99ù–<w£b% �����6sæLUWW+--Mµµµ:räˆ\]]ÍõÔÔT;VS¦LQ«V­TRR¢ÐÐP­X±B“'OVYY™E{I:zô¨fΜ©K—.)''G;wîÔš5kdggWï^}õU=ûì³6l˜6nܨAƒ©¤¤DmÛ¶•$­^½Z÷Þ{¯âããUUU¥Ù³gëòåËš5k–Ƨ²²2UVV*))IÎÎÎæq åææ¦éÓ§ëìÙ³š3gŽŽ?®ùóçK’òòò”‘‘¡ÔÔT5kÖLï¾ûî­~¼†BH����€AÕÔÔ¨°°P‹/Vpp°$Y¬r‘¤~ýú)77×°¼ùæ›òööÖ°aÃäääTï¸IIIêÑ£‡$Éßß_ÞÞÞÚºu«úõëWoûñãÇëÉ'Ÿ”$uëÖM999!$¥¤¤˜Ã S§N)33SÏ>û¬¼¼¼ÔªU+ÙÛÛËßß¿ÎØÙÙÙrss“$5oÞ\½{÷V\\œš7o®Ù³gkéÒ¥0`€$éÑGÕÂ… oèÙý±Ý �����ƒ²µµUVV–&Mš¤É“'kíÚµæmDÿãááa±¦wïÞjÓ¦úõë§ èСC×¼‡———úõë§#GŽÜМäåå¥3gÎ4ØÆÃÃC‡ÒåË—¯;žÉd2ÿìçç'IúöÛoUZZªÒÒRùúúšë66674Çß+B�����¬¬¶¶¶ÁÚäÉ“õÕW_©gÏžZ¶l™zè!8p ÁöžžžÚºu«²³³uúôi…††*))éš÷prrRãÆox¾];Nøyðr3þ7®½½½ª««%]=·çnAH����€•4jÔH<ð€***®Ùîž{îÑÈ‘#õÎ;ïÈËËKëׯ¿f{;;;éùçŸ×Š+”™™©òòrsýç+\***´}ûvùøøü¶s &“I.\¨·öóC‹9"'''µoß^žžžrrrRII‰¹~­ é€3i�����°¢‘#GjÁ‚jݺµTXXh®]¾|YIIIêÕ«—Ú´i£òòríÙ³Çü-NõùàƒtøðauíÚUöööÚ¸q£åîî®Ó§OK’’““•˜˜(åææjÀ€zôÑGoÛgìܹ³rssµmÛ69::êá‡6×bcc+[[[¥¥¥iÞ¼yjÙ²¥$iÞ¼yŠÕ¹sçäíí­7Þ¶9! �����V4eÊÕÖÖ*11QÞÞÞŠ‰‰ÑÇ,Iª®®VÇŽµlÙ2íÛ·O÷ÝwŸ^yå 8°Áñ<<<tôèQåå婲²R Ð[o½e>·ÆÍÍMC† Ѽyó$IC† Ñĉ¯»…é·ˆŒŒÔÑ£G5aÂiÉ’%æZ=”žž.777M˜0A#FŒ0×âââäææ¦+VèÂ… ŠŽŽÖرcoÛ<­Í[[§ðóš¯çƒ>ÐСCo¸m``à -I›6mÒO<qS}€µk×.ó©æhÏéæñÌp»ñ7fLü^�à×¹[þý¼[>çØ²e‹BBBôÓO?ÕùŠpkÛµk—~úé§¿íê—ŠŠŠn¸­$•••Õ¹¶hÑ"Τ�����0«lwú£ô�����p³8“�����ÜqÁÁÁ,âø¶;������! �����€Ò�������! �����€Ò�������! �����€Ò������€­5nÊ÷ �����Xb% �����€Ò�������Û������ €•4������@H�����`�lw�����0�VÒ�������! �����€°Ý �����À�XI�����`�„4������Àv'������`% �����€Ò�������Û������ €•4������@H�����`�lw�����0�VÒ�������+i������ €•4������@H�����`�lw�����0�«„4;wî¼é>»víº 3®âïëÆðœnÏ ·cÆÄï�~»åßÏ»åsâæY%¤qqq¹é>?ýôÓm˜ �����€1Üñ¦_¿~wú–������†ÇÁÁ������@H�����`�„4������@H�����`�„4������@H�����`�„4������@H�����`�¶·j ¢¢¢[5�����À]ç–„4·b�����€»Ö- i\]]oÅ0������w-Τ�����0�B������ ¤�����0�B������ ¤�����0�B������ ¤�����0�B������ ¤�����0�B������°m¨PVVv'ç�����pWc% �����€Ò�������! �����€Ò������€­$-Z´ÈÚó������¸«ý?|è'O_½����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/screenshots/screenshot.png���������������������������������������������������������0000664�0000000�0000000�00001565277�15003540030�0021141�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR��b��Ž���£‰÷Ë���sBITÛáOà���tEXtSoftware�gnome-screenshotï¿>�� �IDATxœì½y°Çu'ö;§û»÷½‡í �¸‹¸“Z-Q4µš#K–lg¼TFžHvÛ©$.'5K&Sã©IÆ•ªL*åØ3•'vìdÆ3qÊÛ‘%Ë’­…’lQIHâ*‰»H�IË»÷~ÝçäÓÝ_wy�$RòrO‘÷~·¿^NŸ>[Ÿ>M7Þp€#�"�eUMŸY¡�Tí¯`4ÿ£Ú•·j¦KVª’A¢‚©bDRL¿K f²W(× ¢ uÌÖgUUë™*�f×ÕS·2·»‹û/"Ö/ÃXj»*FÄ¥€ªˆF"0;ç\m;iÙ¹••ahC¯ÚárnÓLœ «–bªÊìT%udDÅ9¯†…\² ¼~=?ì}6œÛß©‰+ƒÑsÕó4š¡ÆR]ƒ�H 1�ÌŽ=PðÔð) @¦í¨wX£Ö2¹€BÞ;›ŽÃÊʪHT€É«j„*”­]ff‘ˆˆÄu £L�ŽYUIUDTÕ *‰BRÏ Ð<^afb""‰T|ãáH mˆ!²¦q99åÞàlV™ç÷¬¤=…¨¹ ÓŠ‰hÁ­‘Æ“–™w6üÂØÊ«z!¢c­H@3Lw£¢9ûÐú¯€ˆÒg"vL*"“ÐVWœo¬NGCÛŽ'DpÎ{ïCŒ1@A„ PÀ1ûÆ3qšhbQqÞ#/p66"´ˆ®¬Ø<ªÍn¢~(˜9J\€g®É¸p­´~ õf¶ÀijS6Ë4 8vS•~2µº !"ceFÚÎ9ë˜-|bvìÊ¢®9mÇÓуb"q ´€*:çbˆ¢ÂÌŽaL¢W¾e ¶šHãhbËÌÞ{°kšFUG£QTišÆq“˜"ƒˆbÆ@ÁCj¥â`DdÄ "1FN«sö°.SjSÕDH Ró¶ûÓÕ J+L:瘙O&“º¶ÒPÝ.Ub›™53¥R&ªØóÞ¤çÁÖý)xˆ1Nqï©ÏQ† Ïn2Þ ¢ó†iÛÀLÞ{òƾÕ8êÌÀ39©ƒ#)ˆ¢m;‘£*{ç¼w®)<߆IDeIZ¯Ê¬•:§0_?,_­÷oõ‡MäÎL¡}ê]JòEP­ Uu®I’¥{©«Î{£2­Ú† ¤ÌTæµÐIš&Õ,™™ÈA'ãÉx20h¾ñD.H$æ^ÏUUð4¦5ûplÌüª$¶¨Iµ.0‘B&!Æ ãÀDžÙkQ†1ÛÅcòl$,™™K­Æ8h°ºâœkC«ªìœwÞ{'¢“Ñ(„Ø8gzÑ©*‘à*(;—š†*;019…@¡ˆ2¤ÑTDB`f ¡%fçè"š% †BÛ¶m„yçÙ³'³#Šªˆ¡ŽÀÞ ÜP™%¨°B™ˆÙ–³W"Â웦oœaff/"1¶ªÊìs!LˆˆÙ°òª¤ªÞýÛ_£Ò´–kXÈXU‰4Ï$«FU"ˆ*A1†‚ˆ8ç'K AÑš áR–ƒUŒD9¶xg¹7} ²¦F[/†_ûË$"`‚*1¯ ‡`ÚØØ æ¶xçÙqˆ2‡ÃÁh<öÎoœ>mÝÖ(ļ²2ôÎoŒ6L~ÙÚRÕ¤iBH#�ÎqŒR_ªb°¯LU@äœÓÂäED…‰I•@ž€(1ª€\d·²¶ Ç 8Ç´1°s5â¤úRDâE._]Ö+˜ÙHQ…”I5ÄC€¤×E´4ÄÜ4žœhŒ†÷~ÔŽ;Æ«JÌžXUó"H ^YöĤTlÌKœbÎÕO}° f|{îT—¾õê3ßI‚˜™K%ó´kžª°ne‰?ÖP‘AMÓ�!0£mÓ21#€QQ©“ŠvDð\9² &†RªJ@7Üp]]Z+§€ª‰Õ)A¢ž,V©Ô«ª<ÝÔ·(’Öd7‹³B¤kg!ÈžMêBÎj +ìFÌDD†î\`º†ÙN‹hM ås1›§½žûÂCEduu-„VD½w�Bˆ�𯇰fIÅO».Õ:S]žˆ¢œd{/¢¯.”Ï›.¿©ò¥o½Î(�„vlV„÷޽'"Q#�‡ª%$7AWçœÚ§û3ßí²D,TèµzÎTê_Ó\R5=T(A&ˆ€TcŒ iÌ4*ÌΣ“¶m!Ú8'!"Dö^! 1­ÎZܼÿeõ€7uý]+ê 12‘K«žyà={UÛ¶ !03“ŠHòkˆ¹CØyçý 9þDBl#bšGÑÒS)Àc ê‰É;R†*!V°c°£ ±ø(*~ c¼÷…‡ 5Îf€A¢Œ[ï`Ë ˆDTE&g6ˆˆ½1€h¸²ÂÞE¨õ&™RɯÁL†ídíöð©ªݳsàÜ3Þ4N³®“\R˜q;WwÂÌ ÀL}ëækExe¢*O±AÌpÞÃÜýš`f©¦¼è˜DTDD“{ÅÜ"SƘfWF‘D¹@®ªGˆ(ORUÙUs¬(:+Ñ11©JlÃÆ˜ ä’›@ mÛÆ(¤: ˜#U%eGC·¬©îöÔìkŒQ³ám`6ªªšãÀT@óΈöVkí~¥Þ€vH!N8 ÌÌB©BN®KUUÒäã?ŽIõY™Y)uÒ9'"RÉ©MĨ`SfVÿ¨ËLFcfn¼I²,Æ("Þ{ç\â&ÌJ`æ&��[ iD` !ÀÁ¢QbŒƒªF¢ÁpÕê4„ÌkñÕö‡`—Mÿ%Š1Æ›¦3¿‹ÅŠivñPV£ œsÞû„^ÉÖ‹Ìø2Ý–‡…Ò6A83KÅÛK£ö¼¦ÌÍ9¿h'[ggsæ©x‚Šlll´!¬­­¹AcÎ)¬¯«š�ˆvúCýëÔbA^¹*•>@Ò¯,½H¬�4DˆØÅ$ÄnÖnŒžã¤UUÏÌDª‰È° Ô9bæÑxDç\ŒA•œC!�±¶–ã¦P…уsNUÆãqm‘È B4J((RYÉyx,ö.¨QoÐ`ŸU•™˜÷bÏ”HW"Œ,E„çE$„0™´Æ.šfÀÌ~8`Ož›Ô?%sªˆ±]Q‚FQe8r\èªVêüŒÓªXs)§¦ÉJ‹V=â,­ˆHTˆX NU¨üêHcŒ± ¢ÁP˜S•5ñ‚7®Á*™Ò“Ëh\e¼œUÇU@ˆ¼÷“6ˆÊpehneUoŒT14ã¶%Ï[¶l‰*ιÉdäÙ>y ÀÀ{IM&ƸȄ“*DUÕ{!ˆÊ ¬¬ ™y<Æc&nÏ@ÛUõÞQA‰Œ7‚©Yø¦iÛv´1òÄ…(#oßf¿2pƒ&y]³\“=mjÕÝúiϽ÷dÎF!"v£3¶@Tu<ž€immÕ9gK8Õw­¦ ÔEÓ×u‰{”3³Ö„ѳµb>tûj4W~Á'€²P(Êæ··M^Õï‰Ë±%i˜‰mÏn*:UqS;¡R×\nY>0³5Wä”=·~ξUð<Õ[=³8÷Þ—!×,ZÒìwý·_}?èûìk²àŠÈËÞ7-8 0L9cÌ*Sµº¬tÏy7ßœSšG$VáGQéÕœšz¥1#¥>cÜĺ$L{§¨`É#:5«îf_)ßJ“†9m4MÓ4.„`*M&f‡ùPtý¤ŸMu (×ö¤P€½�¶@g±½pUוOugäj"Jš÷JY>ÕW£_Î$Ö¯4ûšfúZ(æÖ �0$;[{+¼x`»ŠUóÎ?Š:˜mí´¿Ý)‡Žœ*Ô© ¢íÿ°m…pí^Ò£#"&e†¶µÒÍ`&ÁÅ0ksÍ}‹ˆ Æ†ª=I"Um �(T ¤ýU?h¼÷©³m€* %ëCDÚv�Ê‚(¤�E�Pg†ˆb�Ô”-Ûg£Š²cÏ<à¤}Q;ncŒQ¼sÍp@À£Ø˜<"2¹H‰7Á1G€˜#Ìy@ÌΦ‘’åi¢"E€${4³-HŽ ê#.˜&-"Û=ŸÇ¯T•ˆU2{Ì“Q®?gö]C7Sf¼1ÚŠÕ’+=šÐ²šz˦3üj«ÆšèC¥îK¢y2GhY+š®‹,b´sXï{½± µ¼ócµ‚”’X?©yTUE•³òÃImF1V›eÛRSHÍÍsr½YuŽÃ:ý$ "f—-¡ÔIÇD"±ôŸ²_‰´0-´Ýß)†• €ˆ¡É‘­"…sžI¢Ñ©ÖMtÛnF!j‚WApìD„œP“Þ±½I¯1€ÌòôTŒ7 ]@¸ÄŽ}Ã6ÏyÏÜô f&²}E 6'…¤­G©‚ÓîeB_Ïm”ž¼Xfç®Ö‹½¶.çméÏ­§FN­ù•Wzõ¦~¥ys=·þ©‘5ÑúÉÜòÓ-Î<Ÿ[žUA¬Ó�œE¼÷|‹z2ÝZD`Æ´%f_©1_Êä*ª¬,=CUÕqÙ´Îú½ý™É[\’†"T™9„°²2lš&„pæÌ†ªn]]Û²uÛ™ÑF!Æ0™´JÒ4¾qžˆÂ8¤î÷iCÍõS‘9@¢Xè+ƒÄÈÄ£){ qÎyæ¤ìÅÛ8™LÌb´¢t0h†ÃÕmÛvdûGÛÙ±cr™-«‚‰(j$°/³(¤$2er­›ÏfyeŠøíóý#„XÛs!y-¨.ùOMh‹ÀÒæ ì¯jö•Îúz;'"œ Q„Å­2¹Däœ !ˆˆa¹!ˆ4Æ:˜”"D[$oú“� rì¤hUÇ)¸€(F !˜}Ø H‘@Ð"äh‘a�“kýõÕc5šfõêÌŸÕ¢GDR¬œ¨Šœ9sƤÌh4vŽW·¬QP!rZÕIUm˜GNSµtc‘ÞNÙ5E±E<Y±û9Ë“‘Ea¯Ý ³LÕj(ÛEu[µO¿j‹k*G)Ÿù¡oöׯ”t&SËlC›ÀÜ2Æ@ºáT±ðÌIš3÷dœï˹ÊÊLúFÏwP>3³æÀÒªÒ¾˜ì¥ê*¶‚û¬ÕÊh¶–ç¾1ûµt¸&²)y_*¯šžÆR]>ž•îÊ…>›Kª9"Ææ(¯VVý¼QU™"£©Ï5ßÎ;]Rj s±ùÜvEâ|ê\ôžíëh·Zê2ªšý]»©!ªDÓ‘0½ú{?‹²RYñÝ3,–*ZúÊðªÃ³U•³Q]v•óH)ù ´ZEVÒ6s¤ÐÓt3AaB–­jï˜,Øl.•.ï¢ÍÖ`?å³½Þ a�¢PQQј|Áb8‰wéU:v–ür9žÂ6·Ä|/¹-ã‚F"¢JÌÄBU‚™òª0ï¹�VêcL̃&¨8bïݸm‰yÚŒ)š¤Yj…ºÂ|Ew율"m“HT"avÙ†-fŠÀ ¿ž­ƒlÞOñ+¥R(4\)»S3–-YšjèbUг‚@‰ÀfV:úׄ¢Ê˜™V¿c™¥zµ~/™ð�wÛ >ESžŠÜPOg©Ú•Ì#ÜY­¡¾ñf’pW\“YHÇóë(\y×3­ba£ÉÕ’‘÷k2M™`¦¸R¿BÍcªÖ×by_kÛÝÄ‘À‚Dr@…½˜JJŽ@$Zä+R\öÉy¥l§ }y¸Z<峉y¥ÃnÝäÍ…5äèB˜"u£ £ìe`T]2»¾B‚&¦*]c9’æBÐÊø/‹%c0'¨;@—ŒIgÓš´ú,xíë”Ì*ô3+͉M¶)lá5({æŠYEsjê§¾–‡=uÿ\ÔÄsV¯l^Sw6­?3RƒRºÌB­ÙSë@{ R(CL¼��3,Z'G\Z,LÇ5:ý-ÉcÍý)H+Ÿ}ÖÔ™ÙqÃDmBÃáÀÌu�A�Ç£ÉØõˆD@™É9çœ# 5ZЍZRœYç¨""EÀÎe·œÑ›Š/Ø¡3~€ãxc²¡!í: Ø5�Îy眼÷ì½X£ì‰tàr”Y–PÕ®¢«.LzJNFTþ–µSÎÔ?Í%€ò¡ ?-‡**ÇùEæÝ„ÚÛ®ŠžËÂ*šêÀ&}ë~µÏ½nÏ-ž•ÑŠŒ Ù U1†ɱã|˜ Â[4¥ºfˆÒùÉR³ª†êb帚Ðd^&)ÜSSUUD{GئŒ¯jD@ŸáÌà§<O1Dˆv¼dÎ9ÄØ¶­©¦Ã5Þ7 iìB9´ëìŸnq*znª·³@)žªé@1(ê#“6"b¢ŠÓÆAQcH³Ë  Ú~šÒ„‹¬/RÑ©ziLm±¥.Õœ£«_)+§T+«õñÉÔít~²MV‹!,¦üÔ\ß¶-\«,ùúy®ÊTÍÌWUEÄç×zS´ÀGÐ(G5(+jÚYR}Ý‹„ŽÎ«Rè7{.Ð1ŽúÉæµÔ=/|C3T<´Û+x¬ÑÚ¯èVi¯Kõª(^™Ò"²h© 7ŸLZ¢v82s =ªAÌë@ot”7ú¬‘Òl)Su ñ#Ñž7«Ô° äæúŠŽ,x­r|T{f Ýæ|ZruI9wª*Š|FªëF0ý†0Õ½yŸ¦¡…@ê`«®ù¤¦vë+?G-{â¹'›Jˆy±¨EcG¦A3pÌ-Sb؆%N³·¼ç%ìø@57d¸i“É„˜{""°Š„¶ m«ªäØ¥ˆyÛôTÍq¿¢ÊDìTTU%Ùì–Z swfÖ´•v8ÍÖjü Ä8 ¤3LÍJ$b?8æñdІU‚l_‘DaUUÛŒ¥¤rªRh†ˆ4nàœ 1Fç=)RŒfE+ ç¢pF5g£)Ú+«›zÕšíO§`u´Z˜CMžTýí*×tܱ~½¨‘WkZûÚ«½šâô0ûpöC%æùÔµÑEÇ/ÏéÊo¦¡¦'ülž2Ñ,‹»<Õ´d$Ìß²Ì_„rLª*QœI‘ åçÔ_JL4Ïo{½W~]×­4^3í)tÕÕ*Š„˜EÄž-Ü=©€�13}ât²Êô—¢¾—¸DU…¨’ÖLi®˜«GtîžEêªìƒéÙiÑ$ýªhfNUÁ £UçL)e³F—ë6A$jG–‘‰­£`î¼uÚ§Ãô¼S:ÿAŠ”H%[³•†[ *}É‚>UTØV¡1FÛb4Ë!kwÈ3U¯âÔ-Šfz’ç¨z‹ú g ç½Óó2÷ñ5=«ºd±æfÅ} ós£ôÍNdfeJ1‹ÔKc4BDÎvðDU¢Š¨D ’óNuö$úRu–+í€B!Ä••ápuà¼!„V†«mÚ¶+««ÞùÑhtúôicžÌ4 \ÃöºÆ¨•Ü[2 V1Mõ'¿%ä&º}Ŷ1¨s®išÁ``9¹•ESkÛNTÈ‚q¼÷AâÔØ©‚ÞTV¬ ~¸ÉÜUŠë4bÍÓ:Åǘ™úf6jõº¿*­XùZì²}¬Î¢È]©a‹yÑÔ€<Ÿ?|"°¦ æ( !8b‹—,øÔ§K’Óæ(‹µ2œ"ÕÄYŒRçó²�s±c&ÌDÄβ3¤±BfÌìC•Ü›—žQ³.è¢áÖO¦L¨?dbvŽˆbŒíd2™LVšÁd2aæáÊŠw‰üØòÎT‚žàÕzK!«¯=•§*»ø¹æ×Î;•;m‚‹íÌ31³¶QHPQ•. —Ž™ô…µ}Ä»xº…ÓZÌdÆ›NÙÉq˜êÔtâ)ÙYRÙÉÚâ©i»V)¬f·yšÛ¼Öë± sŠÔH˜*hÅ|•e înR’¬ž4Þ×”{T¤~)_Ô#šZÉ4³[UžÎà¬ì±7P…✛Y;Sq¥ÿuý/£H›DsÊ׬µlg~O5DD…Lý®ªÕ‹ÎhEB0+Íï&Ë»ïœHlw?˜ížjâ5—¯¿.‚Šÿ§³2ý¢íœ¸õõ¢:-<õDl3NË F�Èüº”Ž.fvSÒX¨öª]8Y<¤Fç–®õÎòpJÈ!cX‹>X Ú©šµ8”D Òn$3 ÐvÒzßX–Jž]M.ÑÌÓêAKzTûnj4L­nÌKOm´SL>·f9É ç•cAˆJÄ ÏN)vwÒ×;f!j›t#ã¬P2ÕKÊùCЍe8cçˆ|ˆQD‘³b"F È€µlÙ)©ˆJRW\’y �ŽD$J„…'hž·Œ…´ðA–6IŒºßQ$Ÿ† ZPDec²#¤Â+êe7ů´¢ç¸bAÓ>Ç)è¯åù9\ÔÌ@ô×X¢ØüvÝFr6Ìq lêªNìs†ç­£™Ìdª^JíÚÿj´Pz“µœŽsªYa ç<`™A’ª`JUH‘ Æ3Ræh¤i&i}!U•'L%L`Ïïˆz„åkIéWø§æ3í¥ªYeZÕìIšª­|îuü‰ˆÍéFN@lXªÞJYDÅU>—„.";Þl)�Ì¢îœH3œ¿îsÝÃó—j¨vL�AA¢¦÷åmN"réˆOš;é^Ÿ%K[t¶SÉkP‡¡ÌÛçrL´ìr§´¦ æSìgJ±wˆêš¡ì*´ó6I\¯SáìæîO·Ä˜\OÕðÌC@iuÚJÈ|$ŸeÎÀ"y§‚n ó«ÙV¦Få¯ñùî9ŒŸH‰ `­+36-ÎùÆ9�aÒ†6X2㖤꜑Nn/M˜îDïÐ@Å{�`bhHUCbnc¬ V¶l!¢6†ÑxƒXB"òsÞ3qˆ!„ !0yRP;™ÎE7 ú´g8 ã1L»sNSúöε“IŒ± €÷Þ{¿:XYR+1ÅŸ;oi¹-U“–çÞ§äÍURá¢F4ÅsêbÈvBÕÕ^œv­ÞÔAÑS‹½„RÏš]7²FQ¿©½ÎXYf@íŒ lr;ß( eàu?§8Õ\8w“×9-uµ}“ÞVŸ²q2ßM #ÎÔ¥Q*j/Љdô6±)Iж­~&çó&… ³l? Y"Þ8ž ÚyÛ¤Y–u ¨FQÍl2&‹n*s rVˆB-¢xâ ÂošÆ�¤Mi:K­¹˜ÎWî©Z%rš™š^‚59E·%T&q˜êuª’èi6ðæƒfƒ¿ê ©jTàV® wÑ¢xj~žV¥Áyë”óžTV&º•¢Z¹À’I_\Ž+w²é3Óù<»}ûÔ´â2êÒ Ôrg"7E·FjªÊHÓ1T}-0*åÌvxŠIcø/’b¬)M.T«µJ} í6-˜£…°)SŸɼÙù]Í.¶V-ä*m¾Ž-¡‰Q*—¾I5ÝPy«î¥HK­uPUjšAŒq2™8ç‡ÃàÚvBnñ¤ÜÄbœQ!H9Ezu„šôioË9«¹žÙ¹šQ/ºišqp fÅâMëIW+Ú@aØÙºëá3:î¦ÙìÂâÚûQÓùCô½,eh2UQÓÕì”eÈγª–Š)ÄC¤¬²‰…!2ÊY†ºi)Ó¤cyö¼gø[êKÆHÓ¥ ‰EXßIAì Ýö#e¾FDd¬Ór”rÞ²4M€¨Šª˜Ÿ\ÄR?RoGY`&y¬Æ?‰½XX ;n\ã-ãšj1ÏH‘Ù ¨¶“6„à¹ÉØQX8ƒ*²ëÁ8_ IëÑdÅXú…äI€EɦèmMóÕ']UôYhMžÓôÙcGYQœ18SWžŽ™EØUUZYPlŽ —µ”B*ÈìˆÒˆ{’+såº%ÜÉh*6TVàM¾g!Ô!¡cÇ4C¢½x¨DŠjyÃR‡&QåZëq_ !&3&Ê’ur¸²8åSY:Õ±)¥þ:Wí®ÏIvT‘7 kÝb¨ªwK.1†*{OvLêFʹ@"Al/šˆ2#*DX’QiŽ(VUšNÙ—sHk&žÕ3u¶¹Â’¥€²v'"€2[ÎàäŒiúrÖr"î’D”˜ˆL6¿k9ÕS¼+;¬Qc~v8‰µæéžšÁzŒõQ”2õ¥æ¢è—ÏZ±ÊÒ“^4G†*®SUûl»Òö¬Þb”�ÀL0väÜÇ³â®Ø9ÖªáIk›+ó"¯‚N¨n\óÖ‹, wÊH’Ï× ´dt¢¹Î¬LYêQ-ê5ff\«¯Q¤iÚNZeÕµUfïgv!ÄñxB`rƒÁ i¢éŒºX&�U0[ÖÅ)¢ÒjØÚgv¥‚aCr ´èRT‚Ë¿Ú*&r+ƒ¡­…£Em£jh6RºbWI߈šžôyó8˻δ0‡o5ËdX:–NYñ%3 ìÄûR‘N$™»L`ÔªÔ¦ƒê~]Üÿº‡¢˜*∅”ƒAk) óÀm“LÒJ9W¥•Ý“ä\•;ƒ’¦‡â„@6Æ,O’ÄäÄÔ¼{dJKwΤ©3.$½ÕmÓ`¨W™Î a'Yt™ž™)F™´mŒ!„ !ZÆÖÉxË–­ƒAÓ†ÔFD“ЦèÑ¢àÏTÞá÷|=šó*)s…4·¯Åíe ­^1 ™ŸÌû‘´bàý—ÌMPk1åy§áØ×z1r§‡L ¡›ÌJFtM3S.7G¿›ƒ´^·(¿Vu‰‘º�� �IDAT,¥^Â¥KµàÑ›9-=È΂®£ûMl°ÌP[qcÕ.®¢¢xŠd5èæ£{-¬å…»P+êy85º '¢ªÀ¬g¡ç>(¬€zkµ×Pé[Õa­í‡JÖ‹spI>K,¿ÙÆâ4»¬ÌÝÜ¥ª7 ó3³9EæYï_Ôl%øç$Ï”ï2¬Ö¤RãœrذmA›5£¹X¡H¢nJ¬­Úè™êÃ&Aµj•Ííy©­V}´Ø.}zK««°ƒÌzºåS“Q® €æd9‚’HA}¤/ΞM–õÒÈáuëY{_ç>)µu¦c;d¡j'0ó�“®…˜ET$²˜+c™´b†$Ç$4qí´5Àéb<›þÌ X1ĶmUısg¢cQL³HlÛ ûªØm Ýtä©Í3cæd½j4Š’Åjp·NE¥ä€H:rf(˜IÃ!é¦ÄqÙE¸0fè-{ ‡î˜Nù~Èhù¬fTôõƒ> êÓgùjõôÝX45ï3Õöœö|æI~Þ½Øõ¼>Mg¢R©ðÀn]Tÿ# *?iºc*+a’¼�°[0ë…9¯$¹A‰ÊM“šÌË^ç)ošÆ»T˜´´¤œH©%’¾ fM9÷rì^„¢„hûuŽc5î0 Ŷ'‰‹!¢æy+çÝ™ÙA‰Òž  Ë'ž¨ÉÑ(1¨LO1“«E<eqŠM¦,¼2mJëEQË’K¢­È£jˆ�DUcBÐHÇÆB•Š#šäžåù°ÌÞ%êjº€‘˜D2ÏBgá'¡ST”¬§S&�M¹Ð‘sö7Õî©©ìÉOw òÌÏ‹ñLŒ%•¯-Ò— Ïésy¸©|DÍ:’ÎÒ-de Õ\÷¥¯X 1,#LZ ÃX£(@ 3l [f÷Ê;™}CÊJš%Ú¦" sÊLÏL:�Åè¼4CU !„8‘äš3 AÁJU5FóV3[νìþÌç„Ó6xꛡ Ów$D QN”k¼gòL)_ƒÅ @¨šhfò½év†E5³g†å♢ ®dÖÑ“&õ”Uºh‘H:U¦~·~RWX–dž¦®[¦†\¡š2¨v>‡îuô–"HHê½bÕ”ŠdZï]H–¨³2L˜ÕÔ­<„@A`ET˜!ª„(BÎBQcm( /Ÿ9{fe¥$—w·@ VÍ‘M='£­R€A "Q‰1´­U¤•gŸ™#At~�ï´¾[>g‰@�åMP©H;™ÄÉ$ÅìhŠ^‡Î;E…‚‚ô]*Uú—𞦺˜iÍç4Õ6çôC$”–¤0š…FB_v]:¾×®ÎÛïa& *lg‘od®<u Õ¢7Ú»Õâê$NÝÊ”8(õV?…Êý1ð®ý‰íZȃí–9€¤Í«¦³-¤° Îiôák¦0ûA•&QÝ<zÎ0uMøBýèl°¹¼úöëùÞ:kU³ʓʜ·ð.òC�A3Æ^®~Î>™ö$¼­œ[áY6_iI —±â¬õ‹çÒìÂ2ç?ÞŠsTT}Z$æ65U[Ù§¦{…�Ÿ?tǧgÖÐüjû½›ûuQßÎúë\. ÕOå³Î–çùllQÍà!3§`¸þ[“��Ô¬ƒ¡£neÐ0½Z�«Í gœW)x2ò»P/�(<:#z¯ š»žSfñ»ë¥1ó‚.ø¼¨Ì9Âì\LéïŒÎ>8_úIe­‚™%s.µ+;aN'šn·6Õ*' ƒå/È׫iëÈwVôõàRú;#m³Ç'}Ö|*¹:Š—·è̻͜ò´sÄ)(ÛTQ=u¼j– ‰˜ÁD1Êd2šL&«Ãzeç  H &Sà4Vª|rÇ¥Ãü(&Dê~GÊ�Mž¬â-)žšî“¡ej$èÐ ”©VXj:;j^n‘ÌÛ_„”œžE´Ü‡F)½™Ý?’{È`0ÒmÔ̰ߨ‰hÕÚÖÒmdm‰òÆ,JÌQ­Ù«ÕE~õÜ%ÓO©}ýƒþ;ížN/øY+nÔ¦ZUÇy3€ÙWªÅÚ“·I³\lnÍ÷ìdÌXÛ%"ËÁ^^ ”†pÄ̬!N&!„6%Á×ÈàÕ0¥Ù§¨7Ê $@—šÒ”&˜L&À¤ ïÉ7æ OÆP�ÌÎþ#ÂÄîY$&‚¤›í…)oíV»¯i5ÍÒf*Ç#bef‚@Ìαwv1D%@8æÁ`ÅyêÌI«Ó9ç¸A ¬±4ŠÞ»bÿÀ8J5•yZƒAéÛì-…Û› DýÔ¼si¯f†$S ’üJ·™WÕ UYkÎlaN.‡U…ù×È·?‘-æ\BLÁó–Ä$9UC/û ,z*¶›=+ÌËI™¼žŠo%™ªÓ4ÞûɤµÃ–HMJ !NZËßQ¶Îìè~ůÎ]Àæ9*œ ÆÄ¥÷Þ®Á†B±uë¶Cƒs „ LÎñLÅy4½G•'±ÐÛü§ž•ÁDµÝaÝÊÄJqÖ¤zÄ•íÝÄŽ@,ïAæfétu7WJEåËÈÛ}·Q‘ŒjÞõî°Ofh÷ûˆ“Ç¿k‚{'4ëU<ÒɕҸîF΋JYbñzµ¨$7AÉGB9çP&ZìÙ{‘Â-°–°„%,a KxEAáØ±gÁ™)n;R˜(¦|Úž@mlÁðÎEq�Μ®­³DiVWLØÅ½÷p¬1šö&*ÞûL>&\Ó탚·BúŽüžýR$z­y§C+Mnê¯ {g{Ý1š´.A3i>Ù. nœ×hi”T\Ã1–)]êH SСΉ)%ìØ©†ñxܬ4È~TC¨Žåký!ífTª­}038ç5®Nj�DBë¼c¦ pŸ„D`¶ÓlWĉ°+#éüˆÕ/ÙMàg© •9ÆX®bµÞX +b»Ÿ=ˆ->U±å´/SÓ;ÃeQ %²WÕBÐÓµ~ÒG»™•-d8…¦Jj–Q`»ÝUºð„Êâ³`ãL ÕÎ9íÛv¿›‘¾k}6|ù©¨¯e²fé°³ß"š]lwÃŽ”Y/ä Wª­ÝfЄ¼óÐtã—wž‰Ú,h@S?mÈìJQÆÏ aâ@ÎFãÑ ñ"CL´'‚”ÞÂf¶LnÊÊŽ<æŠ1:v!ÇŽAÊÄ6_ã6´í„‰Ch•h06MÓEìV¶<êLÎ{ÄL,9ó Tí8‚’cÇér-ôÀ%ßP„D!R8fïlQC-Q{ffŽªv ÁI(д/ÀJÌž©Ûó¤Bä‘¥6,§Šê)Ö’<?[¤6;vÛ߬`jí×]ŽêÒBðìPÝs1E`åÝÎ,! !Ë+Huàœª¢Ü“ Ô) êÚj¨­Ç©ÎoR¸ŒHí$£j†++:™ØýìÝ–Õµ”Þ_DÕa$œ9³1™‰‰5D˜(„ Qxೇ57Ñ5›l9si>–˜<swÚ(O³EP‚ÙƒàØ™HçÍc–%˜Œœ(´¡ Èsªì¥hG¶¼W#ò/�ª]âvãù""gNžò¾!â7¼¶e™ÇãѤ sf!sÔsOÀV¢v;첫όÔzF8E ‘ä\}÷B>¾ê¶ÓEÊÎIŒÑ2w°‘Š ¶K¬á'„ Õ¤#g™!Ù¿Ìl²³,%1†cÑ9ò+ˆ¸l«w|8›÷i¡ÎV˜6†Þ±¸i6žFÝ4ƒy¥§Å̆òÊœïæ«É~*fÁf¾ñœeVýn}úÒÛØ4û$l¥™ûxtïÞým„ȤÐÁ–°„%,a ß1 "f·wÏEG= äc0 4McšDŒÑt_efâ6¶šŒ%‰"f“ÅÉåtDfÊ2‘PɇDRÅå¸é¢ ,¢tÓRÑb“q;ïæ—¤³j·P”!;çbì%15ƒ&ƈÍ”2¡n{VÞ®åN·pÄa2‚À°írÐÆVU‘.r9æÕ•Õ€0ÛOsmf­@gÕtÎWIÕ…µïh¨”‡lÿ—-êΑæÎ©Ë{û�$Ÿ\‹PŠQEZH;XBštÁÒOd•¼¸3´¨k‹ç1½˜ U¥î4Éô~”sNÉòOigö8§È)-™HÒŽaÚ\Í0划*–®g®Í½Ü‘þ”Øî\¶Khn~èTQÞdëÇöt†_Eð¦O3³©ÄÌLvš J̵âNDla:S8OWt÷×ñçŽX&ÀyDQ•‰L ªQœs$¢Âì4áϳ#k˜=Wí|U…H:(ëŽ:êMš®sÌÌžU5„0™´"Ñù¦iä8%¥‘sÜá!¤Ì;:5Ò2#\|È8H&: È騑v!9_ƒ‘²£ŠN€sñ_A·P2ÎgÜ=³ÄPLâ²¹º¬¨²¾ú&´™SFxñL5dCS¤ñ%ꢽ;’k”UCr®ånÏbcê:§ê/© Ô/9‰ÈŒLbRBŒq<‡HTDÝÀÛÞþÀ{ï= ÇçavÞ#]c±0ìÔ[yâ½w΋DóÔ–.´m À9×8g—Ë$ŒD$F—¬ê”)vb«Ú7÷A:ÍËiïF`Dvù�DI1éHá"1„ ¢ÃáŠYÎkkkÞy“ªÜ4Ab�ÕÇg7Àä)�)¸„tå0¹Híyççâ­¿"ºìV0n“Ó6k¢:R@kó8Éi ±/Î9d¿€Ë‘ök!¡²æ7¡F›€)z+­ª.¿ÊB¾=Ss/6'µ’½ C2æ×UuÖ—º§]4\q¯Û‡”› Ã…vé—b$öƒ89³¨%,a KXÂ^QPÕÃÊpM…#‰¤Òtö0ªÂ‘ÛöŸcvÑEqì¼÷ÉV6Ôvkɶ|Ñ4N9íI¦­�JO©Ýó•ûÅ”¡ÔÛ¢ ôÌTä’æ+¨ì7Îi Pô�‚B76FPeyǃÁ`Ðx qtfƒÎ˜X@«ÏÌPÇÔµ¦\€'/¶OCÌd™=AÓu² =ÐjeÎÇ~‹Öb7 —ðþê­ÈP²Û¤`‡8 kJrg“Ϥ’Ï‘€r–Ê÷Q3³sÌ.HTeŽÕæ!ˆº.wÇ °©zgc.û9éCâônä¬5ej½mFIÙC©Ë§e;~$LÉ „¼îÞ,³RRa(,uö$v¥”wª²,<Pžkw{WOýV²v`»’æ}âbç•\BY…NQÎÉby`­çj™ÒÙ.K³38A#+ˆÙHÄŒ‰1„`GcØBcòý5æJËùðÒQ óEŠ)ÈÚq±ŽÒ*�V8bd,!黎˜%jCÛ…9"òg׈Hˆˆ *¬”è�bÔ(ªžœPºéÆZcKÌC‚]–Q_$ª¤9`<mERrÙ)�É954ù†;*n.N7ÂláH¥õîÀEž—ù|Vý|ïD£j6 Fý«Å7M¹ (ÆBâ|QH¦¶i‡EI휶ÉÖ0ÇLG”™æÓm©}ɬc¢nb.Ø%šSžƒLüFëÝšJ6µü70M&c‹íjØ©Fê(¶Á5Y¡É'”1EªÛÊfú#¢Çά¯BÛ¶anœÚzáÛ)|AT&“‰‹¢ŽË=­nEœbŽ ib3§ ìÙQCãâ@*ÚJ´;á¢S&‹0"cà‰íWS05#ù­.H®Sž…2‰›Lå\yJ9KËÔ¯v´'ÇÓ¤udºq¨è¡¼høWsÕ”þ–‹óˆÓ7ó�Ï^<=Ã3ØÈî³ÒøfTÛÿ}jmÖ¾^tnÒÜN¿–¹‡‰Óiå óäX.-ˆ$¿Ÿ<7Ýtƒ*DìTPÊ“¤ªº1që»ön¾—°„%,a Kx¥ˆ^|þ(ZnÙ%"´mKÄMã™]Œ™=¹¶Ç‘ªn_ß$ŒG“áÚª´ñÌ™ÓL4IB]ã#0‰DSp»¿{c]¾kÐm'Ú™Yg›è›³ë½_«w»’•Ë‹ª6©¼P•œ?ä¹ù‡´;ûœÊ—ªÂ%o™3i¿Û½H`ä±ÈöIÄ“á/•‰4Õ·s…)wdY Køë ŠJªQGÙ·›~¶P¬âqÉÎ'Ê—¢ÅÁ³„%,a KXÂwª㜚@ÓÑ{çœí"寂Wóuz(á÷Ê`f‰ ç8ŠDÑÑ왟‚gf—n©9-a KX–°„¿ê „pôè3Š“9mÎÌ­r€Ïy59},>‚¥>´„%,a KøËªù®xRŒeŒ‘"â\³:ƒã‰ÄB@>kW%J¡ã‰îÙ{atn ž¥›` KX–°„%ü5"bvûöî?rô ¡Óõ±; ¾SU_/‰ „r»)OèÙ¶{÷Ží[oÚ²º×7�Ž´íá3£¿tò¥°É%nKX–°„%œ¤˜7»4™9he;+›rŘ pD“І]“Ô†h'@U5Ѝj D<ˆqã»=²%,a KX–°„ïhÎúõ%ŸQ¾ë1g ôÌÎíæ·JŠÇs…×n]ûàÞ]+Õ•<— — ïXßúGŸÿ©eD€Öv\rù%ûV'ýùC'üw»7/7ЖõË®¸tßêøÁÏ>øâ_»Ñ-a KøËYR!¥´ƒÞyovõ]hÃŒ4Lœs¤ˆ1ú³%Éc")W©4-ôä\Qß‘¡-a KX–°„%|§àÌÆ†EH D9AfNO¡ªìçÏÞÃîÁqLŽ£JÔIûðÚ­k?»ï‚•y×ö®ÿì¾ ^»uíåÐwdxÝ÷þÜ?ùGÿïÞ±/�P¿}ý¢ w΂+u7¼åï¾ÿ?zï×íø¶Â+α¹ï0¨»þŽ÷ÿÇ?ôžÛ¯Ý¾ Y–ð ‚Å8b;e "ŽyÜŽ£Æ¨¢Pö$„‚ªŽÏŒš¦!B#1“ã¦ñaX0§ûv}Ù–°„%,a KXÂß0¿€ˆ°sDJ¬  X!Vo×4ˆDM·]ŠÔ»srl÷îƒ{wÙFˆèj• q$²ÂLÀ÷ìzhc|2þ51 uû•×\´:pW^y±ÿä‰;~üÞy-?ñ¹_ý•?>:ÿ扗³é•o³9¿zÕ{~ìG_ÙäSÿþþð2½A¦þ¢kÞùî·ßzɃы}ésúÈŽÍôáÊ,a KXÂ+**¤Lùd»àÊÄ È;ï= ‘°8.€23¢T's>T<uÚô%,a KX–°„¿Ú@È·jI¹*™ˆ‰¼Ä¨*1»ñU¡Ä„(碽cûÖGðó=uÛ¶µŸ¸`€óÜ Ÿ:yê×®¸À óÛ·oý/œxe†ö÷Ü'þðÿ‹·n{ôðWD×WG¼â@ßrsäv_ÿÚ·¾óŽ[öl!èdn‘­—¾ç§~üu[8ŽÇqm÷U·¿ûÃð¯~÷ÞÓî¬eîKZ–°„óqwå$H×’+;ç;;W眓ìrËÇ“sô˜PœŠ‚;‹·Àûfß¾½;×w¬¬¬Ñh4~áÅŸ}öhí+4Ô%,a KX–°„W¨Ê]XT!bò“vbWOÛž‹}"bœÃ¡ƒ›¶t Þ½¾ý^8ÜuòôßZß^~ºyËêùº ”v¸í]ï|á‹wøö…Çþì}ìóÏžR‚»üæzÇ^°kÇÚ€ÛSG¾þðg>üñûŽo(€Û}ð¶¿õÎ×_¹;ŸìáOýÁßs|¤wù-?ôÎ×ܹcë–•FÇ/<þµ»~ÿÃ_xnC p«×¼ûÞyá½knrúä±{þìÿúÐáñ•‹Ê«{Õî|ó­ÃW­<rÏSÇ�€Ü%oúùÿéM€>ö»¿òkw¶î}íïxã«.»`•ÆÇ|ö·óÓ†ï×üÄÿð‡M{âÉoÜõ>ôù£gtŽVª´~àMw¾íuWܵêÂÆÉŸ;þðÇÿð#¼€Í¥ÉÜ~Ýûÿá¾JÇŸÿWÿâ÷ŸnÁÛîøo~áûÖù±ßû—ÿÇ7¯ø‘¿ó®ƒCˆ¾™‡wÝþ¶W¯±<{߯ÿòï>sÍÛæß»ïu·½ú£‡?½!g+sßçR.ÿáô÷Ö×íñ§ïýãüñýφå†Û–°„—TÁL”ÒíØi9òÎòî*©ŠÆP%æA/Ë€ÌO`¡g‰صs×å—_æ\r›ÑÚÚÚÚÚÚ…ûö}ãÑÇžþùWh¬•a¸º{÷îƒøÌ7žÙxÅ£í^A ­;/;xÑv·ñõû=åÎ^~ KX–°„¿z Z…X‘*XUE "“Ѵγž¦™ïÛµãÝëÛ?sòô]'Oß¹¾ýGv¯—ŸöÎ3­­î×Ïüä»n¼l×­®\põ-ïý¹¼eW(í¹ìÚ+ö_°Í‡3§Ç~Ûþ^û·ÿ‹¼i»€Õýß÷Ó¸ó†K¶‡“'&ÍWßúÃ?ó£7z”ö\zíåû/ر‚Ñ™1­^pÕMïû©8¤�dÛ?øão¼î¢5=ñì±—âÊǼIù9HE8ýÂsÏ=÷ÜñÏD�XÙóöŸýé|íÕûÖx²š[p:ذ@ÃF6ÆXÙuÅõ?ð“ï½j^…´zñ»~öƒßË¡=«::=Âêú¾K]yÑVZÔ\éÇÉGüf ^qÍ%Ї®ÜÍã~õ¸<sï§>ùçðk¿òï>3ä^v±#?|ï“ ñ×?tJ@{.½lµ»ðbq™4ºfû®­Mhmïeozÿûßu`å<¦| KXÂΈ”Èü«ÌÌ �Ä<ðÍp8žœÄcDLNiù‘[8·6wíÜuå•WA ι«®¼b×®]/ÇØþ’‚ú‹¯zëûÞó®7^³-€[Y[߱Šyîâ«ÞtÛkn¹zÿÚ+yäð;óí�ï=xà WÚ¿kømd:¯~Ξž‚—ñ­%,a KXÂßlHê‘´¤î³ŠwÎ(J$"erRàœüuÀ Y-{¾ pýÛù@Ü~ûÛß°Ýé‹_ÿ~ùß–µïùéÿü=.¸í7ößÝmÂNõ·~é÷žÜuá;î?½c÷¾;ÞvÝÝ¿wïÊ›ßþ†í.>úÙù¿}ôˆ_yÝÏýÂ^|èõ7¯þbÚèÑã_ùÍ_ú½§/¹õ?ÿ¾+vºî€ÿÚ7ÇÛwíô öáÏþïÿç§N0±#!ø…åçD—ÆoÞ÷[¿òÇG��ÉöÛ¾ï¶]ž|âõß|þĘ׆îLN”õÂá_ÿç¿ûäWðÞwhûå×ô?¦*ÛqûÛß°ÝëÉÇÿ—ÿï»Onú™_ü±Kýâæê=øÐãaÿ•;¯½æ‚<ròÆWÄgyð%A3¾ÿ#•߻�å´eÇ¥“'NDää‰SŠ-ض¾ 8}Ö2lt_ù_ú]]{ýö_¾÷àÖ[n»ñOþÝÝ£¿Ê›HKXÂþ²jrcD„˜1 Dä¼QâÈÄDJŽã¸%»‘YËeŠùÜAW妑Þ7—_~™‰18×ñãü•®¸üÒ“/½Ô†° Ž… Í®ýW]sèÀîkGg^|öÙo<ððÓ¯à1†Õ‡®¿êÒ ×·4@{æ¥çžzâ¯<zb3q¯«{.\÷Ž÷]¸‹¿êußÃÅtìûø—O¾ ÷Ú(vzûÛnÜ1>ùÀŸÞõÀ);'ßö¾×ïr£îþÈWŸ^láêàeîÌ· p{^ýœ[¸ž‚‡N¾lo-a KX– 9—aÞI)÷!’]–˜k D�³¹ ÎÇÚpp0°Ï¿yìùÏž<õÞ];�üÁó'†GŸÿàÞ´Ár´=?åIö_|ÀNßÏW7"èÄ=_}ôû/¾nåàÅ{ãÝÏÔÃ:ýô_|ù‰7¿åŠ•vÆ/­Üßá²Ûþ«ÿñ¶RdÇî=Œ^<h|îɧNÉ[W¶mu�»ÿ¡ã·îÝsÍÛ~á\ó•Ïßýé»î;ÒïM¿üYµ·xÑ¥„Ó_ýÂ_+#nŒcö;Êõù'Ÿ>%‡¶®lÝâÐü‘öŸÜb{c¾ô¯ÿÙý_ì g¾ú¥{N¶8?ÛúÂ}ßxË•W_xÅ5;/¾ê²ðä—îy§sñ ­ŒÄ(@8uÿCO½çàUƒ}{wF<³t,a KxÙ@�V¨ @Ì,P‚: ‹k£ÚP˜(¨2;RçDDEóé%¢ä<ïIº9ƒ /Ü[âî¹÷¾Ý»w]~Ùe�}쉣G½áõ¯àœß·oßSO?}^cÑá¥7Ýñš+·e&éÖ¶í½bëÞƒûþÔ§¿üÂè[AÏÙZtû¯½ý{^µ UQn]¿hß‹6‹¥ç“¾O/}òi€§,/  ¶]{ûëÏ|ì3·ç³­ðŠtæ[ƒMq{^ýœS¸7/ç[KX–°„¿éÀÄYûQ e†P/"%p s'(ôdõ}§GÅMpߙ޽þîõí�<ÑÇNt¾ë{Ï,¼•z>Ì$á_C„©‹�¾ùàg>–{Ogy®Ø��!,ƒ#À“û?ö¯ýè÷Þþ¦×\yñ­w¸áº‹~íWþèÈÂò=H­0Õ¸bK‹%2/ïÀY*Ž�Š¡}}ns½/Þø©÷\}p÷õoøž—7®“;�� �IDAT¬ 9|ïÑÅݨß<õÒÁnÞ¶}»£ç#oÛ¾• 8ùâÉs)#Ûzu9ï�¨œÃ]KX–p)DŬ}�"ÒªŠˆª’h:L€ˆ™•IÅrÂ.÷1&ªR1ÓA;«<;^xá7¿ùMÓ±cÏ]tÑ…]±ëçé&XÝsókmc<÷äá{zæôd°ûÂk^sóe«kW½þ¦£ùüRºà’×^ùÎm[V …ñÉ#Ï~í+<~Ú¶Ü•¶ìºêæëíÙ¹BáôÑ#Ý÷åÇO·@~këÚpÐ8 gŽ}øž/?vzÈÎKnU•ŸúÌŸÞsT¥Ù¾s7ON06} î¢K¯¿úòfßàè_8 �Ä{}ß蹿ø“O=qJ XÙzÙ7\µo÷–†Ã©“_ÿ‹»�Ж ßøÃïñNFÏ{äžû¾~rn ]�D[.¸åM7žüÄáçg$í‚‘¦{ynûmïyý>iù臟 Ûn¹ý‡.xæSþóc£Á5¯{×܉'?ùá/÷еWÝtÝ{w­±œ>väk‡¿ò“c@±~Ñ×]±oçŽ-Æc{â{>]í¸]ßø½¯Þçôø½Ÿ½ë‘çB§[l‚Ûyý|â”Ö¯¿ãÕ—¬­ËøÌñ'¿qøËž\P˜»)øâq?¸è曯»x϶ÆÅÉèäc~îËOèYßâ9sôÀ)ž©êÉñÒ¹¿„%,a ƒ€™Ìú7í¨x‰È3³ª2ØT(çØ$œ‹qùñ—N¾c=]vð¿\zqyþîõíïΪՆèÇOœ_¸?ûäSáÊ+×®»õú?yàpÜvëõ—3aôÔÓÏ•ffÀo¹öê Œ}žiò̳ñÐ¥nëðÅÏæó/Œ@´¶{{<vbó=yÞºßøòG¾~øO/½í§~îÎû¯¾jý#G6{£ƒ0`°sÏ>OÇ�vèŽ={$ºdíú[oúøC÷œiáW†- ›lýÿöŸþNUå¾£Çã¡ý[®¾î2~àëê¸Úä™×\ßc í_ºÿô%·xë[#ÓøûŸR,Œ&Ðõ[îxÇuûôÇ?ùÄóO<þL<pp÷Í·üÄ£ß<tÓ«¶2âs?vfÇ­ß{¶2¸±«”¶ìý —8àôSO½°Ìö´„%,áå„”‘PÄvbAq)@ »jv¬ªM3I;žŒ‡ƒ¡ùPU&òޱ鹃Á Ë²ràâ‹Uõ™gž袋.¼äàÁòÓp8<¯ÄÕ˯ÜÏ@xéþ»îy<(€É‘'ï¹{°~ÇëÛ.ºlïàȱ oßyáÞ‘ÉhŒÁÊúË^³w×ࣟ|d,¬ßðÖ7_=ä89³!ƒ­|ÍújüðgžRêÞOb³²mßWßÎt÷¦v2Q¬Ñê¶=l}ñèK“—^xHù¾5ÛyE˜œ93‰Ägló¿ÙzÝ[ßr횃ÊdÜÖF“Ð#8ïÚQ¤•-{ößüf>õ¡¿˜W! ã3ní‚Ë_{ãñO|ùÉþ ,é¼Îl}ödÜ·e°s} ½trç®uíÜ»Û{jû®]NiüÌÑ0X¿éí·_5`Hœ¨ßvá[/X|ônD·kïe£IÛ drF87´²~ãoÙçpúk÷üEÏG�`Ü.@ZT¿e͵ãQë†kÛ.¼ææµÑ©?rt~ášr®¹ùõ‡öûØž:ñR\Ù²ÊØÂA6ykîMV¯yÃlUKX–°„¿I ª¢Ê”eæx(Ô—ZRäg­÷¥ãèó?»ï‚E –¿qôø©x~ÛÊî¥ÏüÙݯ»üë‡~ìÿƒ÷·:t˜<ÿ¹Þpfó­ßø“ÿýå§deûš§xêž»¾<v~r×'î½õ'^³ãò÷ýý¿wçé‘WW&üÖ?ù퇛´.~÷~úº•Ç_ vìE{âÅSçôùÚróU[ýè/þ×ï ƒöó¿ó¿þÉ׎æ“_~õß¹eÇ¡¿ýßýƒwŸÓêÚã¿ý/þí9ýÈçï~ìuï;´ç†üâå'ÛfÛ–¦ÅÎm®yÁ£{þüÞwÞrÛšw:ºï®{NmrâÀíyÃ÷¿åÖ­·Ÿúê¯~ôø§?qø5ï¿õÂ[~òŸ^ýp 8ñÅ»¾¸±ûö³–9•…íºþ'~ñr¬n[FG>õg÷O– Ç–°„—´3èÕ.5$"‰ªj‘r0  i£„À`ff&±Üjy å\¢æz­k;I{Ë“ñÄÎ@”Ïo$[vle…¼ðÜÑQçÌ•ãGNt}@[·oÁÑ´ñ®'¿ùÙ}ñø¶m7¼õ-×¬í¸æUû¿q+¯=´Âñè×?þɯœdwÙÛî|ÍúžË/Þòô“§«·¾ðü®KÞ|çkönÙwÑ:y 'î?ü½oºrmýUw¼õª3/=óèc<òøó¡“ÐóÞš#¿å…§î*gàIV¯ºîª-N7ž¿ïãŸûú¨¥fÀ“¯¡/=uׇ¾p|×ÁÛï|íÞµ=ûwò‘óýsî¾ãuû¯¹ù–£Ç+O½®.é©9yöE9´{ûžÝî›~ϺéÊž ¶Èñ]ë«9úÌ!]½úÚ+†¬§Ž}þcþš+îxÛ-ë[¯ºöÀ׿ðxÊ$tòéÏ|è‹ÇÿöÎ;>ŠâmàÏ3»w齄„BBï½»¢(¢ ¨ ‚ŠòSQQT,é""Uz5¡$ÞÛ•çýc÷.íÞ Dæû9ñr;;ûÌìÞÍÌ3OÑ1‰q��É!¼{§úö¬8)vϱ+r7°ömÙNà9Ç×n8!ëìõ²>,ºg¤ŸS=û¸T³ÍÂ% ½³3#4_ÿgßÙ"DÆ€[¼F*?Ëæ=W[U àn£Ân‰é™Y³AU5åý5«àp~á¼”ôb[öåEœæ&§)(¬¹¬…Wÿü~ñßg®d›$=3eÆŸÚ8wáÖ «±"97³€9ÚAQÚ¥½K–lJ*�ʽ¸æûŸ6ŸLL/ä:G)?ùJ¦¢¯Ú,B’ò2ÓŠ%·z~~®˜{)îï…kŽWwæˆqbÍï»Îgp½“‹l(2I €²/¬þþ—m±—3‹IïdùédW©Ÿ@Å:“¬øuóÉä\“lo¯dgæó’C¶.WãÕýGÍ@Êõ{/TéìÁ3/Ä]-0^>u!—å^X·à·}‰i&³¢Ì {6ü´:¦«QFBJ¿töRrV‘bçâbÇ ’ÏYõõO{rDle@P» –ä%D  U(ƒ �47@ %ÕÍK |�j¨‚uFcI”€ÄÄÄôŒôúõ223.^²2*³¦¯¬%5Œ ŹñIi ÎÓÓQáî^¡äÞÿ¡ûx`H= 89;—õPG*ÈÈ2 ÎÁŽ�eß¼eç±3—ÒóG·ÀfÑ=úu Ñ•“¤ÂY7€Ü½¼dBã•ÄÄ"3�’ɤ” 2³ ¨³×WV!ò¼kGŽÆ .°më@Éz~5[ZRO~Òµ<F:oOw/ Œ&3º{z:xz9#˜2’ÓL�ÜÝÃS"4^»|ÝÌÁ\tåj*GÐyyº”ŸÉpk¾ tò ôÒƒ!ýøsy6Q­¾-é4pöŠêÝwè}÷ < o¤¿ €²TT˜{õz€ØdÀ í"‚œª5[³yn®*@ ü—жX´‰jïP.¥ (£)¨>‡ó Ïú¸:G;9ÔÓé� Åd:^X´5'¯¦v%(é—vü<GéJ¶”{qÕ‡«/[<-c6O»´sé¢å+CóÁµÓ®�°P²·~üþVˡĿÌÙX®xUåËÌ>ºuÑÑ­¥Î�4§Äo]¿µL¥k§²YaEXþ™½ËÎìU»!zÌ{8[Ml^®ræŸ?½÷'��èʺ°xÖë‰ÄãWÍÿp•zM�@cÒ™ ßÙPúŒê”PbVÌ‹)/ÈZJ üW@d FÝU`€!¨Ñ´ˆ¨¥:”u²bâD¤}¨ZÖ©qz¥Ê~¢¬~5²²s-ïsüýÕ2ÉÉ)j)–•U£†°¼Ü\^ßQòðö±Ã<‹ûóôõÕ#�äT¸ÂÕJ–1\ɼ~!%×2b£1­À’öØ WT…­5ŽbHO8—žpVö lÛ­m}'¯ˆ0¯KqéPõY%R¨—*û!ZîF¥?û–õ¶ˆ<%µ/Ÿ:ìïÝ5Ð×ϱ|å6[jK¤ÌädC”›³Gp¨““9ïllvÃè@ÿfõ½ÍדSlÊcˆŒÅEhïhïÓ¼MhÆ¡‹¬ � ²¾M³Ùi­ÛExØ™Ò.Å\L5û· õ±:7T)!*×bwîÊhìãÑ Ú# ÀýŸ­Ç nØ.÷ÈfU'„· @ Ü…”2(°è PFâ%…*¼»!yв&+gMVN­É*AyHKÄ£&ñ%®*´„=Z ®(r‰AX Ôp<jʆ @)]wÅuVrrª_½zj²ƒÖ­ZZ?ð÷·è @QÌ))©5j'&¦DøȮͺ´4‹K)2é<ýš´iâND…)—RŒ%¢hñvõþþ^HhÊË-@4gç(>ÞÌNW”ŸPh$D;g%¯°Ê°B.ÞŽ<#=§Hád(V=dÙ®ªÅ{Y±Í&£ ;¹¸2Èd 8ÇüœÅÇ[_¿A`ÜõKF$YGæ›Û#H?z4Þ§G„di>æUÚR›Âð¼kɹ‚\ƒ�d]¼–˜îäRŸ]»rÕ(�ËÎÌT|ëéøŸ¹–DvAõ}¡)3;¿ ã‰â´£Gó£º7u‰jŸž½ûRVY{¹*úÖ–œ¤wvÒ#²ìø³ñW %æÑ<LUTÒ¨R0{JM:™z%Ö«a·ÞÑžž~~'ÏÞà,Û÷HÑW¬êDV íb@P—)=÷)•5QÛ¨F@B­Øž. @pûàœTŸ@ 5/"2ÆÔŒ¤ejô3‘¢˜‰¸šæ€gˆŒ!(ÚFtuòԙͦ„‹‰ÂÃ*)!!Ñd®YÞ_€‚䘉n-C|ƒ; ¶Öæâ„ǯ—ÚòE—ú]‡{Hg¯—_9—dbÌt>îJH—G¯–ƒ45˜@§×™R÷®ÞŸ\Þˆ¬Œ ‘Ñí¸1$RC$à¦ä«iJÕ)KW‘œ–A¡~>íîM²rþЖ³)yçÏ] ñjàèÓöÞAÑ3ØÙgìýó@ ûCÔs! ccÍ€X~å-µ) ̺tµ ØÍ… ïÚõCÆÕlÅÏMSfR²ê@‚EçcC}:ùt2¨gz™R{ÅȪè4_?{è‚w¯ï–­¦ì8W\ZRUߢ 9¯gg‘££O›n½ÃòLvβu«ÆV£J]HqoÑ¥»Ÿ¾¸ È$Ù»pSa¾‘nt³u6ÄW¬êæn›@ ê(Hĵlˆ jÀb®OˆªG fωPùÓ¿š®>yê”ÏJyü—‘N|÷Á;oOýzWRµROÁUÀ2 ÈqâŠb&Eáfˆ‘$©ü"“˜¤¦?Ðì‘1k2E­æÊ/š™™yþB‚¢ØP(ŠùüùøÌz¨`Q|̶‡Î^OË3˜8WL…ùé‰ñÿÞ“Y:¬ ©¸ÀÀô:fÌ͸°oÿ‰�@QƱí»O^KË3‘¬·“ÌÅ9™JU:�`¦´kIé9…&…pSq^ZrÜž]Çk²}LWŽKÎ7‚NoÏLFubQ˜vdǾ3×3 L Ûé 8ÏXÁç­Ú 9)îdJAÉ­©¼¥¶…¤ÔËW 9pSòµ BSʵ4ÑpåÒuk†ìãÛ÷œºžY@LF%?õÚñm»c n¨éÁÜSGcó]·w,cNPUßÚ’“¥ÛüZzèÜ|}¼uæüœ´´\VÖ(++ÊÏ3£½››«+N¿~j÷Ñ+tólÞ#=ÚªJ w¯L°¦‹.9ÝLKkP’è@ÃdÖ»yøÜ‰@ (MNVš’$Kc’„¨-… ÅňœHጡ,Ë’$##^l08º;ƒ,L& MFƒQf’$K(뀢|îjæ¬6Ô²:Y®W¯ž‡»›ƒ=�ŠŠ³²³SSÓjnGPm¤°èÁmBu9I;ÿ8’qÓën@ äd¥æ(ŠÂ˜¤FyÒÈX‰Ó¡Àšö æa˜@ ¸•0”‘!0$ƈHá:Æ$‰'…CI§×0ÎÍŠIÑéd ˜¤×3n2�W8”™�ÌZPžj9Ù™Ìæ¤«W“®^(ml'J@ uKz²AVí 8‘\RJõFP=P J @pk±&8Ðb¶"*Š8CREB™s°8˜³R\ \QO’�˜Ä4û:D5âíkŒ@ ÁíÆ²ûA�ÌšPÖæ[V¥�Ué£)Ám‚,ÿr"$D@¤pŽÈÕX…Ä9GÆ€‚œ8q…!c“˜Ì™–ðwº6\I8±>á@Åì¶@ ÿ¬a›Ô­"B‹S �@€¨nªX’K�À±ý[o—Ä@ ”£E箜Åe.IS3!*–¨¼ã8�`ÈP–™$3@PÎ9p�ˆ(ÉR™„ˆ@ wjŠh럥ߗ$DTœ! ¿Ã·Y@p—ADH@ˆ@œHÊ"CÈA͌ȀH1¡¦ÿF"®p®0@dÀd5÷P@ ¸{A@›‰¢å’JE.îš@ ¸ÓP]æ¬ÿ )œr5U� $IÄ$Æ8SÍ® €Ș„�Œ1Æ$B.B @ ¸kQ5å*21�B²øh’åÅÅäI wªsê8P*¶ªi}Õ4§�@†’„ˆD€ˆÀILQ‹WÂíkˆ@ ÁmGÍs`+u“Œ€„D¼djw~`'@ Ü}E�¤*· ˆ$Æ2�nÖbªvÜlR8ã2èPBÆ‘ˆ"Î ˆ8'n6q€½Û6`)@³VÐÆJÔL¬VúÆŠM}¼@ ÁHËÎíÀž°ÜúŸ€Ä$Æ1Ƙ剙Ž@ î8%ÆC�5*€$Iœ«nu��z½^’$^"âœ8ç(É �8‘¢ðª®!Üî@ Üh™�Ó4ÖCŒ€8çˆ%‘0ÅÁ‰ÅØMÓz«á8iÉ �TƒK$kìÕ^€Tí‚Є @ X Ö9’Œ€D5iž %›2@ ÜAhË|ëeõ’CPC•¸ÎU›ÇêYbø@pP’¾@ÝeÁ’8ÑLÝoáœS©LÓbŽ$‚;UA€Ö•¿:ªi«{´|@j¨CkPC� ´(˜Z+3Ô‰ <@ îBˆˆs®€²šdʪ‹ÂK9jª³+@ î$°l ^ÍN $  CDu„c¨eð!K6²8)ج¹´•@ ÁÝ�¢æX@@ªMA‰ÓzX EDˆÄ9 Šø@ ¸³@í?uÅh µcùŒ#0Uu`±Pÿ‡ˆ ÊjJÅô:@ w!œ8p-44–UH“$‰8W8'Õ¬@è@p‡€ªs�RIˆBƘª2@TCð�0ÆÔÁNˆ€P.¦†Ð@ ¸K) ð„�€ŒYO3Æ1I¯×˲Ì(#Z ‚; ‚R¹}Õ•?C@h9ÊKÁ�™ª8°&>PÇ:áa'àî¦TÐg‹7' �È(IL’Œf� $'Îâ„7;wJMK»ž””qõ\FR|a^v­HïèâîØÐ«~„` ¯O­Ô)‚: 0D`€j¼&I@(Içœ1’õ:Î#WÌÄ™,q��d ³™™9qU×Àop9@ ‚ÿ2’$3N¤pH 5-¨! %†€Äµc„t¶F£éÀîm™1ë»›Z¸9øD8;Ú;׊ø…Å…iYûÒc¶íÛ®ól9¤C×Þz½®VjAÝKh o«£ºüW8!爀ãŠBœ+\aŒ1I’¬¦rŒ!c�â òó33²JûãYêe®]ö¨mùî<C</o/7×°°ÐÛ-ˆ@ ‚; $"ëìJ5Ã$ D‰s��Õ¼RœC@d53'HMMݹj~8&ôkí+‘¸ Ì9”W;{5ȸëxºµÕí;»rí¹£=†?ëëë[+•  ®`ɇÖ D–ˆj ` F…ÉŒ€8'Y’T·kþ_�HHLdLîÞ³¯ÕCÁZTPTñ¦D¼;OM��GˆO¸ØPh @ ”F!$DÈ-³ÍŠ‚\!-}”‰ƒšyl¦í+èç›äå ÅÙPIÆ©ÿGà �H€]#<ƒ3’ÿ^ùðÑoÜ›Ê>ôÝø×¿ÞvݵÝSÓ¿y§?«í+€)åಯçý¾ùP알"nïØ(ºËïÎÕÚùNœ^Þ4Æ”Óûœ8{öìùx}Ÿé“øü§Z'nT’ë��ÊÆ#D5ñc $‰ ™Ì@¤“uJfEI΀8©�B^^~^}j̺ÃhÓ®ýö­›¯$]-,,ÈÏÏ¿ÝâÔ�gggg'çúõê¢ð·Ñ'•QG{¦®‹}»ÿ/ˆÔàNÖ-’d 8ç¤!B$¬aT§ý»¶F`B› çÖžØ6!(Î r³‹HKØ¿kk÷>k¹zåüüÉ®5dlÿzâg=ÎêV»šCì‚çxgËu³¥‡M©ñG·¶xþÓÿØ*ZIZöúˆ©GL� ï1… B|q@ ¨È�Ê b–H;D%GTó9Î9'®·Ó£$LF™s^Nž™‘ù_Whd¤g¸»¹…5l„w¨Åƒ Ô ÎI—/=w^¯×Õ-áo¢O*£ŽöL];)))00ðv‹#þÿX~y¬ö’€²ú^Ý{QçYêdKËŒX RÓÒ2ެíÛÂL·Bj˜Š;„¹/?²65ºM5#Rú/µ˜¸ÕXy }·Ï,Æ;?/ß2‹¤Â¼‚Zp¥œýá•÷JéTб]§úÚ½RELßî0|î�@×ê½ÖO—nx’ùÄÌnfÇ™�‡Ì=³à>—[-§zÝÓ¿¼þɺØËW“S3³só‹ŒuöNî¾! £ÚuòðÛ¸Õ¾‡@ ¨P‰[�0dˆ@9) *ªMç\§Óç\5= ÆPbc5Õ‡×yƒ‚ÀâhX7`Œ6>w&¬axþ!ú¤2êhÏÔi±âÏßnAÁÿ 5Øòš”QKD ˆˆ"Põ÷~/'&6v-”͵+ó Í…‘nE—k9ñÜü‰ûþ>ù¯$“äÝþ…ÑÝìj³r0ǬXSL��Ì£ËøÙ³Fw Òç_>}ÎÔäÖ¯¾u‘­›Ûá•B�sü™sFw°£ü«g³=‚#ë—x?ž‹»¤ Xrã–QŽ·\J åÒÞUþ“_zo(ÈN¹˜rñÔ-Ëç}ÙaÜ7?½ÝÃW¨ ‚» B`êV2š)@R©ß kŒDTÕÝD@œ«. ˆ€È´"jàûĜ��ëÊ Ä çœ1É`0ÔEáo¢O*£ŽöL»®¸H‚J±†aBTjv—²Z€H ]bfÀªmó”šx¦½“ô¯g–"Gv1ñ ´kW«Õê=µàÐÀø Ž!Î7Þn¯ ”uòäU"5xäÝIý#u�àÒ¤‹­^¦2œ¢[…ËO˜�€Šâb/)ƒ#µöñ+‹ŸïÿÎA�èÚ¾»à aÚó…Øó&u ͼ¢[6¨Ýþ¸iÈœ±Θü½â© ¡(î6¨´ƒ�)êzŸcLb(ëtHÀ…sÎ9WˆEGPæÄÛ ýíãηd¶‰*vþ!ú¤2êhÏÔi±ÁuŠTzj$3Æ® 0 œˆ‘W{ú”y5Á;D_kj&¡[ e]ºaAo}fbÂM\Á®×û[fô*o'€Ž¾Z=´÷ ºYx^nžÖ­’_€ÿ¿½æ–‚[4÷`'R9�˜N-„HÕ†²÷ýsR³1ŸÜ±3ml˜Ÿ¨"/.6IÕl£®iëfò¿,1��Úš½ïÛ{‘›òÓoøæÝO7_6ðœý~î‰ ‘wˆêB ü[ ¢Åñ€ia �$…+DD@D$KçdooGD’$I²®¸¸H6#"*Š‚ˆœW‡­»KW @ ADÈ€qÒfWÚ!FDX&l4ZFW³ö¢ÜLG‰€jçEfsq±Aj?í\«.é(QQnæÍô‡s½ðFáʽÂÜ$�0šÚÙÛÇßÃÇß#ðÙU…ê JNüž•ó>|eä=Û· öñ ŽêÜcØØ¹ÇJ¢&nûæÍ‘}ÚFÖ®Ѻóý/NûõhjY_ «‚ƸÿÝèzþê…ü{L?^R®øÊÎïß= c‹à `ÿð–íï}~Ê‚=IËaã™ÙƒPj¬7�� �IDATzúø{øø{5¹ôªE;CÙ›'´óÖ>â§K6ìÖôÍ[7ש÷š q§ÎY®Y¸×Aƒ% ƒñè¶=¹Úæ §Ïjq¤†­Zº•è«ÓX+JÒ¦©#ïiÓ¬±ý†a­ú>øê×›.Ù.j {WOw7W7¯úÚÝ7aö[½ìT9̉笭4$Û´äÓÉãÜ£y㈀úAõ‚›5ï6ä‘w7–ôPÞ…sÞx´_Ljпà&ͺ?ôüŒå‡Ó¬B¶¾ÖÒK½#ƒg_°TmÜ1¾i}ïÓ›´ÏLûþí§Þ¾æ&ªW üsë>õÞíÃC‚}üC‚";tòôË3V.ÛÖjt]µž7஥œ€–ã�¸êG@ZÊCõ “$½N'ëdDäœWð¾£:ø2'¬|ã‰W–$ðêŸ"@Pähk’@D2ZMˆ8¡ZyµCÔž3PvRò‘~eÞö1¥œ©üÂÊ¿d2ª\Z4î15h¿•Ü”‹'2¤Þ *SL—ÖN~lüÒØ«àzìžÕ±{×/[ýö/ ÆV;Õ¡)qíäÇK×SœrþÀúóþX¼däÜÅïß(ƒ¾é‹Ÿ½¶iÐŒÄ3·L²º×üüåìüø­åW�`Þ§}òd°MvôhÓ1BÞzÒ�Ê•Óg²©7ìÜ›k½ÛT¸oÛþ¢8PÖÙØdõÎ2϶íiÆ5n¬reÏïW, J:½uÉémk׿´pé´^7a°f=œ¬Á ;?þÔoÙ¥cfR\VFH¡�Æ.÷ä;k/-E ×bwÿ»ç÷Å¿¾>þä®^vzwv]²:›�ÌçŽ,€pW�0Ÿ?|$‹€’tìh oÈ�ø•£1)jÇè"úô b�”½ûÝûG~sºÐ*B~ÆåÓ—c/ú Ÿ4¼Ô¤ënü¼ w5„ˆ–܈ éÔ¼¿œ) g�Œ1I’t²lÖë I&5„¡þ°ç_á×öþ¾jó[bù/¨-”„_ýzµã+£;¹”ûbðk;Xt*lÔ ýüÄ$ÿ)1@B´†+°xw2�Õt�QB@àd™@Õ€Ê70Ð'’ù·(û!b½¦�XÙ)ö2sÊ¿”´k©Üï]tkpÇcT«� Íñ’eí‡(éu’–]Òœ¼ãÃg§ïÊ«x.2É “�äïÿèñK­!Kày'=7ò‹£Å��ú&ÏÏžØÎ €§mœ>e] Ïß÷ñ䥉f`õî}ÖÃõmäRXûv^ê!2ÅÆÄš�Ìg¶ïJS�1†�À³wï8l�0ÅR‹�Ú·é åb¸‰ÆV„òN|ó┵é5¹…Ü”—·õû7>Ý¡Z>H·»A~9¼e” elšôÄäR:‚AxæÏž~áÇx3�8uîÝÉQmKqÌ‘8��¥:¤˜Nï?ªš—ÄV‚Ô£w¸ ÄΟöÝ™B­Sô.Þ¾>Ž:D´kÑ®…%¥æÿ³ë¬Ï›@p7£ªÊYpâDĹEcÀ¹¢˜-!x�,f $;pÍÒŽQ~Üš£úµ övµwt¯×¨Ãàç?Û–ü/å©[]=¶}Ûé¬Û=V ª‡»ú«ù›/Û8rqóÜïןÊw²¦ÔS;·M¿ ®Rn.„¥ö`"iæš„ Qb:‰éeI¯«X‘m*ó @g_¹ßÿŠuî¬ñ�©Ó©ý3Rûgtor Î+=ËÑËQÇðôÚ‚”ià4tô¬ÜõàfºÃ°~Œ¿Åà_}Õk5q·éÆgèš[søDüÙã1ÿ¬ùuî ]øÅÅÓ<]L� ùõ}ÝÑ«W/_;µö£þùÒòïצ•T×qZ̵¤ôä¤ôä¤ô«ÛÞ‰–AIXøþ‚8#�Ê~}Þúi×ÑcǷέ‹º°§¢“߸â:�ÐEŽùtrG�ž²öo¿7ù­ñ&îÿäãûý+U÷ëZthí Þ}žqâø@‰ß¹=Ñ €Î½âË�@IÙù÷q�O:y:SûäÈmÔËÝLcåá_¬ßuæÜÙsû~}¯¯Ÿ¤Ú§müfÙÅ[¡Pñêg‚=|ü=ê5hм׃SÖ' Pòlÿʼ)=œ+–g÷~¶ãtÜ™óÇwoýmîŒÂ%0ùvÆŠ$3�êÃî{õ®#g®Ÿ÷l �Àsö|:ëïtïÚ·^ÝÐO>z4‰@ÁÁ½1šv ï?e�Ó™CÇ‹TUE½½šë� è̉óª{†Tÿ‘_NÆ?}"áRüÅC–Oîí®uøM='Ÿ7@PDdÈ�T3TÃjq�…8'³¢˜ŒFƒÑ`6›­i±Äý®û”±ý¾ûòˆÇ=“æ,þmé·3^”›epÀÛêzP=ák V/X5ªaàðWK­z”¸/û7{ùïš{<™bç¿ôìÔ?’þ­ÐíEOêÜÀ¯~P@pDd»~Žÿ|m\^5[þ§hß;‚ºL;`¸qÙ»ž¼âùè°`¿úA~AaaÍ;÷â9[.ÙБT‰ruÕ#'-=ûo=ãüêÃCŒZS:ý¸ñ‚ƒ‡~›XgR$‚; ´î”µ@Õš€Ôm`“t:;;;;{ûjÖN–šŠ`÷ èàæÜo’Üíe©Ù)ú)ú3³Ë<¼º²SˆÈh2KíFú¸Øeì_Áœ}XŸÉ•­ý¾ºèì×0ÈÇÓÓ7¸I‡ý£½”ø ¿.$�@»Žã?×ÑÏ@ïÛîùcÛê��¨ððî#7™•øõ«bÔ}ræ3ìÃ^ëä× zð”ïß  ‚Ýk7k:k]ĘÏßèæÆ�@IÞøõ²óF”‚ûdúŸªöÇ\ÚtˆÒô?æ³1§‹€'íØk@ûC&<ÔÓ€reË–³f(>}✶gîߦC à&Ëü[õmîïáêÞsüW“û©¦õd:¹soÆMÞB½›‹)%­ÐÖ!wƒ°�/­{ÞÓ-DÓ±Uk.jkøÐ‘_Ïy®gd€hÛGf|ûF[5ÌOÿ{íŽ|�æÛ»_´½Á{èX>€ñÄ®ƒVë%ùà‹ (IG«¾̳Wÿ6v��:gíû³OoÝ›e�[pënQÞªÒææº®âó&ÜÕ”ý h¦oŒÄ$IBd’,IC‰Y@dÈT]¶Å@ûSSTc‰·mÚ˜//4}ûÝË?xéÑû†Ü÷ØèW¦~÷ëôA®ZÂ3Ë߸¿c¸¯»“oã^cæË! €Â= íRÏÅÁÁÎÙ?²ïË?*°ÔiLüãýGº4©çêâÖeĬÝi �™3¢o‹zNöŽN¾Q¯þ]Æ}ï÷ˆôssÒ;x5hû茭)ü&TwÅZ™çe¤=ýÃê5k—/˜3ùàøE/Ü÷ô¼³ÿnºæ;~}ÍÜßr¼¼’W|»®‚6únÅ”›‘©´}í×ÕV-]ðéønG¿uÿ˜W…a€@ ¸Û°®ªÑ’Q…Ø81R'R²\mk°½ÕÏB»ÈÑKËŽnþ}Ç¢WCæ!w‡v.å£dädgÉF9\?l.È–ê5a-l[ÜsCìÉ8uB†=“ÛyY,¼Ú¼w@Ý}¡Â«W³n8ðb-1ѡӀî–Ù0úôØZ½dŠ=g™ðÈáÏÌžÒÝÃj8€r蓟LïïYõB’ÕïØ%TÖÄŠ9rÚ˜ºíï&ÔµÐ3¨k¿.Î�æø­[ã 玞P­è™{§®ªåüÿ¿±èÕ¾K¤&€9ñBâçoÌÉ784$$4¤A «^uÏ ¢‹[¿~nȳ n¬>çi§OZ",øôè߯Ï@ î߯±&JAìÉ‹f�Ô§_”¬¶ãØÞã&óù={S@Ôéu`ŽÛ¿?“çÜƬöK·AÕêô‡òSW'çí߬ÝÐ13WN)1Q©­çD ¨Øp°ä>P‘$†ˆê¨‚Œé%Y–t²,—Ó0Wc‰»yáŠ+>Lyµ­³­ÊõßFz~nø'¿oÞøÍãº?^¾â¦l�2&îßzÔqØœ•l\·hrûÔÅÏ?2c¿({Û¤ÌÏêýÿZôbàþw|î—k �ⶬÞ+ßûùª¿6oX8ó±æv$÷?óç?¶íüó»çêíŸöää ¹·Ê– :7}ôìÀ–aÁM» ðý¾4íG‹§íýrÌàVCâZ÷Ÿ´FsÇPÎÎT? °^@ÈÀoâ(>·rÊ]£ƒƒC¶è>ü«cղ⫮!­Û¶i×±k¿ácgý4¥·rä—ßO›_Û4cÄ=Ý¢"ÂêG¶zê§€²Ž,|cX—æÁ!Ñ}žyÝ…’h³gŸ6²W«ÆA¡Mš÷xvÁY�(÷ÄÏo=Ñ«m“à†-»>>mm¼ªÕ¥Ü˜E¯ íÜ8$¤AdÛW�*ifÑùµSGn×´Qp³.Ã&-=©:Üú>1YüãÞÀQs?æ¸ý‡_âJ »<mÿ7/mÙ0°Q›>£œ*Q)çÄ’‰v‹ ‰îýèìý« [=©¤í7aXÖ C#[ôýÉÖkf­òŠ„ÍîªQßÖ*Ì3¢mÛ¶í:öøÈks~|­EÖÖ_6^á•?”½ûËç‡ôhÓ(4$0¼Õà/Ž›€Ò~~4´^@`½ .ï6ƒéʦGöhѤÓi;òk[è*¨¤Çlß Š·òÖ?ŠàFµÒ·j �@ÖæKŒ��@ Ü^“Šmƶ#m–E&©f�Pp~dˬÌlOÇ ëÈï.ÏÌwÙ—qü¯z@Ò±ÈS%6"¢ìV¿új‘ÒPAnžr£®R”®†© /_«<ÝKùÜ£«§»Œ`$�*È/5àHÁ}·rعMÝífÞízF»Ýp·YŽìÑÙgöÙë@¹vhß¡@Ý»—ŸäÚµogÇ › Ètæ¯û]6� }ûníìk«±èäl‰qHEE…7|ÊPßæ? îÕ:ijòƒñ¯/;g à™»¿øáÀu.ŸÛ²,”Ÿ›¯]…yx¸—rÈ`î5 äå�H¡}ú5úäè3ðôƒÎ]qÝsA›>ù”ÇÒù»‹M1»fì;VL�€Î]úwuÒ¤tï7mñÿ2F}´åŠ€xQÒ¡³ý6ï«§Íû⩦Nµöœw;–¯0–|Ÿ´Àœ4õ1¨Ñ 9ç&“Y1õŒTí8(–ܽXÆ4¡Ê/§r9ölÕ¡•£ÍbJÜÂÏÖHO­_4©·# ¶ JÚÞìû¾x/�€Ðv`ÿ^.�=»ØÇüõЦMqÓ:4O[ñéOé÷|¿gÚCžЮqc–ÿ•1âi��)¨ÓþÝ],õûw6��Ú·u;ù[—•{Ïš†´+­%¨5ŠÂ`6 KG+¦’Ÿ.ÊØ<éá±Õ1ùëw‚‹Oþ:ëÓÇžÈ]½nb+û­ŽûôT—éß}ØÖÓ˜|¡ D ƒÃBŸüfþ3$@ß0ÿÕ7VëžýxùÐP}Î¥«ö·4¡-Ú;;ÉP\\LÀSnÞq-|ÒWwp7g*A’)öÛQ|šÚmÂôEmœ¯mûáË_Q6~7Ìñ¤__|dÒñF£^ÿrZ¨}NRaƒú(=÷øéýßþèçV‰«g¾÷Ò³ú Í“[³˜Ùã¦ý>qÎï=ü #!ÝÓ’°¨b3yÊú ¼ÓúÕ© ;{¦nûâÝ·Ÿâ~;?ëíd«píR°û§e׺¾ùdûÞ™OD ^¶xÿ˜º:��˜Ï}ûìÈ“;¼<ý‡.õ vü4{§v ¿ºâ¥Ç§‹xæy}B iïÒ9A#UìIãñÏGŽ˜¯{ü³§4¢SK¦O;ÎuÓʱ 6 ó±ŠÝU£¾½…0'¹ÅF2ÅΫäÙ ÜSÛþ>ãõüìzû@N±w˜û�=‡~ºôõ6:@½g°”¶úÝ—æ§ ÿhÑ—M ¯^ä n|éÚÂl³ÇŒÇ¿´yƒ¨Ü­ô¿´èÁñë)ôÖ ¢šÕ@FFÄ9qÎIõ@PÈŒ„ÌæÊ¿²Ê+NTÐ+Lö¬ú¼¬¸½©kÞq³‘…ÁÇ¥ÇüåÛc‚iõtèüˆ.¨¥Ñ§1¥žµqé›À¹^x£ðêúTÜ�tpt@Õ;õÍ™øPdÅ sRp7Ÿnèäâ"i^®ùéÅ�µe§eiY ÑÙ¹Äß”°àµw”²ˆ_ùÖ›}Z~÷P%á -è[wïèºHçzÍ Sœ�t‘}z…H€Ýû·µûkg1™O­œ©\2� .ºGgUÿPUR¯[‚~1gçŽúv¾-ŸöâºU¯n6€’zâÔuÞ9¤ÊJÐÙÕµ™gdd”Ú®ç™Ö?ÑÙUU^Èû‡~væ¼Ìçvÿù»ÃIÔã‘gë^¼ç€1oÏÖ%žû29� S×AÝ\Kò.¸¶·øÀÐÝËýüóò¿Ž¥€òϯ|ói—àíŸõpª¥çD ¸Û!MS@Ö¥/2FŠ¢�"1dŒ13€$I:Y'£Tlæ HL"†²¤ã@jDƒj\’8'`e²ók?=Üí·Y1 †ã©ã±Æ¤“C|YŽ™ÌîjS™jìÃ"‚qKjÓ™£§Š²Óž te=¢’’9øT¼xα…ïMýnSLBZ±›.W±/*¾µft¦ïwjø~™Ð­)�€¿lΪܞŸ¬ŸþX=èÖ%¼xÀ ¾ù빆šÓÒr™_Ë^]Z†è ¹jGh�Ô{7ŽÔì¶L ©éŠk×Î=:4wEˆj}KÄçfÅl2ä§%ÿóÛO7ú?ÚOó´Cׯ=úvi©ŠRð÷ܹÇüžý}Þø6v�Ð¥µóµžãæ,9=dbäñŸ¾Ú!=øã‚iýJþ›,úæ€ÿØŸ<ÛThíraû}ëמ˜Ô::=5ƒ<‡tëÖ&Ê@kwfÅf*ç–ÍÝ(=¼tθn�Тþõ=]f¯Û?£w/…kJÛðÓF>ð«{ë¡ìóÈSçÍX¼yR—û<LžÜýñ%óÞìæ�];ò= f�(g—/ØÎ|ýã»÷{"�tt:ùëŽ#Ö*Ëôdþ¦yóϵœ¸óýGë3€6ÍèøöQk6%>÷¢»GÂh«»× ok½w³Ùl2æ&_8´vöW ÑK=BŠw¼^ɳќ�Ú…uØ­ƒúP)‰� ¹5ŽŒÔ>¸–VlؾgÇ–¾ ¢ZÜ©+ƒÛê±üm¶oи`({+M‡ÿ…¯§@ ¸ãàœ3ÆT+Îвƒ(# Cˆ¤p …8™Mœ8ÕÄü¹‚N‚;UQܘ—‘¸îsó…B½Ê/õ™„Ž^Ÿ —K½G{ÔÓt J@k–W‘þ-ìE†JÆšHIWÂ{q÷M9Û5‰j,¯?f â}nÏ6TuDçÉ[6j¹ñP׸YcKVÂ3ó&LÝ‘ÍÐÁ?À!åj&W’7¾1aq›_Ÿ«2¾SÇnmí×l)" ÃñC'�äÐþýI�À|ûl¥ÿgŸLgŸPËKá]ºZ‚"þÿk8¹n½f‡ö‘MÃk®¬¦ÜìëóVi>óiå'íº¬�ðŒ›öè¦T.nÚlñóplÜLóÅ£îöõ—çÌd<ôÃ7RóíÞ£E¨oŸÆò“¦äuó–å)�€Î]ïëãQ®õA]Ÿþ_×§ßL9øËÔ§¬7™¯þ±áèG=ºÕÖs"ÜݨÚMW@e@é,æÖ †ª9ç\ýý †eO© V?<ÌÞ|üøt·/™ ³RÒ¨HKÁ(G½²úç‘ ˜Åü|ò-õ«—Ðéd Ŭš?HA#®›mýµF{ïPVÖ—�� ﯉Ã^ü+jò¬Ÿ‡6õ2ŸúæéQ›mD0¬UtQÏÿøÑ½V…¿¼|„ ��`8G‘ov±DÀÑ5êÒ¹Þœ¿cΛïk3h̨Ÿžû ß|ü‰§íéfãÇ]×âñ—ûlœ<ªwÜ=>ùäãÃ:Tm v˜öOm<��e—àŽ#>ŸöN7'€ò†ZæÄã'rÝÛwÖ@ÎÝ¢ð¯ã's¯1WXˉK'äi§O'›Î}98â+Ë'f“®iŠú.ϾÜþÉOì~ô¾O<ùøàhÍfbO3]?óx“eZ¤˜P=‡nuŸðKk~Ùí9lY7g�`~÷=Ùwæ+¿®»6äéú›ÆšwlU14nñÙ3ñÐøá6î7¥Ì‰§ÎäÎOí<Mk7sßk©Ãl<6º«f}[Ë6½ò"��êÜõûíŒñÑx~V%Ï5w¿aRذZñê„>ƒÿ|øñ'Ÿ| GˆÓ¿7ÔÛê±Jo—;û_øz ‚;P2¯ 5˜!Yì7­á HáÜl6›Œ5qJª5€û6©¢¸lçècÏz;° ç’ÙÌzOd ÚÛe]P …rd?¥¸��¸‹¿­Ø·9rààpY ý÷W†Žýjõ¾¸Äk×Ïž<¸sãâÙ3¯–%¹Ôðžû[hõ2Öÿoô'Æ\ºzñغ÷ŸŸ±Mu D‡Ž÷öUî '¿yeÖá@—.ï,ßòÕ# $àÙ;?|åÇóUß9ôìÖ·¥®ô¸%‡ ¼§©:Yeõ n[æ Ô WOk¾›k,g%§fee^;½åÛ瞟§ÚF0·>÷õ¼±—€± ¯È`0d];pÃw¯Œúür[ªuc+D]«áCCÔ(—~eܼ­±W’.üåÍ>;ª&1`ž½ïµfM› ¾·¡ �dÌÍ."`îÝ{·±“Âûô–xNv�tî6¤o‰–€_Þ4÷Ëe;N&å�}½Ö÷ôk®ùVP^n>ÕÞs"Üå TˆJPîo$�"† ¬:‹£!"Ç’Onì×nýä™´|æÂs†òÎþ`ß,:b.:‡G6ŽÐ^õÝ¥Š,ïåÈVÍäkÇÏAX„õ”ÆÁžúŠ1”ËGcÒ=¾2ù¡n­š5mÝ6ÜY¢EçQÛÑ œ›·jݦöjÝ$ ”Ù4Uìmu|òèöÎ{7Î~4 öÇq}º=>÷´¡âm]øˆùÛö.ØÅ°é½‡zôyã¯ÔÚÇ¢k>vÉÆ[¶n?s:nï²6¶ÆF¢$‹7ç_Èí&­Ú¼m‹úÚòÏ?½ßSàØjܲ½Û¾¡Iʲ‰C:ýxo.Ùj&qräs‹7[jغu×ÖÏï÷Æ[Ü'æS+–)¼8whX½€ÀzA£×¤í_ºò‚¢5˜¸-ŸQÄJŽT„ˆÐeÀXzæï;¶­žÐJgó‘°Õ]5ëÛÚEßùíÕÿþ{Ëî1çOn[2y`°¾ªg£"Xá ¸ç‹¿w­{ Ãþ/Fôì=zù¥ÚŒ¨€z{=RQa)‡M*Ì/ä ×ëÐvUzƒ*p뿞ऊ”�L]p«I§ TލÔo#~¡³·í¢fSâº/Ž~p)a¿ílˆ@gÊÞqö ®H¾Ì…Ù� yÞ™! A×|ìÛÃÕ Ï]õѨ¡½ZµhÓªkÿ>;~Æw+U/£®öÌ;O…ë�HIÝ1kT¯¶m[÷óå~5)!Ú5yöíG�c¿ôÕÑB@‡6/2ª‘_ÿwf<PO�ÊÛ;óÎW¹àdþ–8}*rèÀÖ¿YýCÚ”ÒH~}úµ(Nnª±¦cŸ÷kÞ4¬q›®M_ŸhRç”.í^yóÞ\� â?^Š  ñ kÑ}À3S9‘«^]ÚÕNƒó@׿ÅÉÃ$�2%n˜ö`÷öÍ;Ý÷ÒO§ò-¢L˜8°dÉ/7zOÃ’->×n»8ÈÍ )ùÔ¥ç}½JÙäøuÖ´—ëÞ*Ò¿~hƒðFÑ/­Ñî›Ü¤i#ùf»N ”Æ¢ÎVÿ@D†ªO³hÊŸbÍó£nˆjr¨A¶ô¾ÿƒÏrÙ9±oŸçgþ´vëŽ[ÿØhýÆJÍžž0ÄyÇÛŒýlùæÿlß°äëÅû«\а€‡_Ñàô§˜ñóÆ;wlZ>ïÇ­×lý�HÍšºeþõͬջ?sâ’%’óòñ¤‹ÛVl>Ÿóï ƒvM£# nÏÞtí’¦ {ö§Ø7‹×l±<›~áãU[xÔþÀ÷KšÀÞÁòrʦ$DÇ N¾ýæMS¢/¯Xøg­ÇÝw hݼI£>NUØÕÉ!Í£\²î9¥éœ){ßî“ÕÌUòmÒÔ—ÇìÚŸWª8ómÒÔ—.Ä^ó o¤¾†ùiûÄ̵aÏQÓoûíÅÀ3‹í*¶ÕL»Èf áÒéËÎaJjÐL.n]Ÿ-ÿýbÄó ÿþû¯­ÚkãWÃ}Îüö[Œ‰ùDEùó£ÛvgW¸ž]“æpfÇÎäJrHÓHÇÂ3çr­í o⣹uVx$*v—±Æ}[‹ [póèèf‘‚¼,æ/•?¶&,ööP”›[vwF_¯õ° _¯ß<»OÁ_?®®ÍÈ‹èÑØŸØ±Ûš&Š2wnáÞ#5ŸÅò=Võ ªPÿ-þz ‚:…6Œ‘¦ @@Bª²€l8è\}m–½´a¶|f}S/Ä* @×N¢Îžu‡’hÏî\�� �IDAT,9¸0“�tžæŠåïˆ_0ôôѯŒ˜²1ÑPQ %þ\‚êWc5‹.]§,ýøë+Ï•«š<>gñ¤öŽ� \\üîG‹�å†ÏNÝD�^ƒßz½ç¦7¶æåüäe÷.Ñ Òmv)´oßÈŽžTÇ5¹Á ÁQ%Š�Vн­ßÛ¿_ËÍèÝs@›ÒJçÚh,Jmžÿaþ˜&7o@ˆ:ßN¯|3wl£*ý+¬¥}¾äý‚§¦þ™d*ïãâÞæå￱qiQä¨aÚ͙yÜDjœÂ.��ºèƒÌ›“ ��óèûp)[0Å=©u)Åy9%WvŽz %€Ú{N‚»,»Ù«Æ& "@Vb2 n ª£›–;‘!0FŠ�„¶v +C |øÇ º~6káâ·V~˜m’]|ê7ì6´W„�Xàc 7KÓ§|ñõË+®0÷ÀæÃ?¸DG›« MdŸo^éÿöŒŸ_ô£rˆì3¹Ç3}*–t2sÉÔWßþþ¹ASóÉÎų^Dû† 5~jʸͯ~7ñ‡Áý>éRëÆØ¶‘Â}ùþŸ^˜úÜ4eüÀ`ÃÉ_>™w¾Ñ¨º#ǬZïÙ2Ü[_˜x !=<ÝQŠnæ2{Ã׳;ñÎŽéW¤Ö4Œ_ºÓШi 3Ï8v2;Dy8Ü&ç+çÞ/މúÅ+ãÜßzªµÓµ­s?øSê÷Ù“Íe`íŸ~¡ÃïS'>ãüÚè~ÜÌéW êÞíG¾Ð~åÔ·ŸõLÝ?Ò³cMÑÏmªSÎÿ±è˜>*ÒÏÁx}O\¸z¹Ë<iûÏå›)7~lì€Å>xzbÁ¸¡-}åü«g³B~¸ Ù(\k}R°kåºäˆQöŽŽ,ñiôH¿×­Y¶ïµ6]Fï¾úÍI#]’ÇÞÓIJŽ]åj< ©Ñc¯ ûyì´'_Í?¼•¯œ0±ü¬ÄŠKŸ±O7öíøgì'<Ý#ÔÕ”Ÿ¨ëòTÿ0ÉÖ#a«»t5éÛÚꙪ¨üÙ€ jæÕ4* ø—ï>Yé:À;'Ññ`»Ìßפ¶ug¹g'd*~jŽÜö©ç:.Ÿúæc.ÔÄ-ÿÜ_ æüal7edG=€Í«ô•¯ÚÖs+|#‚»뢱$ð�ÈêæŒ¶×¨¹&Ôpùmc½_‰–¡~§a<e/ò,dÔšù6RNÿI…™jG�µÐ/Lg�(éî '›86{ú‡Ýݶþôã¯ëþ9w)=O‘]=ý„7mÙ®×ðFÕvÀׇ?ôÕŽ¶CÌûeíö#g¯eíÜ#Úô¾ä‹Oõ q�� ÌM3>ß›G�À|†¼1¾½f Ê‚~{Ôü¾Œ3ÏÙùÙ‡›î;¸R·B©QŸ~aŸŸ<k�)¨ÿ½Ñ¥ç˜,pààÖÓì3�óèÙ¯cy¥sõ+ù ?%gÛ±Sqñ‰I×’3r ¤w÷kؼCÿžó`[ß*çµR`«¾íN¼p5=»ÐÈ �%ƒ³‡o`x³–{Ýûððîá.Õ÷‡ÎÍGý¼£óºùß/Ù°;&!%;x5éÜoøè±vò+/Š1üñv³Oì-&tì<°‡Öº÷úvn¢RÀ}öu-ÝgAƒ^›³eïá3 IéyÅ\²wõŽhÝcÈãcžìZbóZ[ω@p·Rʃ dñ„–à…6O²äøÑÎãÄcÕ N���’O§Ñ3=J™¤£˜œ"úxåC—±V'p{lMÞcÖKH 'ìÉŸ`ùS2èí¥ƒÞ./iÙS�€Õë1yéžÉÚÄÏÚ6xVõå¯ ÐgЬ_:íËÇü”%ù5ï;á—ÿië�À³.\ûÑÚ®å)zàÖ÷ÎørL¤ Ø÷Í™Ï^ŸþãK#¾²÷oñô¬®±›~øaÚÅŒbtªÙõ•¯§ v½ñEo ºfã.µŸ1cÁ›O|bt m?ì‹å“‡0�Žþq™ãÌ™ß}>~a¦ÙÉ/ò±O÷ ½`¹ÃÌ™?|õÚâŒb½Gp‹‡ßt¸\?þÇW‹g$å˜$×À¨¾“çLê¬Sbl4“¹›ó›õá¼Þ\›ZÄ\šÜ;yÐCm\”´[Ö'”½eå_YŸTvl±kwÏ�Ÿ¥k—ïø_÷A#¾ûÍùó™_óÚÒ”"tò lÖµW¨=� ÏÀÏ~ÿ1䣯½5êÃ|nïîÚ±„;ÚzØì[O\²ÌýãO–Ì|áûl³ƒwXëgšŽèhã‘ vUì.Àôm-uMÕTþlØ(Ûö•OÆÇOYôúˆE²w“?èšµkéçÛÞM)�ïðO~ùþ£•œy“H Ÿ^ø›Ã'Ÿ.XôÞú´b;ï†mœùñEÈ�d»Çt¶oP… Ç-|ÁL–•ت}+"âœ#¢B\áœsŽ�D°ë¶êÔþÝÿ^™[þÓ'Ú{7°-M^šùÈ20ä³ÐNRp;°s�åÊ1Óª‰jCh·¡ï–;«8ý2üòL¹¿Œs3}nu„A]§u·LB`ÈCdŒ† q®ÙDDÈ“%ÆC¦˜LF“ÑËËKbrNAž½ÌËÍ3˜Œ^^^²^'IÒ‹O=sßý÷c‰o‚E—P*opY¥€í7Vjîµ÷¯€¸ê÷•½zõV”ZO=Ë‘$)&æhË–­ë¢ð·Ñ'•QG{¦N‹Ý¦µÈŠ Ôa¢;µ–$«âTÿNu*#«:°š_” íTM*è!L¹é•© ÐÅG×óårsI²T¢ iWñ,SnúlN ‚[ ªN„D�H@ˆ–Œå†õZ€82Ôét’$W½JE#+ýÆúoÕo òO´ÏïLõ@ -d�@Dmê¤Yf¢š¬ ÕTXÀ%s ³¡_4äÈNnÖ? YÉ…×/ä\8\pdMCw�töqhÖ¯â‰E‰Ç„š@ îfˆÈ¢ËÕo��“´Ëb ¾%�0q$&1ƹb6sÒéíÔœ>ŠYö*F@ w#Œ1®(L’c�Ä9çœ+†€nfË£¢5Áù]ÐûÙŠcá¹%ï˜ò2IÅéW˜©ÐAÇ\ì¤PW=¤“ú¼†’(5¦ó»î ˆ@ ¸- SM´¾ÀÍf£¢(\Q"0jØÆnD†LU(ÈzJ$™M³IQ¸¢(ÕÌøöŸAMt»¥¨1Z(ʺ)ü-BôIeÔÑž©Ób ‚:OI®(Íb@Gi rdˆ!ªÛ25PØÊKè•P”zÉÁ7¸Üçvþ‘#?*øýmÓ•ãv(1§’c:GýÐ÷YpÛŠW(J½ä•�º q`Ä”@ Ü5h9ʦ:˜Ä9—$IËq¨ÆÚ4“ˆ€È9WˆËÈ9çĹ¹æjw— ˜E©2]î Ô]áo¢O*£ŽöL][ Ôiˆ€ ç¤%Žf(I’ʬηˆ¬!lk¸oOË;ÊxíϯÃF~Z±4³wqyb¿zR9µ‰_9LB½‹ì#7¿W gX‘ë~ Û”JüD Á]ƒE9 ¦<TßHÓ¡N10-s€:Ì1Î8c¨…à!P€›2¦±D÷½[ÆE1›ÍujRŠ:-ü-BôIeÔÑž©£b ‚ºZrEqÎÆ™$É`M1Md™9•”®.¶ ;]Ú“›pÌ5¬•Í3Xýæ¬~óêÔ›pÌéÒpøWòå àŽEÝþ'@NĈ�QQÎ5#85Ó¡,ɲN6™MŒÎ‘€R¿@Ë’Èj5YÙ ¯¯ïÞ½{o·7Oþ!ú¤2êhÏÔQ±·T/-š@ ¸Ã±®ý­[)²–ðIõõTwb¨fîFTIÆEW{)qÙ»ãÖ9¹ß´Ä¦‚ì«ËÞ ±—l^¢Æ{@æøõs·;Õ§þMO )cÿ/?]iù\_§s×죚ùÛ•>fÈN-p¨çigóLž´ýûÕE}^Üèvj<4ù_x(Êá6J!70k>Dë[²ä3 ιDœsι³@MpH�(¡Ä-µa…jäeWG¡.]ºÞÍ@P]V¯Z¥¾±®³Õù’Â9•¬–ñ¦g¤fš²ñ €Ì 'ÑͺZ‘b¾°pR�dVVMõ† ÿ÷Ý)³ûÿcûˆ'¯›5ýÏûk?»oÚÖÊÜð|‹¶ãþÌ%(X5¦íýß«¤µÊÙå~¼/×M2_ÞðîÐv‘~õê‡ßVxü‹>Ñÿ9_ÝL¹”{tî较øw|c‡áfåwÔSÚŽ™OvhêíòØŠ¬ø…7ëþÑaSu«1Ä-{yP›õƒüš½¸*ón1Ùw겟i.tª–[ó#àDŠ¢˜³Â9)f³¢(Ú œ�H’ÊüþçwÍâ¹²aT¼ÄK¼ÄK¼ÄK¼î¾�”Mج¾çĹ¢˜L&ÙânPòïÍM?l¢gè•q*öÛ=ó©Î¹f6¦üìó 'ygžÒÛIµä:Z¼oÍ&c¿O»Û€P-øÕ«ŽFû,˜­Õ>AçÈ#ž; «ê<sÜêµI]^îï“·ùù‰kœ&®8þPˆä`op|l„wKßjj.”³?¾5ã|ç…;kïb09Ú6[¨†ü!Êþ÷&|Ÿ÷ø/{žoê(Ù¹ÁÙûž2øIÕ¬%ù×wÿ·»áÌ¿—ôõ6ì<î–Y¶@ ¸½�C5#³ª Ì\R´! ‘1”%Y’Õø;Œ!“$I–5Ó9Ibf®©eK@ w!êò¿dRD¤pÎ9—µˆÄ 90BàDÕÞr'@PÓVWÀEÏ ãä™Y‡>ùkÃÖÕ¬27þèÅŸ§øC¦‹•-¾‘UzÈ&…{WýçtúÛóKÖo~ß·A¬Øú™>|ø¤W�ÀXÅ™æ“kצty£¯‡råLlqë—Ž®ç ��ÎG¿Ñ±Ú˜bO%ôý¤WˆÇM9.Xå‡ô-±é=Ü>@3­ˆ~äÍèjWc¾pò¬]×1C{énF @ ¸9˜šòðÿØ;ëÀ(Ž.€¿™Ý»‹ûÅ(!Ž÷àîVÜ)ÐB¡ÅZ P XÑâ¥8´Å!xÄ`A‚F‰»ËÝÎ|Ü]Œ$J ý˜.·2²;öæ Æ�„#@#�„å„P </¢"ðÀA ” AFÁ"Qy‰¬„! ÔR0Ê`0 ƒñÿˆ PNéµIéº âæx•÷¦Jï´Ç¢¦©›Q Ô Ò -ƶ\fÔæ±O·Ï(LŽ®ùn…ÉÑO·ÏˆÚ<Ö–ËÔãn›Q ¨iêÖ>Ÿy'.ðú6Q+¹:»®ë,Å¢¾àÄÛÖKÈ Ÿ˜ÑÁËÉÄÜÎÞçË?“Ï~ŸÔ½¡‹ƒ‰yÇþ;_ �BÔÉOôîZÁ³Aɵ¯]½f\Uèë I§¿míjgbåÞtØŠ ¯Kíd÷NžÍhÝÛW\./¾0ÉÞL_já8ãjQÔ–¶w'Ñ7““Å]\2¢£»CK.“v?Ì£� ‚üņ.R©™¾E¯-±Š#ÿ0ÄÇ¹Ž©­w»ñ›‚ÒМ«sšÛõÙùR@SONlà9íBºBœR.ÿrA.»½ÈÛÄL_j;ì¯ÜÌÃ#,Û®}"�y}jv¯Ö.v6R‹º­WÜ•I Þ4¶K{k{çVc~ Q‚,sÿ`k}©™´ùò{r'ø¯é[×ÆÆ¼^»¡Ë.ÅÉ�Ьðun½ü~�Í žÓÌsðÁ×UK’ £vBˆ@€R@Àa„�QB�(U Y% ï‚ Œ9Ìq˜CS¬°LÀ˜ã8ŒÆ”Òj„Û ƒÁ`0Ÿc ”R J§M”¢øÈcŒ)¡”R¤ð]XJ­úÖöqQ LÔj8GŒÁÉP-;.(bY�gælàÑVÇ¡XÏX¢+€âìÔ’¬”œ—w2„Ägæ:"]C5€ª(”—U¤_Ǿ¶¹„œ€ãš]wùT£§O3Î,ùþf½WŽûêçǤbc ò¤ûQ® .øuÖÊ/à-9žž>ñ²Á讦ª›]"Þ²óâm[꪿>¾pÚ¸‰úþ§&:r�%wŽÉmûck]�9�ˆ;o}¼µ·&Žçã"”— “#ÇM=n·ôph{Éíµ£¾œºÆóòÂú�À9uæÊl7a^\rsѨi—½>t­­Nô‘yÓGLÑ 8<¼õ¼ý;Mšõ{»-&k„µZu©£!�^”æŸ�ßdQÈ£,1Â"QþÊ\ô'÷Œ§œ?2D*Ï£F\ܡɣwqßì >`ütû—£'/sYí+@Òá‡WµâÄðløIû¥³»ÚÝ,õâ’)SFª¸0ÓsÆš û·®ËþæÌ9a7÷Ò`ËÏÃ¥8ƒÁø§P`@a�JA”�PD•Á£Bˆ � ” ¨Ž`ŒŠ ƒÁ`0>G0B¤Òt!ÅœG€�•Ù$P ðŽ ¬]ÒŸœP¸Ž®ùL] §+å £²¯½ˆ¹Hä„–�Äæ1Ò’`K5^Cúv¢…``íRËLÒ¬€c:=x‹«± @"--qVä£ÈÌv­¬œ´T_‹ ,¬ E`�òG'OG7ߟ 6òn×ÆÝ–Û)‹ÆœhìôËq3¹â›'ϵ[ÕZ[•©I$�€Š® Ë’“ßÝz8Âkêïý]¥ºL[ÿðž«‘ ê[ ĉ% �ÅAþHé°lɰú:�ÖÓ—;ÛîÐñ¨¡_Û·X°²ïØ!]ŨÍÚs –G”å_ñ¸9‰D"ÁPùÁa [S# 0µåHˆùðËã˜r`:}b»‹.‡Ë|��p¼šD"�ù½£¹:¶™†:#V̸XÝ_÷¦¹úxNýeâåÞczì/qYxa 0Œ¿¢ª>!J()ð�Q eÁR ” ‚Àó¡eC!F˜`&&`0 ƒñùBb¥h¤ þ¤šWa@ªpˆHiy ²N¨­:¥õƒ<õb¬¦ Hõ¶ ›i‰ Õ\¤êž¦šž¦š.RuGC53-±ky“b¬v?OÝÒÆº–5qéÄ5£î½½¶ô½)ÒÐî´|ß,Çßµðh5x©_䛲ù“ã§ã›÷é(­]ÅpVš˜F� ƒŽ_wèõN®…䄤â9 ë˜ZؘZúÌ •egdUÒa y‰ yF¶ÖÊ­l­QrB@:>½»%Äïö õÐ{å_•‹ä’§ëÚYÛ˜Zؘ:N>Ÿ“‘Y¹æ„¤øduk[©B €ôl¬uS’�HÜúôpHŽÍªÛ¦…0Œ¿ U(¿AË™œ(�%(À€8„•? ²³S*”Ý!„0®õ€Á`0 㳡 �Õ$ •þÿΞŸ-Ì- u?p?r"úïÀ‰ÜO7lÔÝÂÜ¢VY¤iN„˜õèáÎ� M õ‚ôÌ¢J'a£FcÖ¾÷çÑ_S§îŽ©´&—Ý?y*µE_ßÚºõb"£©©… †ü cqçÞïæ:K5Ú®{““œ{oiãJ‘–©™fZTlÒó@\T 517Á�E÷7|D:î+¯¾?ŸJß'ÿª\‰½çÇÆ$ÅÇ$ÅǦ$>ÛÞU\é$ÎÔܤ06*UQa4+&6[jfŠä‘»çl/6£KÔêoÄÖ6ä#ƒÁ`TÏóFˆ"‚L.ÈäTˆ@” qŠå? e—wcŒÑûÇýe0 ƒÁøD¹¾† ÿQ åöRJ%JµƒÚN§ÄbQ»®=cÕlÃ’JX(ü“1‰5Ã’JbÕlÛuí)×ÊÓ>M¾xì†U¯^.Šè�¼“Oµ¯<‹ONŒKÍU,oKbÂn¼HÉ-›¹:ÑÜœüŠ›æ²;'ü²>�††ºû8<MV)©âôøøÔÔ×ü–ÎÞßth;.÷ê‰KâN}›Ôä»áMDž}úÛÞX;woЋ¤ÔÔ„gwž&¿éAÒtØ@éå•?º—ø*pãü]QM÷±ãŠÃ7ÍØ¥ñÕšù V/ð¸úý÷çÒiåü×®N÷~ _윷9 ">551òîãø7Õ,x÷~C]Ã×Ïû-4:)æö¾¹¿Ür2À[$Dî™ósÚàµ?ÎZùs§çKçîcî Æß‚C¢”BŽ )%J€DË-D¦€”Jt�ÊqM)$WŽ„Â:%ƒÁ`0Ÿ/¥Kþ²¥[,•Î{W‹†½ÆÏ8øZ{gXJ±X‰ÕÊ4ôü œ‰Õ‹ÅÚ;ÃR¾Öî5~†¡A-³—àwò¦]ÞNÊàWH·Óâõ#´NOkݰW§ ÑΞºˆ¤„íø¦·§“cŸ ̧.åR!TVñ­£~ùm{·VEVÀf]FÑò›²àbAÙI¼y=þüôÆîÞ {/­3óÐÖa6(çʉ«Z]{Uç:±ZÄßìÝÔ«pß„Ž>õ¼ÚôùöУü7ORo2oÏFßäuƒ[zµŸ~Rgü¾_¿°è= ƒq?Žuà±yߥ3lÏ/^šY)ÿµ…«3rçÎ~Ûŧ¾kÓÞS·ÞJs^Í׺kÛphT+Ÿ&#vöûuß4W>õô’µñ}–Lk †ôÛÏù¡ÅÃ?Og†À ão ——ÉJd”„�#„1*è@% @"„h©ØºÒ Dñ磂Á`0 ãScŒ1•ÑBHá15lÚRªðb¨´úT|ùJí()‘¥¥¥?r0öæyCbo¤éh¬e ù®‹ãªÉÈ/~‘’™^p3 Y7îÜgÐP##£Zª�ÀÖžŽG;^8?ÕdÌn8M¶ææºNïâ_��hƱ‘MÖÔ9xuqÃÚæöŸà½óÏ`0Ÿ^Mšr99^Ä!@Žã@ib€0‹ÕÄb>¿¨°¸¨ÈÈD*“ÉJE"1!$;'‡ª¡¥)æ%èWcÆöèÙS©i€Êåå%æ¥ró>”Rý]…ï ƒÁ`0ÿ ?vlæ‚o9ŽSlî+C e0…¾Ê¡<ï¸ñ+‹LMM¾3þióVîß‘Ÿ—úAJ¡©¥mlå`æáÒ×Ë«®³³¶¶–BæQKŽ?¬×g£í{Ë� O\&Ö¾“B�� iO^—vûÚëcÊþFþ ãB s¢”Êå„R*Â\9{)#��(Õ €RB�„1PZ!D"ƒÁ`0 Æg „R‹JË6Kxå슔›3)” Þ5 Œ±®®Ž·——k=™L.“ˈðaüÖaŽñ"‘ˆ‹%µW"(åBÔÉ¿—¾F‡õ÷ž¼Ï…H:xOôà¿—øà½óÏ`0Ÿ”ã8ŽãFT0B!¨"zò��…t�!JcŒ(äÆÂe´ftÀ`0 ã3‡Pª…·B•˜@AÅHQ�@Ñ{n³ˆÅ¢÷XÉ3 ƒQ3c@ˆR„`Ìñ'(†5¬0©€‚b8£”çx9¡r¹ c�!DAiMðqKÃ`0 ƒñ1Q:rª \ªW 8¯R¼ƒÁ`0>PB€R*—Ë•TÎ�UéÆ)Dã#Žã(¥c¥Ú¥ð^Js ƒÁ`0ÿO`…ËÂrþ˜îT“$T“O&ƒÁ`0>:¨Â’@9žQ —;*7©àV‡"„ !D „J)ƈB™±ƒÁ`0 †•c寪¨Q|©ä€R…Ÿ' ïê½Á`0ŒŒFcVúåÅ*— ¿…!�ª}s–Ëå„9¥„Œ1!*Q8¥„0yƒÁ`0ŒÏªp\¨ÔÓ,UPú&P 0 ã…¥(¥‚ PŠyóSZ¦W^cLˆ@ä‚ � ÿ<˜ÆƒÁ`0  )çÂP1OR¸0$Ê�‰ˆb� %�˜¹€f0 ƧUhÇ)B�D1ƨBŸ�cÌ!Äa(N„AŽ0Ç`àd…E¼ˆ/)’‰x1EóMÀ`|L _^9rîÖKÒhÚt_&¾c0Ÿ'ù±·o†¿5×ÛMòo'.Š1e-JK„FÀÜ81 ã?€ÊMaéD1P� 御T r¹\@)”„¦q âyTâpšyyN 3ãº=·<‘}ì¼ü äyI/îE$ÕªBAjäÃGqÿtž>aÞ¨®ÿ“×à ÉçWL™µdÍ_áÙŸF[d07z¶Â°ì ¬;, ÍÿˆÙúÄ‘?Ù>ªßÈIó<(ú©#\­ŒS`ö™ ƒÁøoQ6ªQ…'ŒVÆî!DËå”Å€�(/G DdÅEÅE%µZ¦žSGSKWËù›«åÇï\¿±6ºbu=¯Å÷åÕ^Z;„È�¿ðÔì8ÿs·³>y»?!%dÓÔž^uL55uuÌœëw¸üryø’fR[Ïß]È~kÈë_»Xš»´äµð¶s?iJ.O²Òâ%úv$+ Mâ6úêñí:__¯q©_Uuý§^ƒÊ¤?¦´¨WÇBW[[¬¡¯gîìéûÅìmA ŸÀƒÁøW)Œº¸áëÞ-ÜÌ õÔ´M­=}.T…×�� �IDAT~³#4í£wUôl$9ÔïFbNò-¿àøµÃ'ñ{¿p33Ô©iŠ4ôuÍÝÛ ørÕ‰Ço¢>;ª‹†�<PåNÌ¿˜ƒÁ`0ÞJ! €(P •Âô(>Ó 'ƒ"‚Ò_/Çq%²â‚‚|¨&]û]¤HN<ûWÈOmÚ©+¾Ìñ?z. ‘×Aîüß,ï=}ó*z*Ýkt?é'>ß]Ú£ÇÒû…æ9YæëpÿÓ#—#�Aj˜jT„ „|æ3µ*ªë?ôTAa|Ä£ç %q</dÄ=Œ{túøÝƒ7¶v3úï‡Áøt¡™×¿ï1håíRœ’„§!Ç“ŒûÎ×ô£f ªêٰ͈_6g|e=xŒ#÷¯æ¥$=þujžK´ô4QaNÂÓÐøˆP¿ßwôÚpz÷Xç]³ÿ†RÝt#„ ƒÁ`|Ê @�TµÞWI°ÒÖ@¡‡â0�L+85Tü"‹9¡”Š%µš(hµÔÛœ’|æ¯@¥Ò$ͺðçÅ ‚$t․‡nÚÑÓVWWjâÒnøÏ—_Ë�HJÀªIýš»Ûéé©XÕíýëAHôÿyH‹ºR]]-©C£Á›É ü7Ì]¹aù{+6_‹"Ï,ÖÊÁÄ@ÃÀÖ£ëW[of(¦ƒ$îÄì>m=íMŒôÕ´­ê÷™ýdzÂP¯µE~ìÈ£BÊ™Ù÷<7?§(#òÞÙÃ?ö4(­ãâ “MÕ4TìÅ¡+zx;Zêjé¨X×m7võõÔrº‹²Ûó½$ ^«éOÈÿs€®¯á6;T@¢7uÐh¨·\û\� ™7Ô¦ž¥¶–¾ž…“Gçå!C-óÝ¡W–öiÓÀÞÒDSSWÛÌ¥ñ€yûÃsÊO¡+VWù×€f\ù©_{GKMM-3·¶~^ûýMM5µ-½{w<²Du“—'Ô¼ŽT_ÓÐΣÇ7{*&ñïÂÙL½“—ŸxïÈXgžÊbþÜ{1J®LµT×;S �y‡ðíºsnV¥j {}eõˆ6®R]=]KÏö“¶„¦)^™*ƒñùAs.Ο°êv6•Ø÷[þçÍˆè”øýnÚðMwCÕ4ÃYÿöëÖ±6ÔÓU×·ví4mÇ]E_Q|kÃpß&žu,Ít´t´Ì\[Ž^XÖUW×$h΃}ßõöq4ÔÑÑ4vò(Ju¤BÏ&»½cþÊõ?Ïûõ†²Óz÷BIä©y}ÛHõ5ôÌ,ëµ(¦ÖjðØzÂñøÄ¤œŒè°3š¡â˜“3&m{&�˜S‹Ævnâjn¨¯¦mbá9ö@<’qsÛW½luu ŒœZ ùñÌ+åpS|kýж\-Œ Õ5 íwÿjÇ e-Õp¨Æ ,ˆ80³§»¥‘º®¥Ûø¿>žrª~Ë„EƒÁ`0>mVè ”š T!Pè€ÊÂJù�„�Qe´D��J(!2óšµZ¥ªÞlÄ`‡]«Ÿ¥øýy%§cO içŽ\ΦH³õ°>¶¸àÖ½º®¸_ 6°­c’}ëð¢AOŠ./lÈ¿¾¾ÿù'–hëë¢ü$¬k’ùפ¡?Ïâôë¸yèÆEˆŒ*‹éIü‘‘íÆK$H]GŸÏŒ¸²cÚ;)/ýÐP¤Þ¿pñæA¤edd„Ó“_X7&Fd{c™øÃÔïÛ@:z:@È~ü´]O#÷ö&åOÀZ¦væ:ˆ¬ôx�N”—[ njkX’ó*øÐ¼ÁE–ö6TÞL]jc­/ÞÆ°FqMÁ•ï~³7´­Ü=Œå‰Q¹HWôÏñBs"®]ñRCšw÷Ôº1×n%]ñ›å®œ“U¬®J×^=ôHÆké§EîYˆÓ2Ðg'?¹¸vÌ3÷ _;p$þÈhßqG“@ÓÔΟ]Ú2þ~¢øÖþ!fÑçéšYKÕ�AxÅšqqV‡~;_ÈÔŒíêh§½º¾{V·W$ôü—N™M[Ãa0>hÆÙmGâà]¿Þ³wf}Eßi`dá�5 ’œ'~Ae¼†‘¡vVÊ󫻦ö,ïÿÚEGö*ðìÕ{ÅXMÇØH77%:ôà‚~9F÷ÿüÂWÛ$¹¢«û´ý>4DÇH¥ÇÊ4¥²� ¦ží½r¨µm˜U…b©£»³zNôëB5ÃwíàDêÙÉ¡œÇ®S.æÞ>|ôÙ—ó^øí:r9…rêº:BvºÈÀ°èÖ’î—ßÏ‘¶FQÜ?— y±;tOs,{t>0¼«éšH¹œ¤Gç·~}-(æÜÕ¥-´k8T}â´S{Ž9O@¢'ÅÉ/ãò>ÞZ\EºJ0¥„²¨ ƒÁø/Q¦MP>¯âK ”Š\6ú!„€RJ© —"ˆD\­½÷Š<‡ñ#’vþÐù $áÔÿ\Š »Œée )Ç–mzϹ|í÷øÙýGÏONtÀE~û=°Xuµ¸Ãæ§1I)©Iê%GFåQà=§ÿq%$ô^ÜëccM+)dËÂ6-;™Høº“νŠOŽ»±º.Ê¿·~Õ©ôÒRŠÛ­ù:æò·n<•½<sæñ¿¶¯Ê9]1ÓÇ�ÜÛ:ªa;Ïž_ÿrþUy·T¢æ ƒÂï?~x{ç�C|ƒÅ·ÒbÞ¸|&àêö/,9’p¿t ™wûúDøÃûïžäPÃ’fGE§àì†íºtõÆÝç ×f¹þ§Vˆ|ýy¯"“"ýfxHhöUkÎç¨U¬®j®Œyþ[oC HÒ~Ó“ØÄ§;úbZxçrPÙíM?O¤ú]6†¿|ðäeغ¶Z4ÕoÛѸ4«#ñûG{º¹Z›Ù4ÿù¾ ‰í†Œí¢_k“áåoK÷¼”©5]õäÞ«Ç»£œßö?“·7ã³@ˆ|ü´ˆgÒÚ×ý ùêÛ‡¾þ¼ÀÈÈø;Ëš«!!Ñïä­Rµ$l7ùDTt̳íÝõ1Éô?}5·¦&IÓN,Yu#´›-ðKŠIHNx¹©³–ê^Õ÷lï•Cáõ«èbŠ4Z}*((4"îÅï=µà}À&-[ÖåäQÏ£TÃ&Òz0*1>9ëå:ßœ£?m|ô;­»˜ur¬'Ä[¾õAé‹í&ŒŽ}¼¨¥>Þ¶ìDRáú´×m<š “~»&ÆÅ&ž›b÷ñÆ5*T;b`@!é€Á`0Ÿ:”(#P„PéÄ¡2*PB”}J¯UNCx/âE<ÏËåµUò㇌m«…hÎ¥Cg’ä1 * œÍÀ1]ô‘ìÑ­»ùä¿´7«kvÞúR�’UÙ §¦&â:õtÕ@²;?6söî÷Ýöi•g’xóvŒ8Ç>Ã[bP¯;bh3м°›•…êîM=5”ä7nòÏôÛüxùQðÞe;»éæ>¹°}v玲ç]¯ÚåI¾²l@=S©±³ƒ×¨}¯ ¹ï¼c‚ŒÛvo®‡å/·uqòî<m£üRÛ¶˜2²¡HöݰçïVdÔÈÇ‘J²³s 2hØØJ232 Iº+�Éð›b§©ÁkÖæŸG©û*ö#©¯RYfÜóQ YÅ´›Íõ ÚÔåÞ¿.£´0t~M‰†Èrø‘Bld¬ðö†Ã`|&¨dáUlÿÖzøÙø42Ã@r’S*Ypa©Tž–œAjh’²Á7r)Rk=qZc �-­Z(µ½_yÏîÝlDé+/ûÿ?ô(û½…„‚Âi?BoÜWS£o†åS¤Örø0'5ÀÒ¶_ô´å@yóv%çHÛ{â„6êˆÞ½]Éü©â¡ê+Pþ4<¢„"µfýzZò�¼š„ÿ$%Ÿ<R{PJ ¥@)B@iõú*Nœ:ýodÁ`0Ÿ½{öxË�aÀ@P„ÆÀ+Â$*£�æ8D€„ „D)-**Àˆ"ŽD./j퇙ößsñ¥É×öî;uøv1ˆ½FŽmªPB�x‡îSûºKT³Þ¶¾FUc¾¤þ‚óW6ü²ù÷Ó·Îlüòü± }Á{újTq¦j®Þ*‰%b”áßÕUäë÷û¶~¿oWÆùÍéÛëã[ÖœÙÔSQpR–Ýü‹‹F-;Ÿ¬é9òç/;˜$ýaѱ8ÅQe%Uˆ²„1FhQQIåÒp]·Ø´vó®¿‚®lÿÎÿè¥Ç'ÚH¢&A�$3#“€ �$+3‹�€DíM{$‹�B˪ v6ŸJ/D�X,F ŒQ%�`Ã&_ŒoSjf€u[}¤ÍÎvÚ…Çss¿j>`WT^xÀ”¹Í¤�Çq� +*®yqO Sº—î¬!‘›3WMÃ1þ$çÕ Æ?gmo+BÏ “¯>)iíYõÒü­ÃK$€PÀ•Žˆ‘ÊÏl Mòµ@��a®b#¬mÏöN9Dz¾k®ø{¬_³í€_ØÑåcλsüÆš6Ú5'QBÔÅ‹Oä�¼C=Ǫ]WêRª/†H"Á pþWáš*P‘@Iñ'†ã0!!ŒF¡¥‚¥oejÕƒC¨ AK%FÿÏæšÁ`0Ÿ YiIïqUù± A•›Š¡¾ìkB(¢@ð»,¯uÚátdÕÓ°•SŸÈ‘v§ñ#9�à]¼Ý${ŠSst:NÝÄX„„œ˜¸bK[m¨bÏXž™$s²ð÷¡sãv n<ùlÂéS·Jú¶);›ù4´æ_½8±ïútvÏ÷ ‘ÒònTïïTøøëGï©7jéi£+éZšëq�%òÜì|¤£¯‹ä¯E‚TCÉhFddÞ¡×Ìi_Ôƒ§‘~<��X[_‡CPû("›Ö5 2‰xs)†¸´Û!/Š[»”O“f'çXt›³­Ç쥧'4º'.ð䵌 öŸ@4�ÞÙÓU Å„ïY{¤ûòÞ¶ò'‡Vî‹�i»y•›„ÊJä $òÄ©»2@u]í8œ]¹ºè=.`s/.øuv*q>{ƒ‚â”è4u‹(DÁ†—­z±ÿÞØ+güÖûÜ$;ÎØÜ˜ƒø’ð ÛyýZU8™ç9� ‰Ñ±Eà¤éîíÌŸzPœZ`Ñæx] ²¬¨$°µâ«i8Ý™³rÆç2êØ·•öÅ 9~5Æ`圾 ­´HnÂÓ›wŠ iUÃð‘÷‰Uß$©Ž·›dÑõßv…wýÚC ùY=Í7T¡c«q€«>‡E©©¢c—ý5vAÄêž „¼<ã¾¢Mówé3åÙ‘Á‡þjqH!EzmF ´çÞ¡Eî>õ5vûî;ð¼÷$‡Ü€ý§càÖ7B T»—È€äw,¸"ÞÙÍ©´«¯âfAµ¨ÛÀCmÏåÂÀ]Ûï÷ší¥þEùð¨ä÷Ê_šŠßߘw(ŽÕ‚÷›Ò1 ƒñ‚T¢n„TÙ?Ê›áÛÅáå{M˜Úf˴˹¹yÀYýªŸ9�ÀææŒÞ²åÙõ;Ø®50”dfy.¹øã›÷‡¯ïÔzcª™µ™MyžMocg]qE'jôåw=L<ñtkgûCú|~F® ÔݧÌêi„ª’;ü»O÷Íýã}BÏ#A.'fý®¾–¼u«6N¢"·tµ?)åóôÇž ìàeÉFÞ_Õ¹I@=cYôKA5åÐnÚ¾‘ÆÙ멌vº>G½z|økû}zØlù5òÎâ––Û E¹É2åÙ$fïðzó›X[ˆ2_Åà¤vu´?¾Œ��q¿oÆþ°ñÉ«ý#êï/ýVÝsÒ¬ú”»L²ûK[í4€Œ„Ì"àë|1¹›ºoT×­Eïžq‹©³Úý1ãÒ«ýÃÝÿšj¤ ¹™9ꃎEíè^;ïœÿ È ó÷ßw:5Ñ/ëêò¥§ìêãÔ­·ÛŠû÷^nêæð§T½0µ”˜XêâbÂ]‹ÛÕ×Góô½ŸÆ}×÷·Ä™ÙÀr¡TËÏÌÚo‰>1Bïí ‡Áø<ÀæÃ×,?þÕ™Äg‡gõ:<Kõ=ïú½{è÷Õï#&à\ªk’Fæ¾¹)hÛ ÿ¹ÍÌ–êñÙ†î-õ~³g›WþŽ5 pÕæ°8èûÆýSKkS­âøg%©ÛØÕZJb¶t3ÜE‹ e¥�HÓeäö-#mªR¸BÆýæMÙ°êá…¯™ý¨!ÏÌ)$Ø´×ìIõKËB̯=,މ²Ó 6î9e  †‚jqPmš ˜;vsধ?´´Ü`ªG2“„*åÿ¥þh¹mTîÛ2ÅÅgý€Á`0Ÿ”aÁ[ÔÞJ½@©.§øPå•ï&&�l9púPk�©5÷eKMU2ºmWúù­ÝÞÕT]–ž‡ 8Tm.Ètí]IÊ«ÇbŠ Ý»NÛvðJl5lïå½ßöð0æ ºŽ­F®>sfqÍ*oø/CÔÝ{ ñõ¶5ÔÀ‚€Ô l¼:O\sêqÀ{Í<ðë˜Vöú“”*Ó×òÍæÙ8¶­³vNDð•€»IbK×f|,1�¶·å÷¯}]Œøü””M©º¬€‚z«lŸäëbÌç§$g#=k×&ÚºêaZ aåmÉgDG<|™«]·í„u‡–´ù˜‹àòè´úé’ßê ¾®º^¤n`íÕmʆË~?”\XÏÖN·(#9ùNß}|u{UT×ûyàÆÿå¿wn?Ÿ:8/=«DýµZáÇžÀa‹a ¿t#’||ÅΧrÞmÖÁ=ßöð°P+NMÊ”k[ÔmÔ®S}c i5ÏýXj#CS›õÛplá-¤â¢ôŒB‘qÝ&vÚ…´V ‡ÁøL;Žþ#ÔoÃÔž¤Úž—hÛywÖÛK“|èá£Ú& HÏwÍù³+Fµ­+ffkÚ˜iÊ„·÷lï‘CšR7'½’„g"¨yþslcûvÃ*‰ÔÚÖD‹¡¤°kZ¹·ê3mõ±;Á¿öµªNÆ ÙdÑ™s«F¶²×‘ç‰Ì<zÎÚ{ù÷¡åŒ¸†¥±<+›èÚµ½ùÔ–A¦¸ÆC5T NëŸÏ[5ª]]#”””ÇÚ¸µèÒÂá#©H¡rv¥"�„òiÕDnšRJ ¡�„J¥xþÂÇÉ,ƒÁ`0oàݼ)FÊhˆc„•«Œ1¨„��!Œ0¢  @€Aÿ€" ¨BX�fLšÞ£gO¤#KuÊ”ÊIjøPJu¢‡wI0þ Ñ›:Õû&˜ú,}pu¦Ûûf0Œÿ*y‡X?%s˜qùáŠÆ¢ÚúäAèø±cßþ0‡"¤2'  ƒÅ,‹/õH Øiy›ãBƒÁ`0>H¿ ÜB]eAPéPõwP(°áŽÁ`0 Æg Uy"(.Jñ=.ÕÆD�c„�—í©0 ƒñC1†Q•‚òÿ"J•ñ¤h•>Š ƒÁ`0>#¦U2å† ¥”�•§Ò±aÙqƒÁ`0>*L¨rô¢ (<ñP Tip@�+¡LLÀøçÀ¶S/Lýع`0 ÆßEkÈŸCÞùÐ…×B•‚@™ËgJ)O)PJPé\Š* ˜CƒÁ`|R” fHe}@K%Ý*%J !@@éº�0�UŽ€a„"%ÿǬ ƒÁ`0> (‡°"|J� ”(=Å*ƒPJK?1³MƒÁ`ü °Êß0ÂQPü©B”ã_i¬ƒÁ`0ŒÏ…ÅR�PÎG/ŠRÙn–·âd0 ã‚€R·ñ¨Ü �”z2¤a9¡F@K­`„R„B°qŽÁ`0 Æç -?*_z¸L—�”þÝ2 ƒñÎ U „T Tc^åaO9¶Q¤Ð9 Ì ƒÁ`0ŒÏÅŠÒhÐ`„�€ÇS¬t Q•©'›>1 ã“B‹G1ˆ)<”ók¨È(¥å”zãQèÔ)>*ÎaZs ƒÁ`0>g”A¢Ë…”FH!%�^e‡À¨’ñ,$ôþ«t³_´2g–¬Œÿ"%Ƀn=|Uì4 _}½·K�åÑþ{þ |•ë:î‡þvÜ[Og0>.åTJ‡3¥„ š @©CÇ"0 ƒÁø|APêZ!#PNžB¸4²ô‡ƒ$œšÑÄÑÙgʱ¸wŸƒ Éë&v÷rªcláT¯Ýì“)]„Qè¿ì‹/¦Î_ïŸ |ì¬ü3ÐÔS_º;÷X÷Xþ±sòÎϯ»]–ñX–Â×wý¯<Êüèï߇€Ä3dÌŒ»ogÕª<Åa{–ü´iï™GÿÒ*Šf,êìhßhÐΧ²q?Rœñ8:çŸxzŸÜ‹ñF+ø†&€(`ªüÀ„"˜�"€)â�ó€9ÀF¸rì„!¥TÂDä ƒÁ`0>c*MõËÇ1À<Çñü{í’˜Íݬõ¥f~ÌÚ-\áéEVÎË‹Þuæ*¼Ú6nôÇîÄæKŒL4e…êÒZl}V·§«ÚZëKÍ-FË©òùãå­¬ô¥–îsCÿîâ¤àê÷š¹8;ššYšÙZ×kÚfà7ë®ÄÿÍÛþ+Hô­íLµþsv&4÷âÒAã·ß-)ûꃕEödÇäÑ N¿þÀ²¡ÂÈ“s;ÛšXz.¸UöÊɯ¬™ØÎËÉÔª®W×iCÓT-¶$æÂš1ÙZZ›:5î0nÍùXEQiîã#sµq¶µ6±q÷é=sË´ÿö®¨}ýâã´œøk—ïeÿý…[Áé±Nõ<ÛŽÛùìX:ÿC/ÆûSE+¨@áËc ¿hæjobáìÖaª€¤ÒJ)~urþ€Ö¶V^]'n½ñÿ²dF0ÆaJ)Æ4À*8,PÄMd0 ƒÁ`”C[š'„–§ôpUþ«a‘DM¤šŠñêb,nõ͆…º¤ù¨öâwˉ»|òn>yÌ»|f¶ËߎhÍÙ5¬¯¥G<~.ëÛPBÄÆîý¶¼à›,¾´k˜ ‚Âç1 ïF®71yú«ˆ¨¤NÃÀÔXä¥Å… ¼¹÷ÒúGÔñ/€tZÎ9ÖòcçâÃð —EžùèìÎåË·{•OÓ-;²dȰ-ÏJDCÄÞþkáhÁïØ×õøÜ« ûŒÞ%Kí Ò_Þ9¹fä“üSþ?4J=<¾ï¬ T¤kbÊg¿ 94PDþ™“³Üß±½}:ð“×ü~™îÃz}€Ö"ÂÿË’÷ï’ðÃÐÉç¬ù³£yAØ®掚 ~åøTòC ›~Xøâ¾ÚO~_´däd틇†Y~âvU�)â”ÿ@y?…¥®z•â� ”�E)M �B/âx"—2ÒƒÁ`0Œ…_ç*)ç„HEÙ5µfcË1G¢c^Å+~^ýÆ%žÛðÓ¯[—­=+�I \ÿÕðM¼l,­m\ ÝùT� ·vÎêÕÔÍÌ¢Ž]ÃãÖ]K�œÌl _ÖÊZ_jfì»þ¹�@2ÃvÏéÛÂÃÂÂÖ¶~×1?Ÿ* ‰Žö4”ZØ=G�J¯ílg`ìÐaÝãrø"Fžäõ£‡€Äø_¼›š™žtýÌÕ þ8¢„‚Èŧ¾�Eú-ÝÉÍÎÆÔÖ«é ç–«’|jVóz¦V.õ{ÎÚ}¿mf$²óFø½{‘/5Uy™SwdP]%TU.šý×(G©…ôËE�wj´¹´åÏár�áÅZ_k}©uÛu/š¶sFWŸzfæ6–Î šöû妢ØÕUl•PQâ?ÓÁ¢×–X@sîížÒµq kS{念÷<c]saÙ˜Înö6¦¶n zÿr«�ä‰×6ŒïâckicíÝe̪+qÊ=ïô‹K†´kâmkm-µppn5|ÑÙå–gÑ­_†uoâåbaaelåÚ¨ïÜCªd±—VîÚÜÑÆÖ¶~·ñ›o–n”W•4@±ßX3}©™ý´ó%åËò'{ÆõhçY×ÑÔÌÊÔ±q§i»ÂJuîiö½½sú4w77·6­ã^¿ýà~Iol, O×v”JÍô¥Ö¾^ �@3ÃvÍêѸž™…}ÝÖ#žxYXùšuåÇnÍë×±²–Z8:78ã÷Šíñ¢ŸO\ë)¨©UhŠ$áøêÝÏ‹±Yÿ7c^\]ÞJòïnÞt%ä¡GODËé í·Cüî/k§%QWý_GžØçŸAøºOß¿þðÚºŽú¨ |ûŽë•2S{~É_[kûÆCw¿¨jc½äÖ/ƒ[Ô¯ganelãÖ ûÔ Áoj%È£q157°h9ór•?Ý5¦£·‹£‰™µ™“ïè•âZ%a['ôlß¼®}c3ï®cWl[9µw'[k׆}¾;ð¤ Æ ¯o]¼nëÚŸGTÔë) Û:±gûæn.ÎææV¦NM:MÙ’^šGYÂÕº6©cimáÒ¼Ç×»n•7’¢7wµÖ—šy/®¤,TÝs¬á­À/FþÓ?ÞÂÍÁÔÒɹùèO(º½nxwºfæV¦vvÚ¨�� �IDATÞ-‡-<ü$¯r§AÓ/-Ú®±§µ¥•Ô²®gÇñ?*í¾h~Ä‘ùÚֳ3µvóé?wïUŸSü`û¤~ͽ\ÌÌ­Íœ[νZP¹”!{rÉ?Ùaèœië»z´ùãäÖBxÈÝ|�šzzë¾8÷›èئÏ×Vô]ýuø'o¹@(¥T)Ü&”BI9ÿ•â(ŽR !Œ1ÇaŽãyž�Œ1Ç‹ÄbÉ¿]�ƒÁ`0ŒO†–üÊ€ˆ”"”ú„àC\~&`‰–žÊOƺ&¸0ìçÁ}Ö>,é[ÛçÆÜ9ºlä³Âã—çzsŠ4±¦‰­©ÎÊ@„ ÃVêµöaˆ´ôÕ‹âï_3îÆ«Íþ[{õÿyé…;“^ùéÛ#Mæ¥ÌYs·H»Ù¢Í_º–›÷!=Ÿ&®ü¥0ù“{KFK3®<”�-½œ3¨Gñ“GqpÖ>M,1I86¹×ô“É abcÉ/vM{˜$ößÞßTY%ù¯# ôÔIZtèYC²´¶0­fãÊrSÒò ÎÊÖœ«¶ª,WÏ–íHü®e?¸ó\îëòðö*DÞ K%îjÂ^Èwh×¶NÉÕ¹#æN-‹znÆBRt.Òá 0le5[EªÊ¹üÞšI ¯:ÌÞr¢­¤½J34«xM¿ðMŸñg ~·n®‹VAb¦¡-ù7—÷¶õþnÃwÎÜËS«—îº÷ÊÏmôhÞ‹!/ͧnYÑL[H¹½wÅÊÉÓŒüOuà $îöµp‘÷v6„¬ˆ?W-ÿj¬–ãÕù Å4çú¢~cü¬&ÿÛ«¼Ûæ-ý•‘ÿ¾A¦¨ª¤�Ä­ŸYÜA §gYQ'„$=¸z§¤óÊmmù¼WçY²hg{k]{-Eü:¢×²øÆ_~ÿ{++qNèÆ낞åЮ¦«Ûܲg¬#HÝÄšÙ“M#ûþœÚú›Ÿö7Њ¿üëÒ)ýc…ó»ú•:ÊŽL’k™Y&Ǽü}ÎXTçúÚÖjMG|7ňëÔ—ÛÚnÒ™²ÕXá ÛEKÛéd‰iÿþ> ¯dÞy$k¯o¨!;çåýçYí]ã’ŠuëÖ³Ày9y�ë[˜k�`ËN=D®e=|-øº”ÚåßX<x–2kiäDÅæV¥]ùü¤¸|5ký’´¸¨›G)¶ÙÞϰìg-ñíùdvZ±g…¯"ùE©1Ù"k[š÷úΙuc2 ƒµÅ²èЋá%X¢c¤O³âï[sVÓÓW2^íûz‚^½€¹Þ¢·¦X Ytè…Àð,Ñ62ÔÉK½õçÒ/r Cö 4Å4ÓÿûžÃ÷¾’I¤¶6ZéÑÁ¾ïM®ï¨|"} k#5@¦Rõ ‹ÇêŸc ol /y}`â€÷œÆ»iiIöëKJbo„kŽX¿¿³d>=»uÝ´ÞÏòÏk_îV4ïyhðKó)¿®h¦%O½|óÚq}#·œÛÑÛ“¸Ì wûò‡mŒ³Bv®˜Ýÿeá¹Cx(~yõÌm~Ø/û{YŠós4]ÅT}+àÌíl¸=×.=™æê¡A³ï†=ã\†{hÈÃoÞ+©3¬µ"?:ÍZ{ãã·o'/‹OZŸ�)ô@©O€TJ¡r#X¹“¡ôTÅù!Ë…ê3:`0 ƒñ9£š *6¢r6E)åUŸÞ˜aÕ!v[/ãmÊ_$=¶EÿֽгDm×ÞÙ7TŸqÚ±ÕÛpNSŸ]⣕8¿Í€ßžì?üw+ÅÉ|“Îïª�€¦™»íQÒk¿ì̱urýçv¶÷åéµ;§v[àÑ}ÅÊ+·FýqqAï;%iEzmV¯ãTq¡ÈY5njÉ…EeÝ {%sp>¬Dͧmƒ§WC‚.åuR¿÷DF±Aã¦n¼ìîŽ5§“¨^ÇU×÷ µ„×»·™}ýâo§âûN0PÜÉzìá[Ë›q Ç'všz,ùâ®qý&ÙTžP“¤]}¬w©ª]ÝuÊŠ‰.€¼ºJ¨ª\óZ·÷]¿ñêîtš(�%BîÓ¹}¯ˆr–m:¸p9‡b3pv7Ÿ\ÚJ r9ÇM=õ–Š-—I•Ú%$-9 {¶jÝÈMÀógýêÐÆ¿²Û¬9·j˜Ii²ÔýëöDºÏ\?Þ‘hÓ¢›,ÞøÇ´V,��$6:´i.héU|Ëû»‹×S§8(ÖÕØÌ»}û–Z�-šJ]íùÅw ë¥Ût8½ãÚ‹s{é#€ú޹7|u< càÀ¬7“š�HËÒ¹®‹†ò«Ê{¿ÈеU›VÖZ4•F]ëvìòÝ’ö­ä×6mºg4âðžyÍ4�@ç4Ö§WQHlh[×¥®²‘ä_Ü´ù¾éøc;g4”�@ËZñ-'­ßû¨×·|Ù5’6+½¤%™iiyéæôûîRrpÐ ykO^Toøõ�ŠŽo+ŸIOH.¡€ ¥�¶‰±‚ŒÔ¤dAÔëË%c&î[×¥ÁAC’šŠFn\ÔC_TÒ®õ¦—‘7~l×ò”«—õòa �ÎÏÍ'�ªEhö¥]û_Ê@»åOöMväžýÒ½å²o÷ž{%z®<;--'ëÖòAÓ'†^—÷k«<\ôdç˜Cgë6Ÿ»gÇp;1�`›I'"& ùi©Y¹Ïv|1ldz;A·óÆÚê(®À¶ãößXä4»ý€ßã°Ç·ç§[Îi1dold൘o½Þ’b5`Ûqn,ªŸydlãéç³® Ì8@3jߪƒ‘2‰Ï‚ó§¦;‹ÓOk9ùØÍý‡þ^!'ÀC·,mô†9Q~@µÏÔôÆVýbÈÂvÿâÏü}÷ÒŽ:e­*�°yýí[i´l×Ú¦¸Õ¸u;o~±¼Y¥ýk‰mãŽm[ŠÚ´o&Íj=cÝÁ§=f:Þýí—�Ñ€ÝÛ褃�Z76No9lý¶Q«ZI��°e£Ní›i@•­ \ÍY_ñSèðùÝÚÜèÕFçÁ©Ç Víû².4/%%IMUS—Juhdb*O^L€1V¬úq9—¼¨ü@Àñ @Aé‹€"`Œ0Æ”B©LþÉ«O0 ƒÁ`üc”m·”“(„¸ô—ò¾ T»5µº9©i¨ÐTãk¼Œ“HD²ÇwîP?ßÔÍQ_jfÝ÷·HHV\LUNØeÃîP$i:d½°QË]­9Gß¹›NuZ´fˆ%ÊIM-Ñõ]¸r¤-_ùz‘këf„—aw^\¹p£HÒtà#[iÓ¬k~×…ÝË"HݧeC I¹{7N�’yñw33}³F³®åSJâ¢Ê•ñ1àÍ;÷l¬Ž¨ðâÉË*&˜HlêÚ¤y³Æ ëYhsPødû¸éG_WÞ¯RTBµåBí;¹óT~#,:8è9ÑÕÓEù·Bî=¾–F8SߎÞbdÔ²S],Üݯ~˾³·_K� ÖËI$Õ¸b·?Ý'esߦ}g­=þ ¥²_Çâð;O¨KËÒrYöôîÛ-ê(¨Ø¢EK'òèî“ÊÕƒtì쥞šþæþÄÖÞ 2R3(Èž‡?)Êö›æbicjacjé9õb¡œ˜BªJúÝàë8XãÌ´tò¨{÷s´›ù6|sUUò¨ûru›¶òT.ó~‹ÖîøÕƒð (²¿Å½ê;YÖõöhà;ûR6šŸ—_ƒ ®]IfbBN H¬64ãJeQg¶{XjMçÞúew/S!ö~HXTJa �`}ݲõ<òñ³ "Ï.½ì%�X,UQq$åêšQííÜz¶˜r8^�ZP.³BBÀ©à ªÝvÁöé^Ê%iöm}m½|ÚñT”äçV*‚vý†uy�’“G‘–w€ded‘·¥X3ذA{¨ž’I (<쑜Ң[KÚ˜JÍ êN<–J@xý¶ð*µ{Ž�5¾±*ñþý8ìÕº©N ¯&ÒkÑ¡‘(åAxB ÷ÂÆmÛ»£—÷ÃóHʃðר«M3Õ=5µi¤–ûΦô4/12¶ÐªãÐ..š€!>ðÈi¥É­!^à§ ¥”P „R•áJ!�Uõ£”‘ƒBNR"¤Ô6×ÚÏ?saÕü —S™O ƒÁ`0ÿcŒUÚ—eš˜JÞ¤ÖÓ'l9æHØRŸrëη©q*æu¼]—‰=ê•ú\ãm=5j•dcˆâ¸ð§�hÞóÛC-ßð¦æÓ®©öÁS9wO/)¾›/nÐÅ×±F3ͳ/î^gû\ŽÄMÚ·ÐA#�€  Ù¢Të4|sgM.“�¨ä@[ 2è¶ôÕ-D�$õØÄ¦“Ï$\Øs2®ÏÄw*gÓ¥³ËOw‡žÝ–þ€˜õÿºß­¥¿Ý£ûRÀÒJ�ÀnôsæÛ7íØsòÆÕß^;°úüþa¯b�@£Áô?nw½öÇï»¶Îè¶þ×/Ó¼lùC)¥PÙÍÅóZTõ×�œˆ¯FËW$â€A‘g1pã‘©®e¯¦š¡ G#ªHúݦø</…!³\€q5^þf¨…Æ M;1wúÎà|ý&cçöÑ}~pá/W³k¼š‹f¤¦)Þã””< Xjj‚/X|6,8xfº½}dBoN­^yzÐþú޽æïë5� ?dN»AÛ28©——u9}veîKJªó�þ+&­¹œªá6tñøvÆ)'—­8õº|óÂÚ–æ|Â묫+ghºo¸äw7|µðd$gßmÁ´®¶Å×WÏ?üªxÖb‰�@�$‹�PBßšâ[@ i%„P D �HÝ­÷¸N¶¸ôŒzŽXõìªÑ(Í©ªÞØ7^ JIU_WÎ;Bèm._æ0¢ î;ÔJM ®-™¾“›ä¿}JFNsh¸ïœYÛ;Ìr06Ö¦w’R( ‹ŠRÓr‘©ô“V%�€Rs(5#@J§†•ð"”…"A9÷<!$æFˆçÿ¶£Ü„ðìÄš/掟êûþ’ÐÿhÊÓ±MÎGMzé;ãj'' ƒÁ`0>!JËvP@©_@B¸üIïåÂðáÝ]ÅHZŽvûIßÎY0oÎÜiÃFŒP_«Š“E® ¼Ô-=täU1´À?ÎÅ À[{y"È¿ñóôUw š÷hkL£Íž}üM/t Õ¬mcuD³χˆvídÆé·ïÞJ‹¦]½VBy·6­1`3wwchNqüÕüysÌ›9ñ‹ác:XW2JδgOPEú.5Íb„¼¤øÌb �_ÕÄ»Ærqö]»ºŠ„×Ç\+ÔkÕaH÷vVôÙ_ïÈI‡žMÕ�€æ¤äšwœ±þè°]C,9’r68“{—Š­¬ãÐvÜÒýǧZ=þí·ëEå©Õs·‡ˆ à´r/‡ÈÙËU£Üç$‰ÁÁ/‹W½÷šäñN.\Ò£H°uptTýXé‹«L@¢¡ÆÑܬjJVgãä(μ}ãYQ0ÕÔÕ 7;§ôâëx¸keßz¨t2G³B’:înåv‘…ĨèB ¼û cöéåë¬ùÖ)¿Fƒ¦õň¤ùÿéŸNd1'ŽÝ.¡X¯qS7.)*¶ˆ§¡!A�3G´$'·HUZYfĹe£¾Ü%GjncF6–@þÃ?Œ?w×;O7m ²ûî ͬØ(hÆ­3ÇMYðGDbttÞ®ÛÔ‰ûöéäiPéMEÒž«ÌôÒ )çMXv+@ùZ�lÒnì´a}úö¬BŠV#$ó-)VÊáÓ7<D–¯8OGhIj¡Yï©ß.˜7gÁìÉÇíã̧£§‰¤=H#�T.+¯ÙR›çX#•^ l\¯ž1¹-´ê «JŠÜxP¢çìRSÑœÛ7ž[—ºZœ±‡»9¹ýF®òPÁëaEú®î•û#�¨±IO§©Û9˜+®ãÍúXÓ¸˜x9ðM¼Å‘Áוú ¹¡×ï›FªöYò)¡ oPîRˆUJ Ü€ ƒYY0DB¡BpŸÚPsbrs×y·ªî:hzÈú1MLµ lÜ{Ì;úªÊx¸ò×—ömPGWWjÙpð—Y¬…òHÔmœ ÍÅÿY MY7º‰½‰–¾µ[÷¹U?ôšÏ¡Y·V÷0×ë³§ª¤4ûáyƒ|ìMµ´Í=ºLÞY2¶$ÎýøŽ^fzZ&Î>ƒ–øÅ}4ÅÑÇ'z×s³´¼<½dD;'3C }+ç6cV_O­8$ IW45±íW®ZhÎÝ]Ó;¸Yik×õ³1´âÈ_Õ%¬e1>/hvÄ©Ÿ§ökæZÇÈrÂéb€··µÏ”7;%’üîcg¢©kæÐrôÏW“>óîBaNP1à¡R, œâŠÊ—Ð…á›`³>3†ÛKhNðŠ^õ\Íš9SåCBÒž³Æ×S£YWæ¶upvq²ï¥wújœ'_pkíŒíÏdzínüuëO=L ùÔüù%VnȰM—Æj�¤I¯.¾o¿¶Š5ȵs[�DMÇOkm€äQŒoêR§ž»­•}]ßåAe[±$æ·žžnN¾Ë‚r@â<rfŸªÒÔCãšxÔ÷v´wmýcppƾC»Uµ2¨¡\�ÀÙwïå.¹œè¶êÜLdz‹¯–Ë娼k¯fj�@âMôtªçÖ¤]Û>sO&àŒl¬µ¹w©Øª^œÞqäÂán_ ŒÈ�½ò«}Îyè—]Õüç ›½åÔÕ àkgŸ\lÒwú6ῌžùÛ¹àÐ {æZ~Ãxà´VïµÞÀ¦}¦ °|ºq䄵‡/]9öÛþ«I¤ê¤‹@ìâ^nîX~ðJðõ3ÏÜË«U"H¯ãøÁ–Ï<yó¹ [A'wöÆÛÎ[{ºi'ŸÚ¸öt`à•ãb­öÓ&{$lŸ6iÙk!‡–Løî,×iúˆòŽ xw= ²À»ö8|þÙ”·öÑØ¢ÿWClD$þШFvuÛ̾š^“¿l¯Í»tëf/¢Àí½[wlÚ¨Ï/ÿcﮢÈþ�€ß›Ù]º;DD»»°ãìÆî³ÎîîöìÄîöŒóìSìîNErcfÞûý1»âïTVx9nw™™};;ñæÍ÷}ß}Y•iTÝÓ÷;¯PÀ?¯_åγNEUîæs–ôͯ�õÉy#WíÞ»nÔìCbÞ}ŠY€îîâ&Åü •¨5ó†þøHßí19dßž%#—ÞÏWÐáö‚ÆÕ›4i=hÓ³T[ 2/1hÉ„*ÖHsoqÿÿÆ)òΧDÒëu]+ÕoÕ¼ëŠëßV/Å¿öŽ)J¸äL±œ‡?¸óäÝ‘Q•òåÍàîPzÈá@Q¸JykLO¨P´¨o^ûâ’͘‰ï1#©6 ®tç>e·è8ríáS¡¡Ç÷o;®ÿ\â• 3—í>vêÄž9=ÿyÐ¥Ó—‰ �hì™UÓC?}lÓäîC÷ å»´*ă¢Dç•tÛ÷˜ºótèé}³{ \ÿ©x¿©g€ŒöΣ\%ß„ÓG¯;}ëþÝËûæ_uתrÕÂJ@Žõ»·q¿9¯ÏÄ-'ÏþµhÀðÝÚŠ=Û6ú;¸†3úe_9™œ}GßÑc}¨ARë�¥”‚R©”ÃÁˆ”Ù£#‰}thnJ»lx¬Io’ç«;7w٣ǒ]ÇWû´¶cÓ g¾œHwsv³V žºaçš¡…ÏnÙfÎ ›+sdí5îHðŠ`›´ÚÃŒy¾ªÓoc/yô\º{Ó„jŸÖvh2>Õ—žÁ4º·ç× ­_¶Æ¸ÓéTçÉ«cÇ7«?zÅŽm ú„­ï×zôi5�@⥃W¾ ì:oã¶ ê©þÙ¦ëêYzM@bþ5»kùr×=J¶ÐO¹=±üÀÅ›w®R>þà¨ÖwN¸ê—'÷­U²ÉŸ×S´rÒˆí½ê<íÜ;äTè±õ½œþÙ¬ÛF}Ý.YØžÅä(Úë:– sVY±÷¬u‡Žª¨øÊ¾–3¥}PñÎÜVMf=)9~ç¹S[FÞÚ´õÜT¥s4ùÙC_e’› âãã0A4ZN§%DJjVøQÅAV•'íÚ=¥]Õ�g!öc<²Í]$]zÕ5³’÷îžÔº¼¥§Q¸¨ÛoÙ_K›¹“ûËF¯~((‹þ>ºµ'ïÔ`ÄЊ–4êïñSŽ~Ù"œjÕ-©B€LÊ4®ã‚�Y5®n‹yëÖÊ-×G¸Ü6X6°aq/œð1FgæZ ˆ‡JMÞ¥ƒÊæ÷´ã"ßÅb‡¼UÛOܳ{tyË”ïÂÛyçõ°7õ‡ð7aQqØ.w± Nc×ÿ½¢y:‘§ó¹ä‰9Ÿf-˘ dY¡V% P­WÇ>wó–¥T��Tmæ^È~õðî³x‹¼;M_=¦¢êWlh|øõ z7¯_µVóÞë?V½dxùA¹Øµñ’=‹Ú»ÜX4 Cã݆.;ú0šX”½{}¯<÷—ôhѲ뜫®Wî™^Ýîÿ¼„lªMÙ»®OÁˆí£:·nÔê÷qÿ}CÓ{k.OÇYãê(mݼ㠥݉Ì䇵¨4~ãòNÞWlÞ¤ë„a Œ¾øž,jŽ˜ÝÝÿåòžmš÷˜¶.ô•ýÖí]2bý–Í»Ž?ªhúçŽå-ÜRÌfUsJȘ–%s©¢îœ=uînŒ¥W`éÊ3ŒäFVÕ§l^Ý·f~¤Ö¨Ü‹5·9ä@%€IÉá[¶ŒnZÊ“{ÿèÞ³xKÿÊígo[ÞÙ2!qátÊ7é5iû™–·È­�U¡Úµs[[æª^»¸•*ð-;çu­^ÀE™ð>2¬ÜýKT¯èo‰íK×®àaa•·nPѲƒ×Î ®äg÷èâ©37Þ)ÜJU-î–ò›ã}:Î^Ù O× ÿóŽO¯ÅKzå·×=¿|æä¥×ÔůDå’>¦™[ë�ªR™xGô¹„]µbç†ómÖ²¬ƒBó1Z­pÌ[ÊÛBC]£iK‡Õ+àl¢ýð.NálNb’�3ñ=f$Õ†ÁûvÙ1§ò䬾-›¶é:~í©—r2'O·ŽïÕ2xXHXá¡Ö *¤L½8ÎT}cͨàÖ]FïÓTŸ¸yM'�°Wû•ÛçÔ‘öŽîظ݈­q§îXÛË?íùŒöe‘¡Wôôº5«sƒJ5Z Þ¥ š¼~^cG�'m^ÐÒôبà¶=W†µnyp.£%�@ÔÐñ�„�a}’B„cÀ!Œ�Q ”J#Œ¦(Œ1‡y¹}A΀˜éÓy»sôÀ]š®êãŸÎ¬puåŸgLZÎZ=¤Iͺæ®TðyÈ‚ý_œŽŽ-^vÓ½ûŠÅ½ÖhÐoÉâž®×-9­ Vz±í÷j~ζfö¹‹6·ïyšÁY.údh›J+|ígY›Îö.îª6z~ù™>½žë›ô„7Ãýg–œ)gwûÕXè>×Ît†­Ýâò-COFèWGÔ¡“ K/÷u˜mm2ÓÉcU“ôóëžõr™´àÉpv�øtõFÿZ+¼¬fX›Ïñòi8äQª[?puÅ‚Ó&­f¯Ò¤fÝNóV.ø<dþþ”„onëçPdØÉøô§�Í¥Ù}f_Ï3xïì:É[�iÌÑ>\ótÞI±W—í÷Î…ŒíÔ°NÝÖ#—¨¥|{þü �Ì*L»pýÈÜ>­êÖjÜsæü.y4—Codåu1 ß>bÀMÃÅ!ýò%ïÿf×xÅÕk;'õlR«nó~K¦5uøt)T®‘Ó˜#ú-{]zÚÖQeSÛt—)ÕkZÏ â‹Õî;¹W±Äógo‹Í’ážÅ0Ù‹æòô6£ßu:z`î€vu+•*ìeƒ3Ü×r¨tJâõkoص6§}Å"E«t™7§“ýõ•!—sr³"!„‚€"•°cK ¡Š5+é´Z¥RI)‰ˆ¢ I!D¥3‡ÿÎê’3Ì÷W—¢}ã&]ÙÖÚþ× ueŒ[ì®6ù¡ùw65K¿Ïy¹¸A¥ùy×ÝW%ö&-E+”Ç–G+@†a ¿@~Eî ÿÃB)@E„H„Œ1!”Üã÷ ~^ѹ'y „PBÆÒ‰e«m«qðæ´ÒòEJRO=òbQõÂV>ÙÙÒ�@¼:ºxõõ•w?ù³š2iJñÚèbUC*î~¾¸º�@ûwO¿Æ—º¿4&àD¯\öyîÓ ³æÎÆ©SwjƒžŸSÕꧯâŒÑçó6ˆú­ªPÓbÔæ«òk*¯\P¨ùÃ¥>J�Þ Ü|¼MÇóãœàÊ©•nç›\§G1^óòÅ¢Q¯—ª{mWADŸÏÛXl7(¤lE+òîüñ“Ÿ»LîôÏ@{N÷¬W®Gt?Ú߆¤;;èî^¬[ñôër¥ÇôööRiÿ}hfT©óÊüä òbaÕ‚W=ÛÙÒ �@¼:ªXµõ•÷<]oG»Êýï6Þ~nf•¨t¦YXM òf…w´qê¢^übw;�4æhßrí—^z~ucôÌâ�� �IDAT§dç$òt~¥"3ÜWßÛÚÂ6å©*îP÷ÂÍ®t ½4¦HÉF¼5¾Tå­5Üž^:U»&ÜÐÜ¿_ü¤‡úxa� ”"„È›e5òO̵õyH]¹µD¼5©\™¥nóþÝÞÃWIÞmo]¢„+û;»ãôfIÏ2þ)†ù64fWpþàPÏbÖ‡%˜y—k=rú¸F¾)ƒ ¿Ü×r¨4Jºc=}›ïzæê89ŠôÓúÆ>½´sžìì’¯Ú³{÷ÀqÈ\­’$‘ãx•J‰1&„ðE ¢„êt:Œ± Љ‰‰:­–HD«5Î[ óßIOެ|ò¸Ù*կΆL?„+ήl› ókѧáÃu}R&äë|ß�QÐÿû|Á†0:„Ç!"eú4¤w‘Ô¦ ³‰þ9vtvBÑo#µ�ŸÛ€¤È·ïÁéó0”œ“³#|ûŽ@��ðùی삪hî—ž·è¯QUÚü¿Y?2±«ÔÀ»‚ü¹¤è 'UùWÎ]µ8ðò{ÿ²Ää'¡ ›È-g*›2u|*+ª¸j.,éèå»ön™š]81÷ÂeÇ¢¶U®d�(fEýˆú5ò—^(Ù—î"é¸ZÛÍÛ�€p7i�”élVȺ梻_¾ª¾»°ïœ‡e†/oôũм;2jèN³à½²ø’økûˆørkÿñg|~ß×ÑpÝ‚Pšsðû/r¹ÕÀ å7)KOï{ÓdõþúPË´gIÏb˜ìF¸vòl¼s‘†}û× °ÓÝÛ<ºopcí¡ s*~¼+õ¾–C¥yPRªPÚtݶE;ZÍn‘×LýöÉ«XJ‘F÷kÿô] a�L)PÀ  ¬ÓéQE‘HFˆã8@s¿Jç@†ùVšð‡–ý½EÃúõÛÝSnâ¦m2uÎ0L‘/ñÁÐB€0Nžˆ £|1—>Õ/ã8ÌqrÃùO+¹¡$™šŸ·\Y'í½Ûi »û‹BžymMõûÔ#“<~æ•áÈ£Éf£¯\ÕZUÌ]Úü׸иëó[4œÝnËæ~R„‰oüQ«ýÿ™;ÔM5Г1Ñ<ÚСÖ·ª/ß;¾ìW³*“Ø×ßÐÀ&Áµ}”À ¬?p?£„¶é=‹a~yšˆ°häÔ®yÕ… —k5yf7ï—Û¶žOJåôMûZN„œZ/XÝßýd÷"næ¶ö[pET::§S;gø\Bò¨TN§ÓjùR%JBäçD"€(Frr7 &{3¯4|Gèð¬.“Y5Ýü¦éW¦Á^¿|ùûO)NöÜâ/e€ÀðŸáu_—{`LE@ÆòE¥!DS‘ï—‚‡svqDŸÞ¾Ó�¨��HTd$µÍç”"”stu‚£ï�€ù. 9¹:¥®PB)E_»=k ÂÈÃÏqvü¯À@èW2Ë|žˆx…¬›d_º�€D½Kõ¥gfšŒÑØKÓš4§ë²û¯q•RtŒ_îì]»çÙ¢ó†ûe~?ŸæÞªÖõG=«·úèüúî_½Eßoøû.Ÿ™·µuAгO×™u+ ²©ÞÁ®éfFÎüžÅ0¿:^©Ä4&:–�`�àÜ<ÝЧ÷�å·îk9ç^wú?µÇE…E$*íè_­Š N,[Ììëóe[ÈüYÿ!J¨H$‰,g"ÀSJ%B0B¼‚çxžWË€Ò Ã0 úüº”Pòy 4å>IS‚¡á@n&§”RB’¦¡_Ìøß`÷Òer —Oœ—GÕžŸ:ñ´dÙÂ)Σ|¾r¥m>ü{ê†Ü¥O{ãĹOö¥ÊäM+®¾~ìô{³ÂÅüŒ¿c56uvÄñ>„ý´ønÎ&_^îã…×÷³üVv/]6—péDhŠ/½\Š/=3Ód„¼Ýѻͬ¸à{¾h#�õÕ-z˜¹o­q·ÐO'†¶q?hå¡L^·ˆÏoÞI°÷óÓ\“<å‹9 ¯ž‡gÐŽ”é=‹a~yª|~ðìÒ•÷òéKûðÞSðôõVþûZކM<}Ü„£ãfŸ2¯×»•wŽnTÄòðPòíŒ1–à 0B¼…™¹ ÓÅÆÆ Z "Â� ‚(fù)˜a†aÒG©ášŸÊ‘rˆR�ùèÇ2@ˆRŠ"„È£(r'·.ü×N$|K‡jýï5Þ~fz‹bÝúTX3lH·|BÀè“çÜôj?­¡=éþ¢ß‚¦Å÷9x|dQó Þ= îœß£_®)­=^n³,¬ð°Þ•Í�t�@¢n8qÆO÷ôø’É‹^Rßú»¬¤KU¹‘·y· }F›ô ²1Ó¾}š¹qiÿȤÞïsÕ½Ô±“jB7w«¨ðõ'IÖ\'+Šwï[qõÐÁ]ó =£÷Oš}Ó«ýô†öˆ„o®Üïn“ggTµHgšô—JcŽö-×áHé%¡«Û^Y4~n¸¨‰í›Û7ß�� '?Súnûä…÷ ö™RL}ÿæ-��ÀÖ^ù½mŒ­®+=^3iÍ‡Š“;xGݹ�€öÞ–é®E@õªÎn5ºð¸6…,?ÝØ:vs˜kÓj2jYIwÏb˜ì† h×­òŸ&÷˜í<¶¦õýU#6|*>¦C1H¾u_Ë©Hô‹Û^¾¸º?dù–î}7Ïká”ÓW‡9 �`ŒyŒù·oßjÔj@H§Óét:JBˆR`†a£¢€Ïù ô= ôA�$߀D)Ã!Hn`øÏ¹ (MÊ|Àùv Ù•0xÈ® >*½Ê‡¬žPIß1”&QUtèÎÍB¿Q“[mN°Ì[màÖyà )�» *uéĨV›c¥½oé†ÓLè^Ü$w5..mëlzstÔòc­çØÜÔ-·gÍü&àÇ…XV­¶{=?lÆÕ^õÏP7—*¥Ñöçà|»¯Ý“0hП]êTz•^»fb% �’rÃHsš¯ç%W®¼Ð½yÔ½Ò&Ãø|CÎ]PœÜ½x->1bjíÒS 1i¸öåîÖ–i-- i®_¼¥ý¨\íˆáìØyïË¥AéïbYsÆžU¦#g oº0J«rð+×zéþ‰Õ3^géíY “í`Ÿ.!{ÕÆ-ê\y¢hPs莹äã¾}_Ë¡ÔÿŒ¬Üý”O`™ á6t¬îõkœk ñ!D"” !�„1Âí;°W«Ñê(ˆ:A tZmγºä Ã0 £W¼rEŒ1ú< "Ær°�Æ`–C#$èÇEÔ§%�J ¥ú( þèÚ3S"dü IzM??]bN¡ÝÓfI׸ê÷2ê~ Ã0 c\Ú³{÷ðIc‰Dˆ$%õçä1æyžß¹c§V§E€$"‰‚H DQdÍ Ã0Œ‘ûâÚÞq€Â@å´½†€¨~J9Ÿ!óë‘=^õ¯ä›×ÊN%¼8umÂTe¡ŽÉ0 Ã0ÆŽWðjµ†R‚1Çó<‡1%D§Ó ¢ÈoÚ´™$¥qún霆aæ;£�rzç¤ü„DÀ0"I½ä6$† OB@�#B)ÆÈÔÍüŠÔao÷ýyûöË„wôu­3½ùÄ–ÆÖ'Ÿa†a~ê„D ú4†’N’¨Hx^©P*x•‰ ¥” ÏÔGi²:Ã0 cô’â0Â(2zèOfòi #L?wgç¹_‘EÕJ‡nVÊêR0 Ã0̯OE3 €â8Žˆb‚F£Óéx­ ð§O €"XðÃ0 cl!{¡!—�$å1„ä ’¤êX ïl%eg†a†1.æD¤ZZ«Ñp§T(©Dˆ jÔ¼™™™‚çå —~HjBä‘©³ºØ Ã0 ó 0Ærˆ�BY@"„’NiÈpÂÖ$Î0 Ã0L&hu:N§Õj5ZQ¡<Ï)ML\\œy¥BaHÁŒ0F„È÷^¬þÄ0 Ã;¹‹ÁgIè£ >q/Jöw¹á€5†3 Ã0 “ƒEGGARð 3SS"©ÔZ(Š’N9Žçx^Ðéƒ\}ÂQʲ1 Ã0¿0}úÃo¹Ã „`„E0 Ã0 “ÃI@ ILH ”š˜˜XZZ(U*s€ç㔦�ð¹6…(PÖ}“a†1.ˆ¢òiŠbÀaH’$„yžãB”"Q„�aÌcžPJˆD(Å€�¡�rÇ:„�!ŽÍ1 Ã0 “ƒ© !V&Ï!�ŽÇXÉq‹DâåI’ä-I™ŸY3Ã0 cŒ(PB%äžç�@ÈR ¢„y}ŒÖwJ òŸMMM$‰ˆ¢(ñÃ0 Ã0 “3J-­¬8%Ç!$ s‡1`Œ(á%‰�PJ %@¿øÇ0 Ã0F!„9ùD†1ÆÇ"‰’„âxžÃ€J„ ˆÂÈÐ#„æx &a™z†a†ÉÁLLLx•ó`”‚D)"’D /$¥ƒþ<ÐaÑ Ã0ŒQAúvù?Ìas)2é „ÂBˆ‚ô ?¿�B¢ “0æ²æc0 Ã0 Ã…J)R ”¢ï·I pQ ¼ ÓQ�IA* úÚ‹&`†aŒ ÆX¡@nÏ% K#Œ (¥„HòüEÐB  DßAa†a&§% 0 Á ¯É•* ˜W«Õ�†.¬e€a†1V„Œ1 �€ „Œ1â°Üˆ�ò˜= Ð7(�ª+ €1–æ(•²ú1 Ã0 ÃdN¡€`„„°<”ÉÎá„ä_(é1Ã0 ÃB ¥€ã0ŒÆrK`Q€@Ÿ¯ÉÏ©D)Õ7ë› BcJ;Õ1 Ã0 ““aŒ%B(•ëNXîQ€<Æ�Õ7°°†aƨQJA€€RÀ+x @ˆ„(â8�(PJ¨+ôA�cC/$'ÞA!Är0 Ã0 “si4NÉ� „ ”9n€—›0”H6"ëµÉ0 Ã¥REAR(”ò8ˆ„PI’yÔƒ¤3˜�ÅÖ7|SJ(`ŒägcÄõ2 Ã0 ““a§à‰êDŽã8#Ä+”PÞû!„)¥òÍ0„m2 Ã0Œ‘@‰¨8•µ••R¥LLTÇÇÇ>jè=‡Ð-�rvCù1¥%„²N Ã0 ÃäpZ­#¬R©8Ž“DQ’$$ ‰H<B�¨¡ÓAv›…a†1*jµZ©PJ’ôáà ÂsØ0h�ÀH?@ˆêOh†@„€B�”50 Ã0 “£aŒ( *J¢(Q‰r˜Ã*éÇ‘}»�bwW†aãÄó< }„0Çqæôg-ùËðW”ôôsO�#ýÀŠ,jŽa†a˜N©T"Bãââ>}ú$:Ïó<Ç!ÄËqa„äz•˜IY4Ã0 cT!‚(bŒJ‡1¡D”$¥RØÐÊõý�!äÓ˜¾BROB(‘€°h†a†ar2LA$J(‘ˆ$HT!D$¢Nâå) ù Áð”50 Ã0ÆsœÊÄ!D(!„ð<ob¢a˜_3ÂPRÓ�Bˆɲï =Ç0 Ã0LާÓh•J¥……¥( `Z# "ˆ7Ô®äªÔçÖ9gÃ0 à "IPÌɽ 0¡T«ÕèA¤€1Pš¢'@’F@Ô7ð˜RÀRåäIÛñã'äIKNÖ‹!³ lð †a†a²JPPPš¯# ÂÂ@$D@"‚F£ÑÔÄDît@1–DD�TÎV€1k&`†aŒÂrÎ]J)òùÂR�¤o# @)MqYþÅ%: E@(Éd3�T«^ ÒjH}ñŸ^s�k&ø ôðÁÿ¬.GJÆY*Æø±-‡É–~­ û×*íל>y²ZÚ„ @RB€RJ ¡”Ê,)JÒ³gϦ· T(ˆ$ÆÅÆiµZaNK)(x@åžç %Æë³F³: Ã0 cD�D% 8D"q ^’DŽçÌÌÌ€Ò„„DÊ™©ibb"„@(È1F@%‰€>koæ°ŽxY…åÊ7ÎR1Æm9L¶ôkmØ¿Vi3‹Ê·Pô7R(¥Tßd@)ÅÞ¨ˆ—$QÐ #sSSL"‰¢$¼¹¹Æc,g~Æ*}[ý‰a†a~<B Äñ $‰çys³¸øxŒ±ÊÄÄÜÌŒãy^¡PkÔ:CÂ{Õ! „�‡A’�#„1‡€ÌVX¾ž,’"GŒ‰q–Š1~lËa²¥_kÃþµJûsQt:Q’„‰b›™™!�¥BÉ›˜˜ †¬Oò@Ò¶ú†aãbmm0æy^”DF� …ÂÅÕE§ÓétºèOŸ0Æ”PB$ úQ шˆ0–Úb„E!)ÓïÌΉY…åÊ7ÎR1Æm9L¶ôkmØ¿Vi8 `ci-I!E‘‚¨uÏqI9ô¹ å,P Ã0 cT´Z-Çó+� ”j5Bˆ$I„ŽÃæå¡BIübŒ!Jôí4éWæ°sbV‘vŒq–Š1~lËa²%cÞ°Åèg×ߨ tW^1æÒf J©(‰P‰”r�!cÌaŒ‘Üä•<ËW“;‘ÈsKz5(ŸÇ3—S®üEkuštôm¦B}G ;;xÛ:ùtþK >:´œ¯§‡“›ƒ»_Þ’uZ¹ø>ó÷‹¾7éýÉ ur×XðÈPÝËCc[Tðõðò,R·ÇÊk1�hü½£ZWó÷öró/ßpÄîǺL- ��ȇëÛ&tkT*0_™ W��áÕ‘©-+ººûæ«Úeö™÷i}#ÂËýƒJû¶^•ö^Bc®ÍoZ¨èèKBÆ ïÿß½r ¯‹g`é–“¾R/Šaæ;JHHHHHˆ‹S«Õ„BˆZ­‰Ójµ!žçyNÁóâR¤à•ûÓ!„8ŒåA|(!D’2_Q ¿ éÅŠ†y ÕeuA¾ ÿ}ÝZ#ž×§Wò¬:÷Ž˜Õ¥br"¶å|Wße×4Áð8»?šï½a§øfÿ#ñéšÎµGþý‰$½’­vÃÔÕ•^½{÷êÝç›jVD":­NÔ T’@"@�Q@€PL)‘ó!B)¥r‚'9UÁWúzcÏàÉ[/F¨ò+]ÐMzz=œXfõè$ö]øG-8®T!Ð>þÞß«F5j»øžøóK"„Ÿ]Ù¿AÍîŸ&$}‰Ò“e½úrtøÎýkk›Æ.ì6òŸX bXèÙè²C7?}t~mõ¶!C6…‘¯. �@zµ«Oí®»Ä*CŽ„QL@^¬íýû·áû<:¿¡IÂòžǦX–úùÑ9=êÔ|0L›V©É‡+Æ4®ÖaÁOÉÊfhÔž×A—íWžÝÜþ»åÞÞƒ·gI+Ã09‡ÊÄD¡à ¥’$!„ I•JÇó$IZV«ÓQ�Jå{ ét&âÿO×Dj\?±[›Ù[™˜§ø1Ï7ä_ɾxÓªåâ²¼„?ᇄ­iäfQdÔmFÓ¬l˜»þª×Dÿ ö,׺K«2®8« o¼?º³#ŠÛ[[›ZØX:zû—kÚwÉÙH)ËKÅ~²×twvu kSK¯Bå›ô›sä©ú›–ð]víÄ}í½*M»-èŸâœtüü‘?$l]Ó\fÖ¦VŽN¾%ªu˜¸ëaBfgÿò›ýï?FyÿΟNoà Á‚ ¢0xÈÐÌ×oxžSÈã „! ’$éN«Õ¿ªP(xžçyÇéƒ ¾Zƒn]¸OEì=¶÷ÀÁc·îž›d�@înÙ¨lWw_ÿJíÆî¦�áò¨â¶îí6GSÐTÐÝÖ£ËîD€øËKz5¯P4Ÿ«[.÷€  çÂÏéY½¨¿‹«wžr½6½$@ãïlÙ´RÑ\ž~ù«w›{&òkW£Š²ýWoݶçÜéYµ,AsçÈÑ?? �ż³^u`t …á%såß;Nõ;6ÈkcæX¤}ŸzøÈžP5(ü»Î_Ò¯vq_ïÀÚ=Û• Ïž¼‘¾¶(� ;†ÓvãÆIm+ð°3Wb�Ÿ=xdRª~ÍÜÖæNÅ›ÖÊ—ðêEtе…¢^¾÷ë·cS/>Íbk^¿âê/Ø7·¦Y² –i@zuÿ¡º`íFùmÍìò7kP”¼zÁš †ù¡!”P�ÀsÇ+xžçµ­ ÓJ8ŽS*”<Ç#Œè{Èí„Jˆœ³Pn>à8.ócfÕ‚tY4\~ëÞ“‡÷ìî‘Ç´üÔówŸ<¼÷èÔÈR¼YÑà‘TwEY]ÀŸ@wcõâP¥uä–?D‘t§’´Z1Ù½#°«Ð}l·Ò¶Æ÷• ’û‰«·äÑó‡®^ÛßÿÎÔ­?úOá 󢉉*L¿øêÙí«¯RQ½µSÕ:Ó¯Ä}Ã"¾Ç®MtZ&;zä ã祋Vç|ôþ«Ç7ÎïžÚ˜ìîÒpØ‘ØLÎüÅ7ûßã9ü{I^Q™0qblLŒü86&vò”©™¬á`@‚N´ZA+P‰ �ˆÇœJ©Â …B¥RÊ«P­V—ñBù¼ùUH¼¹¸SéëN>‰áÍÍx� ‘û7íòo¤M‰ L_žXÔ£ÍÈ“±éµ9ÐøûG÷Ÿ½÷ѺHõêå}=4¡“Zµ™±ÿŽÎ«BíªÝ\Päþ!ͬ=›«FƒR–ÏMî<`ýëÌ]Q¢EãUóíÆm]Ì!ÙÕ8²ðõw‰8µÿ\˜ZÔÅGƉÊÄð°äWñôý¿'îØU®ÀmQ�äÕÞÍ'5ï··/âæáXg@Èíx  (R½*wpæô¿ŸÆ¼;½dóóŠ­x¦ˆð0)Ùmúà†lÒ ûÀîGïZÁÃ$ÅK³�|¾jU]Ï-»ó~ôÇk‹W_+ЦIÁ´›†a¾JˆçxŒ1¥T%B¨™¹™B¡�¹-�ä¦Ä+x��ôù ÿ+¥”ÂÐ׃æŒ2±ssswwsw´P 7ww77gk%$lkí^|ÒMhÔ_ë put´tð-Öjì´á­Ëäó´uÌ]¨Éô“úgäý¿óÛW+âîäæUºÝ¬³¾vwÀ¨Ä_¹)¢Ö”…ÍMÿ^±ãEÒ™Tû|ÿØV%r»Ú8zço¹ú‘�¹¡…Ì-lL:îÑ’—Kê8ÖZöš��ýp~QÇ*œì]Ü‹4¼ý‘�’¯7{¯€Zƒ¶=N3ô.›C¦6ŽŽ.¹üË´ýGMt=ôF¢ô`uç}Ü­m]<,{,öÏäÖ|œís—k>ñÈ+}¿C}qY·ê…\œ}+ ;‘öf&¼Ø=¤AgK{/ÿ&ËH@ޘش´‡££µk¾ÊS/³NŒ9ÒÂÎÞÁ9—©ß,ûkqÐË9C—?”��Ô· iRÂÏÃέPЀ´�@c..j[.Ÿƒ­£ƒO™AG� ½][z¾¥[•âÞn.–6.E›Ž>.�Ш½} ûxÚÚº¸m:æïý1Cz0³¢ƒ©…u½Uá$éø @ão®P³ ·­}®|5ú®¼._ÈhþÚ DþÜvÖ.ù+t[}#! ÖÛ/Biiïèàäš»PP¯QíòG] }"D›Ô l «ƒ£µKáþÿhÒ9CÊo@ód׈¦E¼\lœÊw˜ŸÔZz{jzpe_gg[×üÕg^Ò]ZްjõêW/_%åÕë׫V¯É̼ZFÁóÈ0¢!DA$Iâ5$B$AGC¨¾eí+ åüº­^;tBÈ?û Ø¿p´oíá çõ.³{ÕÁHäÚnù‘?ƒ,"·w.Õçïm!ÿŒ.Ÿ+£Ey4š2¢(³¯ãÆ'‚Y¥ÙG6uöä��HØŠ5‡"‘GÇÙ«Æ•T<ÏݪæÌsûŽ¿ïÐÑ9ýkáÜœöMV}z~çîËx0-Q§–—™ÕôÃ)K ^2,bø€2y>P/úŠš¨ ƒÆ\žÕyüÃúóÿ ²ÊD³†øèî“bý—,h`{vz‡]gçÿw|i»Z#‡n©1nxãÍŸÂã}ºm¬éñêÁfeûޝs¸Ç¸öGG|°ûmEc?Å×gb†ù¢¥˜"b XE„0`„Ü™Ž"QŒ±Ü,€äÞ'çÜá8LäÛ ™|ÛÌOù“é öù£PèÉ$þåÍ[–ÁovË{aZ».ócmÝ7;/½6£]· k^_¿ZߥÍ2n؆ËÛî/íޮ˄BWÔ4ÏÊ“Šþã¤ñ‡È«ö£ßBÖµ}èQk݆[]Ææ´—¦´îúWáé›ÎÕö$ïbL½0àËN9w »'FX©¤¯ +Lz½±[«ù‰=–Þ˜W}vn÷~mƸžžUÁ$þåÍ[m÷_íê¥{¶kD§¾ƒ ”ÛÓ)å™4ÝRe rS›¼%†]Z¿þ_wÛ<&bØÕÓÏÇŸü§®UB"çIî/î¼ÎiäúІnÆ“�� �IDAT‘GÆuëÚZuøäÐ@.|k÷–sâºüy,¤ˆeB¤ÖÙTz™ÆfVùܬ»m&ØÒ]öVòÀÂՅëÛü}¿‡~ÿ&ÞŽËžë7{o9ßB¾Ã›ìÈåX§m]ëV‡½×1tB»~g«¬<´¼´úŸámÿèµ¼èß¿ã‘SžVÛqûŸâ¦_E››Rú1½]›D?¾zÏsÐéÍ,âî¬û£}¯1Åo¬ldlÊöÛxjœ§EüÅ9Ûö›Yõ未�€ó:~bT!q¼ i’ŽŸm6òþo‹¬*ÎÝ\Ñ¿{‹AŽç—ÿf/†Ýºª©¹úʰ"4ìÄäNƒz.,svX~ã¸ÆÈB_nØÉ"ºŽ¬Þ÷ÐÔ¿Ÿ'Š åší¾Ö;�Å 6Ê´ÂåhÊoV:ºMÏ¿‹ÏÞu¡ºÕóÍC{¶êfunW'oúxaûök솮<Ó8Ÿ*: ¼QÚ‡tUÒ9’¦SÚì¡k—.]:w&„�¥”êÓ �¥äë©#,è '~Æ�<ÆòlüÇ I¹Àóc>+PáYkø–šýîÙ¸höüÇÆýî]údí7„r>Ì]üîÜ‘ÇáQ$—<BÆÃTKï_¿ÑP.O±®†N .QéuHëB!†¾Œ’À9Ý[×Tм{æƒÂÌέ`íæ- Îg,w¹‘M‰î«Žu�ïN¯^w‹_k�@?]œÖ¦ën¦TsÈ\ìƒN«CN¾.*°¨Ú­EÁµ;Ï=—Š“eÝghúºð{îø+!ƒ:tíå{|kW¯ÑT@Þl4øAÓ½‡#¶èÚ/xïÑA…XSÃ0?L±arŠ$Ÿàä‡E•ðI6-üŸ¡e‚(%÷Ô2*a†¯ÿG¢Då¨ A”—/I€A”DBAeãhokïX#¸žÏ¦;î|œmP6µ=7ݾûQü´góY÷Ç»uà b¿žÕ–=rUSµœÄ1¦¢$É+?%òrûú“Võv”U!®q#ß%×0³‚‰öLÈÆˆ é‡Ú•°�g�ñDÂÇó@%‘PJ‰( Owm<åÒùØU|y€¦F+ÞgÝ©1ejˆ„‚‰‹³ƒ#vèܭּή©Û;«2SªìA)‰ÛÙÉ}?–tZÉÔ³L³Y«¤âIByW;¥ ˆ×­¿]hÀå.¥Ý1äj?}àá"ó¶\ì;Þu׺ã.Ž®‘—�G�ñÉú46³ ¦¦ ·oÝ‹hèÇË@””fBøƒ¯âót÷±1;®Þì¾å| ‘H¨DQ2ííá\ämÂý ÛÞÍYÛÛ iÿÖKªÿsúm÷šfæüû‡7žD+íâkD¥tvmI(EnNöfN•{w¬¸hjè]m½R °Ëí�`[)¸QÀª"… Ö„ ˜çx�Ñpü>\»Ÿ6[?¶~€ €Ëˆ íö79ð¾N;‰"¥³“ƒ#ïдg“…µ/\y/øÛgA̲I½a‘Pñæä ¹¦SA£Ãjô_=­¡5 #™9y:ÛÙ`;�1íƒp©)¾Ù„“ë¶DM›Üº°5€Gß©=öWÞ¸ãQÛ>ñ[ÖÞ(<àJ÷ nÀÙȳÅi҃䳤 JJ»´ ¥4½MK¡”ò¼!)I"‚ Â×b¾hld”©{ÉÆC¦t.®�)ìñ­«§+Fâ³wã)з1ÂÅÍ3³0C@Þ…EdôÅ`;gGIonßN ~ä]1`§º6Ø»ëÀÞ]öîXÔÚ'£ dÒxÍ‹¨°'.Ú<­C){# +¥‘G–¬âÑà·" �Ÿ¯ïÝc›û„Ý6öÊl ¹¸;k^¿Š"��T£ÑJeBžÙÇ¿A³¼*PØ—èÔ§‰ÝÕ3×~LÐ ýp|ï9Ç:ŠZ lÐb`›|ÏΞÇ’0 ó“ ”tÕ�Òÿ$k1HÕ.ÐŒ¶´¶µZ �€,¬-@£Ñ€þNwnUw7'7§Ü=$Ä|ŒþEn°H·m¹æTÿ·R*�>_ãÆùÞíÛr"h\xXœƒW.³Ì,âmxxz{ê+*/ogõÛ·ŸR®�lko-$&æ¸ú$2¯9õäÙ³×î>qõàÜ6R­Q)",ÂÌËÛQ®_!ï\Ö‘áïˆöæ-xx{ðÉ&Lk3S”¾efùgëä/Þdè–»ñ” ø}ÅÊF‰K›/X³ßòË‘ ‘ù~ÄÈð÷`ç`ƒâ"ÞÆÆîïîïäâæäâYeöC ®í®én¹·cÉ‚:Ï;ù6Óû¤ÊÆÎL“@Ô¶ oT¶·§wžz ïJ„dPc%Qao%Ÿ\Jù)ïéãA߆QÇŶö6H¨fkš¸|=7ž<ñêãW/ŸY;¬ªsª˜‹L„iÜÛð8o/}œ—Ë;zA¤ð×oÍ<½ð7-I¢€áóL~Ìó<‡Eñ‹°Ê¯Þú®.¨÷ÛFMž¼^v\ÌÓ[wuHX¤ ¥»c§:s/îßÚ³ö³bva—®Åa÷àöA6 eÙ2NÜýû‹»¶} ÜHç{C¶ÕÛ5pþgûñAAµvqƒ¤ò¬UMÚU™wõØ‘Ã"Jçwæßdz·ÌwY/?Ñ%D¿{ziÿò‰³:t[3¸¤ €~:2kêƒê þ®ë,iµ�â”J>ñòâ~KÚÍRÕ.Í/BQ¨A}¯ÕkfüUeR8þç–Ç…:Ôòàýò˜/;°!´zÏbæaG·{çÝÐOE£NL²Ó¶Ïì>ÅÓ¬< ÷× žò0húäßÐCYûù9>?²õX“•í£Ïm>üÈ.0Ÿƒ¶È0 “­ÑT1! 4yK¥rS5ü=óob¤u JiÊÐÝÏÏ?wG  ”PŠåÕE(ANÊ¢ÏîãË}±@c‘f–&�®oß~Oþ,Ø+�âÅø—›¨]ßÑÑ,êÅ«xZÌÚ°ÌñT§ÕýÝ’¤Õ‚]Üœè©g¯È€æåów¦.ÎÖ@ß' Q•ï±|Q„ôJ•]PJ[8y{çRžÊÿ7ü•�vqsN¼ú,R¢î€F¿xã”× !{'zú¹a­@:›( ´š¸½Å»FµïßÍ"ßé1Er×¹¦þ¯ŽLîÚµý¤<×çVSA6“Ý·œo‘rs ïn8S gU7lîà`a×bѵ%µL“Ï�®U,«Ò;âÜüž; õ¸R>][¿Lýÿå‡@¥°-Cð\´ocoåóÅõ+ÿC)¥s /ãóVNRdïê½yþRK½L�@xõì5rqsDɸ>¿MÿJSoØ”¥­‡——'É"):�¤wNyжpq±ˆzþ2ž³F�Ò«g/©sag„”N‰·ž¿—¨›|é‘î!=’mÙy7DÉ’1É÷I0 Š( ÃÖúàEQ¢4E³œ<&b²…´iyO^¿{ñÀ[»«ßxÀ¸>xæìRÛŽ\°ÿÒù·–Þ•{š8&ÈT±lü§1K?øÊÄÊ>wÑüE<R‡ »z³¶.µ›4oÏ…³Çšº.™€<Z/Ú%M›´üÐÅs'î(í½ “4”¿â]!ñƬÖÇy*ßäσ½êçµ@� Þ ½üáõ›Öù7Ëq¹º¼0ÞçÍ c*ÄQH»™�”Å„Ìü0p|ÿwàV¦ÍÒ¥ópêNy6fìïUgE –^%Ï\2 'Ï_^¿pѱ‰š7KkYBăËç¯ú|$ð-‰ ”e‡­˜8f̰šÃMÜ ×µjD%“o^# Ã0ßJøü(P$wu}OJ0Â_Œ$è¯ý¾é-½JA Ÿ‡•ëhÔð4é’=� Ø§~³óŽ\0¾YAûúƒ*0ÐC™…"µ¤Â§ »´}ïkÿ>»6uÑ_yÒȽ~›¹íà»&Z5²i5uxˆã Ú¹U1‘Z§‚^¹Ìý³ÿJ£VÎÑ8ÿ�y (Wƒ6•çM9§äìv~êÐù“Z5ß\Á4ÅzƒT3(U¶‘l ¤ñ²þu>°YÛk [Upb·÷O˜{ѯÍèb<æê7/=oÖ°éÓÛ³ÞE«òåKk3ãÃožÿäèmåÏßUq:6NŒºs%Ì*¯Ÿ½¥o~óM±±: Ƶ-~Ù|Ëù”€6æý»­öã«ëGCfÌ;•«ÿÖî~ \«FæÁSÇT²ë]ÅÛ\¦q)’×6þÑåÇ _Ó\¹mtOcµ`›Î®rNÚn !”"*hÕLåóÎÃÇ-âøþŸyøi#‚Núã$X×jWob¯I*ÍïSŒ»µzü&uí5íMLv€M}¬È©Ro؆ö€”[ûk,ƒ0âS~³eÛµpl:}ô¯!U­^l±òy™Á}°Â³I ¿µó/ñÛ(¿Ebx¼u¡´é`ff*½ºs󽶆£2íÒþÊÔjuè¿§“§]«ÔP+0ü/£ÏËc,%Mòþ‰>BÁB“’HøJHE‘à¹[ƒÓø²l3}›é_¾l[²ÏŠ#}¾xÕ¥ýÞ°ö)^1óo>icóIÉ_R ž¶+xZ:1o¶îE3ýce²ÇÆ�9´Ýü¦mÒS¾Ø¨¯F}1¢ÜÔËQ©­h¼ônã �¦­fn5;Å\Ø¡|¿¥Çû¥\”O§Ý÷;%óë{üE_Ã3³ªÓBŸ¦˜AYgɃ:_ù,€¬ uZ°¯Ó‚T%g†ùQ¾L/qD€Pò©œ¤çÛÞÕh+4ÙOê×!å_¿xŒ}:®ZŸ0bÂàÚ3"yÛ< 'íúÓÃÕ˜bÂh²ßŸ©ÏmÿëS¹ÁËyº ëÚ¾{½E=wî{ݦû„ 'žÑ©ÚÐXÎ._—ÕGFUî7¶^׉JÌVxÔœ´syÃ2‘G›ÅëcGŒù£Ú¼ Ò-ælWÞœùb]¥.CÚ¥ÊF’¯ôþ |¾ßCVª‡MêPqB¬ymèW€�ŸŽ+7$7²î¢w:3:w­n›z3s7»³}xÿM¢´œOÙà©}Ê §s'v^r3",=Š6œ:»–E6\ÃÙ~ËùÈÄÊVqal¥€q¼‰sžÂ•š/=Ò£Ž¯�‹Ê6Ξ8fNÛ*}?s·¢¿¯Ü5¸h؉¹=§ŸyKÍ\ Ô;£©3VÙ§³k§¹Û"·æãF_<ª†W-6³vô®ë¤B”/Üul»ÐaK¯�ÇRƒ6ïÖW•) ›ZÓ¶N?|üoÅÞRç" ÆnžÐÀ%-0õq5'Kï ©ÖÌk,ƒ0¤<h¯l?jýB:rZ‹r´¶ùköØ0=8ª,4pý qÄÔ^5¦~$V>-ç™öÒ\êtn½yDïQo¬¨o–ívC[;ûr*J䆚"…! „¹YäìÙ³ÿÇò6Œ)­Žäß!öý>Ã0 Ãü'%«WÃÂ2ŽC€F%û !Ç6à–ãí ¹  SJä›hßv4lhÈf`Èhì�œ8q²d©’IOÓ|ä'§0ÌÞ0æ>|àçç—ÕIÁ8KÅ?¶å0ÙÒ¯µaÿZ¥ýª{wïš äæ4F: ”b„Ξ=ôåüíÙ½û÷ÎÝHRïM}^';óðÉPR$§žó“>"Ã0 Ãüß>_âËù õ£|Ï·È.Š¿˜¤¾FÅ8KÅ?¶å0ÙÒ¯µaÿZ¥ý)$Jå!¢ Ù}™!=�F�0¢`H‰„)ôa†Éñ0”t£!Œ�QI¤Àažã8Œ�Q !ú°F„œ¤@”3ôÝGœé;ü¬F‘%Œ³©q–Š1~lËa²¥_kÃþµJûs`Œ ‘äX}òB`Œ1/w:�CÌ%¥Tÿûk) †a&Ë.ò1Ç!Œ@ß{%ûûçŽr;¹á©>CAæû'ê’-šu:ø98ŽA§ÓeuAR0ÎR1Æm9L¶ôkmØ¿ViŠ€ã991'BcŽç1Bæ9ŽC‘dcz À±6†aÆè$c$þC)å9^îd Q%?Æ5\œ'µP�Ã;¥ßpéz.EîŸ š ÒÚ þoáY]„4g©ãǶ&[úµ6ì_«´puuûï Á€1B’$J„"�Œ Bˆ"J%‘×W¹ÐçA%„QvR’a†ùåQJ9”Ô€’^Lš@~ 'ë:—"r@?Ï7œãÜÝÝK—«ÈRþ|Î)S¾RV—âKÆY*Æø±-‡É–~­ û×*mÆÎ9õ]–ƒ�cN±¾M�!D)B<Ïë£äD ‚”ž†aÆx$ C`x „¹ÓBa¬Ïb¨ïJGõc(²vo†a†a˜”(N‹Ïaœn‰C˜òŸ'¹’•uÅd†a˜Ì¡$zH“†;Hêað9ÊàËB,Ã0 Ã0 �Aa„HT¢”J�˜ã8ä.!Œ8Œ%òX¬Å0 Ãa„<`�`Œ$’ÆÙ*yÎBùlF)•Ó ýS`1 Ã0 ÃädˆJ¥@„@`Š�8„>©'Fs(L¨D(«?1 Ã0Æ D)è¯þa¬ï0)p yø�•£†a†a˜”(%±q1„‚|Ï…ÇÀópcŒâ0æ°~4)*I’(Bhòa¥~&¶»[Iÿ|—Ýþã’´WfÕ÷ËSñ#XƒÃ0Lv@”r–3&K¾›¢£n:*!D$‰RÊš†a†Éá5Z• Ø;(›4 êÚµåoMê*IT@ÑW³ˆD%��qXï+K.OªVÐÝÕÍÎÉÝÙ+ °RÓnSvÞúD¾­hÒ£®µ‹í¾-Z_›#ÑOï‡Å½øè­ømKŠ;9¹^ÅRÕ¦\Ó7/PÍ«O>ľ¾÷,æËô™xszWW§ªsïI�Âå)A…Ü]Ýìœ<œ½òªÚªÿÂS¯Ów“¼;=/¸r!7w_ÿÊg‰Ô—!~w[W[GùÇ»Ã^uê9i̵ùM })©„¼ÿwF»Jy<}r—m;ùDDò“ø`cû¢ùºJ§Òû“êä®±à‘”üUáåþA¥}[¯ú¢-½ÞÕ#нäðó)ZhÄk“Kº&•ÙÕÖѳÔä"С‹»Ö-›Û3—«¹zƒ¶ÞKã£0 Ãü�ß~‰ô!†¼ˆeâa†a&§Ëgà þÇ:uÒБÃÌÌ”œlm-EQàõšú–À0ôôW› h⇈‰Ä6•ÒûG7/íšþÀÁ‹k÷ͬí˜éê‰yrýγH‡¤ËX>pÀÞUÃ, ›~Óg¤Úð{W„»W7\D#ëÆþãÝSëS,7÷MKJ÷߇H$¶ª”pÕ…ß½vfý¤ÐÐÈg&•N]R±c@÷õܰ·ZæŠ:4¦m÷Án'×¶uÅ4>.^Ugùí¥¿™� eÊÙ>\Ù4òü½7?ÅX|^ÖöA=׈}6ü[—?6¾c?<Nlêè‰iìýÝ gÌØx12žÔH£¸BøÙµ3¦.9ô8!Áëó«êçG—LŸ¹âä›mÑ/fP_›×iâ%W|ñ:_xÈɇý$ � ½³°Y§sõêû󀬽Jµ4>·µúéÞ1ÝF-RmG°«w3 ósd"¹@Š‘>· `Œô—a†a˜œný–'=}úxmÈÚÇOž<ö†H£R©0ÆÇq¼<�Pª¯|ÑÌ݃ç týsí¶]‡®]Ý?²¬¥ðxËð¹ç5�@ãïlÙ´RÑ\ž~ù«w›+ßN§Ñçö¨RÈÏÙÝ×·h!G>é«iºã¿çu³uôjµ5†DléZ½AP‹y×E®-iU«bÁ�Ww¯\…kuš*ßÿֆΨY2ÐÃÝÓÙ»Hù6ÓŽ„%52HÏ7prtu(=éŠ œ›Ü¨fÝ >{â�tÏLj_¾€¯³gþM†o¼OhÌé)-j–(TÀÓ#—‹_©Z}×^ûZÅ‘/Ðåϵ;ö½°ª¥+ž9|;­ÎÂͳ J‡v…Ìmò5éÓÚ-tÏñ€Ä|Š3·s0W©T*•J©ø²)FóúWÁ¾¹5Í’®·ÉÛÛO;ìVÖÛ«d§a=/nÚÿR�)âEB©I{—¶qNóÚż³^u`t‰ä×ý(êå{¿~;6õòçSNžpiú §©e™jIœÊÂÊÊÚÚÊÚ"j÷Ì ¤ÃÔEL€s/Y³¬¿»…™‚Þ%w.sÖFÀ0ÌO–áÕ~Ò>(E� %ÈN¤Øgo>P�  qÏïÇh²¬ùG|¸{æŸ?¸wáî»/+PbÜÛw±é•Lžñõÿøøý‰ñ1É£®mœºòì—!ˆYIˆ Œÿ‘å!±/¯]{öÞ‚j?¾bq– “#ß;´‘†Ãø>üXß笱pþ‚Î;7iÜñä©S½Q*y;{+3^¡À!Œ9�L)’‘$‰Bè7ßhAÖEûüÑÀKa§NÞiäþ!ͬ=›«FƒR–ÏMî<`ýk"ÞXÒê;ؼC«zÅü¼],ô55έ\ó6Ám[UóU&¯»‘𛧯?}oâ_½F)—ø;{§õžp<�8s Ïâµ~kXÕK÷ðØ¢ÞÇèçÀ–5Û¶mÓ®aQ‡Wàq§Çµí²ðØ UŠÅí¢Î­ëßòÝÔ//Ÿ»ý,Ö¦hêE¬¢.oÓcÞõL¦D ’D�@­;Þ'onõ¥}‡ŸÅ BbT¬š“Âß¼“�ÈǨÚ»ÛfÎ_¶óÜó„T«»75¾k“d ß{~~<��—»`€òñ½G"�²­:hjïZ¹Ó»8çÚÛº˜CÊæ�“’ݦnXÀæ‹ö õ¥9#ÿ.3yB}1$‰gW,}\y`ÏÂ*Ã+ÒýùU¼üëM{UgâàŠæéÏÊ0 ó}dþ!œzÚ¤WXKAö Fîï·gïM�ñÿœ˜4áa–e#Òž]<xþuõ‹Y kM �@¼>¹¤K¹w$¯/“·÷¡ÄŒfl¿™Q«ýÆNš_!Ý™VѯÛÞd͟ϸî1oýî3TÐh¿o#ˆpjàÿØ;ë¸(Ò7€?ïÌvÐ ’"©`"*&&¨ØbçÙžgÝ­§ba`Š"&¤’RÒ½,ìîÌûûcABëî¼ßÍ÷cìÎÎ;ï³³óÖó>ÑÄaA`~ŸßÎ=7¹ÃøSIß,w™ß(Óë_o˜-†ÿC~òÖ!YWÞw?ðpιá LÆ\Êÿ]dî‘äĉSáaoUUYXˆ,§¬L^˜/).’ÊøO}5•Þ$I°È¯6Õgëê’@ççå)Ò.º–‰Œ†m<ðÇÚ{§4f>ºt7‹–ËäH±aS· köïšêX±„%m¼VoܶeÝ„–ŸÚï#qÇÙ‡Žøž˜Ñ„Mç<}£�`9L=éwôàö-GOÌoǦ‹¢#“ÊÇH¤í:}ó–M[|z›Vçß>蛨wÛtóÒ¹‹× ÓGY7\JQþê¤aïåûûïaB(Þ?}‘úùgA¾oÒPw7—Þç20Û¼§[ãšFú��d#ïí¿;Ç.w³4±rè3ëT4Åãq�iÒç·¥C›p2nónÛqÆ…z*�ÀI O$,ÿ:H âËŠ%?¶e)ÞúÍßÚg^;•ÏœD§Ÿß}Qà5¥få<´™ùâÜ\ãëSF퉡>Sœáû©m™‚ �¡ û:/R¡ÿM"õî†Ñ.´EB “–žëTì¯Ê/ÎíÚHS(ÒsôØø(·Ê°/‰Ø7ÈTÓëbYÅyâåE½ít\‘AÓ«î~²ñ @gÜ]Þ×V[ Ð´î¹øFZõ¨÷'=¹¦3|:H#Nhk¢Â9ÙÿZòñèŸÓ]-5ø<UãÖ#w<+¨eJ"K8ëm«Öã@¦ò³üc}ù¨*|÷? >)DeÜžßJ³åš¨]¿$üÈäæê|¾ºE‡ÉG#j¬Š©wÖhk¡Áç©5ü{`Å­+:ѯ²6þ Óu,¦v¤O_’õòìT9‹!¸Œ?¼ñù Óå]EßTmÉù¡:ÍW¾þ±z‚ââܾ·µz´¨mºS/tÊžî:Ý÷ü%ó쟛ú·Ûêi›tƽµÍ D<Ž­Û‚‹‰5›üw—ÝðÖ!+Z·ÓŽd@²wœ«®ËS7uòÚü8ïgÝEeø—ÃxýÛ÷ñöîmûÏÛb ø<.#¨ …*Zše¥2Z††bc�¬œW)H’¬?~am”&%~ €ÐÐÔÀiIi¦’mbÚЦӺ×rLgefÍ'­ñn¡–è7w`[ûŽÓŽ~EÔ;RÏ@—�\")Á Hºº|€³ž¡‰~‹ÅAr�ŠúüIg§¥•aÒÐÖNmíÍILHɨº¬%u uIÀ%éçŸ\:ïmðÃǹB«#—¿àÓ’Wûy|Ûao„¾MO‹{Á»!XX’�„A«AÃFN¿öÄs3Uü×zSïØŽ„BA©DR>Àâ’b)G(ü¦±ºpÞµÍGÐè_zj|îA¥S¯œ~bê1ؾFÕ$OÝ¢ýÄUÞ _û]‹eô \°°�� �IDAT/Ÿ±|+O‚Pýå‘ò¾^M€ ÞÜy£9fïýסþ‹,B–ZX�ÔÛí#Ç\Ô^x3üå‰Ùë<g^ÈÁ�¸ ì”O¿– Ȫìé¸]£G^2\ñ(-'öüТm^s/×XÓiN²G1Ö/,üÊTÎÑac¼¯\a•<[é± HήEòÒ'+<¦?vØýt§ó›¹‹%� Yí9íY«Ý¯2³Âvzç3lù£j[°%±þ«‡99OòKþ¨Ç�µ¡¾éyJr£ 0hÐo˜k54yʽ-ã]š{x[ÅSödÅà™ÏšoMÏ ;ä9wвÇeUÊà‚×·_kÝÿ üõU‹KG.»_�€‹ ‹¸ýçH¥R©Tšj°à‹Ÿ É}_èãÙ¡ŠôH«Ãô•SÚ~vh­µà×€å2ù_<áÆÙW} vdÕnmв2³$¨…zÚ&Îò깦ÜxŸ“|g¶ŠïèÉG«m-}gq�ª¸HÞî‰T*•J oMn@�Œ[ öñ}qcI£§¿ÎØÍLìþ8ÖžKhù#—tß ‡Í“É(¹g¥çs8|ŠÆy¹ê6æðG/N� (ZA}]× È|ðû†+9˜eÒ¥‹-W[O›�B§çòþÏû_<ïñ쎡f,RßmÕå7¡×öw„·~‹V]Î6‡‹� skÛà¨Jå¶ìÁúÙûî§[ŒßuÊÿÔ,§òÛ‹8 \”Wð©Ü¤¶¡Q)áor0àâð×qz†:Õì%Ê+¨o¸å¸lz“’™ù:àÔÖi ëýqqað®COU»õoWÝP‚caoÉÍÎÌ®÷.³,m­à]Ä;��%³´µüÆÁºVä!7î¦>]ÖNK[_ÝpÈÑŒä½î'ߪaʇ³îÝymÚµ[á ?>A 9Õ{ê ;‚*TUàO5ßêt€4ÝÖß4¶“…UÛqÓúdÅÄæcP„ûé8síèf–,ƾ|äJ6 Rã%mל­_©yWÄ„EñÛèm©&ÒsænWŸSmòN§^8rGwܪY.æÎSVM0}xøl|ùHQ´lâYÇízÕbú% >þgj×E«ڛغ¯øµgöÉ#÷¥�’¨°ƒÎƒ:‹ÅƆt7ÉŒK¨¶]2ã2­]¿2Ǧʸ¨ª©©©©©Ï7¯ rZ³ÙÀ�*vg7=“ç³1 ü¸díñçî­mS†—J ¼Ÿd7lª›™ªØ¨ÃÜ™]2Îy.Ç™§‡5Ðï¹7žFš=×Ü<ÎÕÞª÷t÷ò[@ç犴tD<Çãq? ÙSùÓ‡ ;ïÝb‡wÏs‡oäfüÄùiWžÞfǨÇÖ¬{›V8?jc›CG_Ð��²äC]÷íT��(Òþìµwûíï³_/ ð½ÊîëYm §·¶wØV¾.¾¾‹…:§jÚfìžÐý•åñggº6Ôà±yê¦=·G•>žg)ì¼[¹²ÃYGz«Ø,z®( ;8ÖÙX•ËjY?¡44QD®iÁFq;ïJ¥¤Q'gvol¨"P3k?åäÛ2�ÀY—fµ³1ÒpØ|m»~óWÎvof¬ÊhYõ\v7K)Š"õÆÒ~Mô„\¡ž£÷éäÊ{‹3ü}M n‚µùÖÕð<[��T֢̆vÄSù7y45q8"]û׋�@öp–‰â{œ+À/öxw´Ö ´¬z,¼’B�wt´S#}5>›#ÔoîµxÅäî Ä|¡žÃà-ÏÊ£F•Åœ™ÝÍJ‹ÏUiÐzÞÍO÷¹qîMŸîŽ&|WÅ|Ê i­Ñé·–ô²Õp¸ª†ÎËŸÈ�½<0±£…Ÿ§fæâ½'¤ú|T<§¡°ëÞ4åÁÿQ:Fo•‚ü½¿O¿æ&ê±Q«‘»C•"â¢WÆ»˜©ñxêæ×<­Ç𳞶I%†EH›ºn¢)Ôl2t`+:!¶šeÑ·¯l�¸0/kèê Ê[ �€´›÷èì`¦«.ä’€4-,4¾Õ†á?‹Äw ØÎ'D�@'më n¿5‘®«u(^oëßÌ\GÄeóÔL†®¹öcM£ê§ôî\u—§nÞ~ªoìgZ-ÎÚäåd¬ÂãkYuy2Z 8çX_“r�€²»“tFùK�äA³-4‡œ+¬Zü“Þ[vk‚¾ÁÄÛ2€ÒÀe®MµD\6W½a×+y:›køjfg]H¢��gùÏv±1Tãs8B]Ûî3Ž…ýØp %§¥¥ejjj€ _D’¬Â¢BÂ��4‹E¢iU€'Eøþ©ÃöíÒ¤åЯ¥<›1f¶àÆý†wÔ€¬ë,ݺw׎ÍËWß)ÔÈ‚W÷è9rÚÚ“ã H6› ÍÛ<tËèQcÇŒ˜}6í ÌÒH Pö!üIðƒÇï>ί6v,œí÷ë€1½Fm}Rõ‡Ví2ÖÄ,¾3¯Gïþ}ÝÆŸNÇšG÷mðWv~X!ÍO¼pÞQǨAë~í¡Š�J£ï]ü!-þÅ…µ[î’m\šp@utæð_ýSêøæ„¾Û°YÇ~?ôô}Ò‹#ë'; s7­k±.y¾sܘõ÷r¿æáátÙ“–—õ!/ëC^ªï(Ý/…íîÆÁÙ+ÇLÙ¢uÊBŸ†‰š·hX9¤âÎ^‰OýyoßÒƒo-ݺXü˜” µ£tŒCAIuX€@å` €cLÓßi]v'0¯i×vZ¤ÑañêvöJe�ÇÎÁ–Š {§�¤ÑÃg˼ޖ¢ªÒ±[ôìFú­Xr9&ÿÃÝMã\Ç 0­6)¢ßDƒµ2¥£=7úM¤� $xå´«í¶­wÓªeð¢ÓÃÃsÛ© ��¡½cÃ∰$ Äíz¹dY¼! ¹ Áï_É qnªUËñÛÌںģ‰zmÝ7ptíiÕÉ¿yê!� TÌš;;71à#`Ù{oZ3ÆI§ªÆšÔ·±G_=ý4£TQ–ÿ¡bg$¥ÊߨIçf¦*UïAé›ÛyM»ºh!� ³3²K__ºjë‰{qõÌ|XV§¹+`ÔÜþèñêàg…uœGþvÅ?ËÔûÏÑ›v·Òy°ikªLÅЮ‘4öM @%¤¾Ë)‹“KЩÞåéØ5åÔq­/¢à¶ï a?Ï6u˜� •S>~}ï÷¦/ç÷Ÿ}]¹­,XvgÕ´3ê ƒÓ ³ßÞÞ2Ќ׬—›vÈõ»9�J<SíØ¹qÁ9ŸÙí·¾Î.L»¼¬«�@Ú,.’J¥…7&%|úOl´ò^\Bà2ã[“¼wÆP€%‰¡¯Äã®Å¦&…îë³uã«æoEĆìïü~ýôÍ! �*v×ð!ûˆ‰‹Ø2À´r „N»|úQÃÁƒmIœS£vqûޮăëA�ÀÙ"Í\]Î]ÛíTL^AÊ‹³¿8)ƒ±Ûn|+‘J¥ù'pqÎ¥éý|Þµßñ4!Æylô´“épþ»çáf¿>KLKx´¦QÐú=é½÷>ˆ~÷h­í3Ÿ9h�YÈêAc/i̺veYÝO.Œ~Dó{›’qÛ§-·¶Š/6ÏØ*u+)?/áÑ‘1v,À¹Wf¸Ï u\wïí» -m"í;õBÕXìf=º¨‡Ü *�½¸ó·ïîL†mð~Z4Ã?ú}È§× F¬y.(º3¿ÿ/OmWÞ‰Ž 9=Ë©¾Põ´M–m÷×Í;–›ýôO›ŒõªfÐñ-Å«¶G:;#›Šñ[µâ#7"óªè…Ò÷v õ›MxÔrÝÚþµÉf`øZêhtFø“”æ¿¿ˆMŒº¿Í-{û€þëßü½! ¸Ž“þ|–syËwʯ~u9ÚÐɇÇô_÷¡ç¾§±Ñ7—˜ß›ä¾(°DÝÅÕ!ûÉã8 @ý08#ïù“H�•œÕܵm+¼OúϪ=˜"õõ³ÒÞÇ¢SR"ÏzZ}ËÐçҫذsÃÊM]yG €%ñ/B…£.F&Ä…žûÅ `ŠÛ”K?.¤,›ÅAÑ”‚ÇáÐ…iª¬TJQ �L°X,� I’Æ4MÓl6[ ð¸<’¨o͇Zš"?úa`Ðë4–Y»! ö\]ÞIFCwœß8ªcÙÛGwŸÄrHª”–”(pVèµ3'Oçv·uIouBµÏ’ Þ­ 1÷®ÜˆËÈ«âÆi7oË´n–Š'vlÜq;]Û±™…–݄Ϳö´g=¿qóIlJNµœâŽ+N˜êjRõèeަ“×Fß­ž¥–€NÚ?Ä©ÝeŠ\Vû_ÝÞ[��:ïíÝÓúuphÑyÈïo¬æÝ:H<=úùãq¹u}sBÏcÓî±èø°¶íï/²góÈ:õX’þäé«Äzó5|É7(zÿòÉÓ°4)�*í]œÔ¸a•™”¥¿<¿rtfÍÚ¹ýr‘ºçÏv?…Ý Ãÿ1¸êKeR€ºÌÀ*w.P¾+×2(“�’þàë'øNô:¨½|Ï4kpIQ1- •=3‰…P\Xׂi¹¯Xæ»wr×Fn{¦÷1&pö¡^B‹Åbñ~.,’ðÄÂòõ�в¢b€"rǼóW.u­\|ȃçZrY,‹­>òrn‘„bQU!ŠŠ1¦£~Ÿcötã(s›a—tFOê¨ù¥ßºìñŽíaæLuTöíH§çÚ ¾óœëŠV+î½îø Ñ©~æ*"ÝÆ}Wä°ø<Ämžö[Õ]«Rè„Sã‡Ð^±oº5 �@š^½~lSî‡{ë5quæsÿ‘šZƒŠØÊæ¡</-½ösqZÌýg®óZ:˜‹´l­‡O³\ˆ‰íÕSŸ¥bœ’ZÚP£$$%‡†¢äÔ†&v_xSj­/ï†ï-µžNŸS5°¬\û·³61m6dݲ~%~'î•T/HˆT’ݨ ¹@Dz‘¸m† ÒypîZYh@Ù¥—3-sób_ÅäÒ"}›†:å?6"9ûÁ¥GOdº-^ݯ‘¶®Ãð…£,^ÞP𠾦ž¶¾Mïþ )¡IS+#C›>cúš$½ /ÄTü…SÏ̧l˜ßÕÚ¨mÇžNz3@:å¢ïAH@ŸÔ®Ú}h/¸qî~1€$8à…~žv,žXÄÊŠ~™PH¨5°1+÷¸@«\Dç^;|‘¶Ü§‹©–¡óÔù…׃•¨„HÛPWÛÐÑklM¹šy‹†† ½FuE¾Ž”ƒ<Ô×7ÖyþæIíš8ôè�¨·ëZóY,‹%ìy0�!Ò77ÒÑi`a(Ê«­"$T)R#ߤH9¦VÆb„ó®<Ã~ÿ}ƒ±±½ûÊc—^®:çµí׃ßÿ~€üå•›Eúuâ½<q,¼åœu#šèj7rÿur«÷·n½£Jœ8WØ{éÆá-L Í[öël]ág}mSØ~á:÷ÂSsûØ›µ[‘ÔaÆöw¯Ò 7ŸíÓ]DyOwnaï¾;º|q†ô&ÞÌÏMÚÞ&drß%K?ÿ%¾ˆÏµ–Fó†fŽ}—ZÜ2üб竞�i6ll¡«¦Þ Ýä‘­ek7S£Ï 0˜²Ù§‡‘Is¯M«úçŸ8Pjܹk£¨€ÀLL§>(¶³-x˜DãìQöÝ;‘‡+Gö·Dm½w1¸êzzÚº®†4cs ¬íŒ ºz{4) {“¢”ˆê™›6°r¿këHòÂþË?LO Ü4ÁÓ ÀXªˆA@²Ê? €$I’M`ŒËÊÊJKKëó÷`·\|çÍâº>E*Ž#Öž±¶ÚÁ.KovYZó2&}×_ò€ÝÅÔ‘å/{ïýYþ’3èpú ò׆Ýùv[ôI•ª­¦šVåÀÚìõsÍÝ—þé^£r½‘•u¿ž:¾ÆY Ÿ|XXþ†ì°%2u˧ߴV“É—ÞN®yPß}Ýe÷u5Ž :­ Ž«v„ã¶+Ú­j9íö N<\PkEì–«_¼­xƒtúïŽè_õc¤åu2Å«f!ÒrúÝÄéµ\ÓaKø³òsÌÆøE©(`>óFüÌêb·™ã<§V™þ*”Ë~ €€2 ïç‹(#*µÔG—ºoôé–Çã6?u之Óì¸�€b!).!ƒ‹‹$ 2Õ!‘"|Óð¥ÒÁq³-‹žìòì1Ô2äÖ¤»ž·*¢_Ï8ÞGXš')—KŠJ8"ç\Z½ Mºé®…à£y<ÛqîÕÐÑ2 ˆT3ømI‘„ *„‹”-öÚ«¿íeàÝô»kG qŸ`õ攇ö¨ ¤ŽŸ-pÛæþ%ç��¡×uÙå°e��¥S­z>µùÄAN÷çÈnsSGû]žnWž3‡0j7ll;�˜½hÂò6­—ìœ>à÷–µ¯¯è¬{îy“\JóY”\`V×®OVq¨èTì° TÕËRs‹C;SÍ?ßGX(ÙOp.[žkË”¡×ÎEû;ö pÎUß»Úo~i„?ž±‰nÙãK«d·[qqû²_mc<·éÈ%¬ÙDÜj¤—™Ë±sÉCZ]¿]Òeu!ˆÝ·\Lýmñ$‡í—‰«7/а†(Eiiw¼txÕïiä›KC•À„Šš*””HÔ‰UÅ •–‘–F&FŸlщ~§CšxhH€Ê'µ‹»ŽÈó<v-·“ʵ@µ^¾ÍØgìÁ³¹‹–zX¯Ñî1ký ºê׸bVJZiÊu7Õýåï)²snõ½ BEU,MTŠBbQYJ)�ñ!SÐÀ¸úJ˜4wòE¯ @ˆ 5Qµ(µWDØÍ>y¼ô×UÝÍ},.Ø´aZ«¬äTª‹i¹Š‡ebaŒRÒi¨”[ÐÁ³wÄù{E=õ.øçuYÕ]¾Ÿò¡ôþL Á/å·5Î¥Š>¤—h;6ø4wÔÓ6é¤ÃÞS#¼n¿[ÕŠ?>}·ûjËgK›²Pq@ÚÍŒn�3¹«G“åîŒ9覴‡!xê¦mÆo]xÉxùéç+œ]˜M †¯¤fߌ¿¨uº,UÒ“Ó(€¿í™“'^Z2cù©Çq9”¯é6¯CQM¥¥¤‰…IùàÄ35×—>LÍ'Üzºo¹ÓOv;©Ëo+%3ÝÍ-¾ñ¢aÏM暆•#»™ºFÍþ³Ö¡UÙCKJ0� ±ŠJK> äǵ´6¥W料JA!Ð40„ˆò$Q0�€¢(…ÒS“¦iÀ˜ËåŠEâú.ËÀÀÀÀÀðS«°ú²Ÿ Ê=ªž�A~‡5ο¿hàÜÄaç.ù8«•—ç[76Ï‹ÿ t{xEÚÚ7ª}¥K½»tæµíà¡6<`k·ž²ÐSëIÀ³RÍÄÖÞÞÞÞÞÎBKhÝÄ"ßD)ãÑļ/³nbÃ’=ñ¿žôhž5 !Äí¾ÿCâ¶Žº£®zììíííílŒTõíí5’Â"” .Iø«8‘]ccRrî\r롃ŒYÀ5ê¼hNOùƒ»¯¾h«Fþâê-IǾ¾a* ˆ?ºÁ7ÓaÐ�«jËNœoAÿÙ‰^~þ‹Û¨Õr빬yYé™u…Äù."»o~8eŸŸ‹mÝÓHB[¤Ž 3*œåiùy\¡ºHk‹Zi!wbCãôMì³_݉}ñRÜÂõ{°qæ•Ó÷ }y„?I\L:ßÀP ªD*£þ¸‘¼Âøöä!¿‡*€eï=µí«}{Oœ»RÜwxW1�Úm¦í xwkûĈ±»ãi‚$AV&«0™é舵†ŸÏ-UR&“>[d[}ñ Šx)õl¤¾‘>Nx—Ps{ˆŠ;úUS ¿˜Oj~û)ãMoï=xâümu/'�°Œº-<þ8>êÌ€ìÍÓ}s0ÉbáJ -=®Å¬ûEå"Êä’ëÞ5,Û«ºU¼$õôJâc?TŸÃs´ÌíìíííímMjúÍÔU¿Ñ€•çB^íiî3hÑm™Ž‘™—X®|S$ƽGúFÕ]øíGx¨Üôõ¿å{AÒgdwU tôt„=ö¥H+nsYÜfŽØÀ@”Wø…ÚÇzÚ&κyúžŽûЖbD¨6¾xœmLÀÃ*_ÿ;‹W…4lb¯Y”‘QR#Ã�žáëa …켬œj9ú¢Ö RRеôtþFæÂK ÇeM¼“S”¾¡cÝa¤‘>NŒ}_ÞI–&Æ}à¨!–ã�wý ËçÏ_‰pvëÚ­GË7×üÎ] 2î×Ïš¬:²ské?k¥¼ûÃåo�>UºP©Ii ­÷=ÚíjИ®:SR¾¦)Z®,›Åâðø|’$er…¬LF¤@ `±í!ÃÏEµÁ¬zÖÃ6DÖt«º!| TÄÎyGÔæîŸ×”[VZZZZ&§XöCF4}µÍçXhbì½ +NÉúŽî£U» ‚0°±¾:{00©¨$7ò‘,ì­«y´†ýGwÉ8°dû£ø„§{–ìMtíaAr{É¥Ë34”ݯo:ã~ÆÑÞÜ*å8mFxÜ^ûÛ…ˆ¤(ÿek¯i Ý‘¤©M#|âðót‰$#ôØñ2ëÆ ¿d=KÅ?δoëT©%À™×}ÛôTRgº¬0#6øôò]yn¿tç  8xýAKnç` Â·Ï9¤6ÿÀ‚fUnH#n]x‘˜šóÔ×gÝu²}çæuOÔh�°¼L¡À?=bq¹tFtv l ²r3(0°ìØJrgãó7 ÅÙQoÿÜ/èigÇ µ[w¾Ùþ$ÎÑÂNÈkÒYïÝîà7z–­Ì¿Ãÿ¸ìûÐtÐà&å‹}/}õ&¿æŠ›.HKHËü¸sÒ¢+š#ÆuæW/H§„†¥æK m{;CvQA! ‡ÎR´múÎÒA£:  ,þéèôÂRŽ¡ƒ.,(ÆlS £´€ó±iIá/c yGxˆü~›süIì‡ÌôİçÑ9_ò´fƒÇuNß5uáÙÐ÷IIYÊ­|êÝÙÓ‘­<û—{:~Z;�iã=·køòYgt½F8²patðÓ¸ìb…Ь‰¥º¬  i𙉢¯{ú>-þåë÷eš=G¸ž¿Ä/41=3-îUhÜùI²š ãºvâšk)™éïSrdõ”BµVDg½ }Ÿ+Áj–M„’‚…z¯qðÉ…‹Î¿INޏ¼dþaiß±}´â ö2µp[atuÞÄÈc¬«€Ý|è0‹kf칚™‘ù4ü Àë8v¤þÅ“vÜ{—–‘–VOhîÚÛ&è;­ÿ¸‘ ¤fe£wùðõØiAüíC¢5íì´‰ ê;‹ßïÞ›øÔÔø——×®ôÍnæÚFÑ©§ÎE¾OMŠz°öú@ý>îÍ™Å�Ã×ÁnÚ®eÉå-[ï¿Kýšœ¥Ô>}¦uàâ°;7BãSž™»î¾‘ÇVç3GÓ4  ËJ¥2úã°ò±¯ìé ±®vÏ^}3:å}èÉ9‹/ªë*�`9zÒ¿é³ôY›þ®*Ý´ ^æsÓxð ›ÊŽÚúϯ‚JztõaTRräõÕ¿Íë<¬—BÜú‹}"hšV¦<$X,‚$I`4`c¬ÜoQº!�€B®(•–J$uOþ~Ê=•öp¨ê®cU·¥Í\eöå™�ðÙŠ_Î~ò(ª hAU‘H$ …êîG²0VÓ꛾º‹­Ã³s}·ö¯+��Rë·ùÌ\Ë#k©uX×q×É5¶¡ ƒá{OMBûÜílݶIGž8èmòE{¼ÖKÏlm2³u£–“‚ì7œ]ÝA@4·ÿÏÔ^÷†êê½¶K<˜hö%—SÄDÆ 6ªf‰.Œô*¹¤®;(»5ݶQÛ‘ŸLñ{vca �`IÒËGAÏã 0Î~Yðpž½ˆ¯Dµïá,LçD]Ú4¶“™¹Cï%!6K.í÷Ò«kÍ.44߬àйémwëñ A_C“€ÔlëeVzúúÙ—4×¹qwã´£+" ¸ÃªÞ}µ÷y™3ñIfK×¹³Œx��„qo#)²ëfÌ»XZSr“¾Öß©ˆN¹x:ØrðàŠ-{–ý°™}$»†. ªb³A¨[µ°‰[ãdj`ÒtÈžâG¯®íÀ¯^¾>6«›•ŽŠšI×ý„÷¶¹JCoaÇéãm°ù°‘N� 3‚wLlo®©¢aéqÅhÁÆIIVó©k½§ÙšÚ¹Íö‹¥Ä]6øïl¿ÖÝÁÄЬYß9çb¾(n8a<æØ•6Ïv±24´vš94àÐgÎÄ8{ö3TÞŸZj�¤ÝoÆp}¢ÅÈ¡H� “n­öji¤&Öi2=´Åš5^¯Ëüuý ·umhÖlÐÊÛ©´®ç¾ËË/èfÛÀвµÇò›uEp®Ë~îù ÓÕ.OjcaРñô‡Z­ìô>»éˆj«HsaA¿&úªªúÎ+Ó{oYÒ[Œ4úl÷ߨ,t~K˶3ƒlÖ\Þ5PˆºMšnùhÆ„#逴3RŸ­�� �IDATɹ(Ç|Üx'.��§¹Ï…?—ìÞÊÜÐØ¾Û”C¯‹@ÐnÕ•?=Ëx5754m¾<ºQK«ÏØ©ÔÞ6éìèà‡Aáé2àº,?³¾É³­tUt›OyhµòÌjW~eƒúÎâ¸(îÎΩÝÌLí:ϼ¦6åì©H ³Þœ]ãÕÎÚ´a«!Ûszî÷_çZW,†: ŒFmß= xw¿Æ& ,{(hâh*DŸktæÝåîŽ6=V½ï¼×o¹ógBÁþpTú.ßÒ+c]c5±ªéìPk#!ªÚWöÈxì‘ õ¯y·²°îº,¾ÓžË¿w�°e_B¸ztU¤ÕËÃEQâ8j˜UÞ©öþókÀ¥QG'ºXY´{Qó—³ûG�¢rHV…,Ê(ÑÊ¿$Áb±P¬)är6‡ƒ�a�!š¦1¦Sâ#Hõ ßs·.I$Qžï€ ‚À@+ý’@€Q®@$*O}XÅpce2Ÿr…)ž#ûôí‹>êP•�‡æ ÷TUUœÚ¸|ÔI|úâ#uEIø¶ü‹ O=hݶý?-EM*¥¢¶vrðíý"h^£¯›óÕ[—`*ãæož¿d/|æ;äï8¯]ìØùÕœhÿ1uÔ­(.(A²„3³¯UÛ²³ ãªZ/?çóÌÀð|ûƒ-»5Ád4:–¸·ëw%›ù*þ…ÍNÜÚ±±ß ˆ{3Œ«k=¸ß¦]{ÓJSIeè�ŒicLÓ€1iŒ1PPPP—.]j^¡ ~~3&O¡Â9¤� Ó0Ê« „BLƒ\¡ ‚Ëå’$ ÀÌi~:ª˜T¨\ØW˜Ð4Æ>}ÍBŸF1``ø6èx¿3/<6üÚ}¡z ÒÉÇ=MÕ ]–gxØíùOd¥“‡ž>ŸÖaHºêVDléb nÚs{î™õŒŽ€á_‰\¡Pîº��ES2¹\!WP ŠÆ4KM]C™>š HLc’$�$ÉbÔ ?LašD"`À@ �#��0ÂX鸎1&HeŒ6 i �‚ He 73ѹ¾¢áœG…ß’ò§Þ‚„ñÄù¿U®»Õº¨ÜÏÀrXü¼¸ÎtW õÂé¶ïCÚ?-ÄÏa:óAÑÌúÏû6Xliš¦(¹‚ —CS´‚¢YˆÅ�…BASÀb±hD+‡C Ä*‘³ÃßI…á@=§‘$@E+#•Q ƀῠ]ÅIJ†­%«D"e±Ù ‚àr¸™LV\TÌØd20000ülT "��¸nË7‚ hƒ2áI*=÷(ŠBˆ@ˆÑ0000000üס(š(Oz‹�!Jil‰1i"‘H„�•–•–Je4MÉer X,bÍ~RP}žqc�#„0VŽzŒú›¡Цªe›®ÈEðx‚`±Ø.¤ (6—«¡¡) þAq¾ \˜•]öM%y !¾­låErcŸ„¾—~u¹²¬¬Bf¶ÎÀÀðÍTZÔ½ò§(L­ŒÊCÓ4…1�Äw$¿c```````ø?‚ 2&Ê£Aˆ@\.W&“Éå2LÓPKKK,ËåòœÜφ®aø  SÏNî<|{ÔeE� “{8´ZöLTÂñ©îËï|×bN;7wвÛ9_ÅËËdåç)¢v{vžé›Ê¿```øF>jˆº}pE:Äg(s(þåÂ10000000ü I’$I*c9A…c&ÁãñX,–‚¢e29EQã²²Òü‚™Löù‹âôcý õ5­~¹­<gñ0Q×±ž|«ž‚_´¯Ÿ®¶‘ý¢`9�½·_ãFºººÆú ›¶êåísôy毎ÿ1¨¬{ËÝÌ»n}GU‘'_YâѬ¡©MçÑ;Ÿç•OtKßúÎíÞÔRÏ´i—é'ÂK”qaè~oWC#+G÷ÅäŸ\½ðî† a®óÆ[³¾TB³i?¯áŒ¿6uÓ÷B8ìa?àH¹b€e=va§ðµïýÍb000üË)7"ÀUÔ©& e¾åk‚D•Q 0ãƒÀÀÀÀÀÀÀðß&Yå D(÷VhÀ¦ Œ1L ‚Ëã±X,¹\®P(86‡Íþ§Å®—å¥g)„æm»vtn$Ì}um×¼®ãÏ&Rõ—ý‡§íŸÙ§Û„?ã$•Q*fÿÔ)W59÷àáÞž9Û&,¸š‡Ê^ü1zQˆý ÿç7×¶Œ\>fÍã�\°xܦäÎ[îŸ_d|oÆ„]‘ÕÕ"tºÿÞ«š£'wTýŠ8\BÇaógtÒûÛwÓhYUeb¯Òy²—Æ•ƒÓ™9:Ã÷QwX¡&À�@$ÉbS$ Êþ›°Ùl’ H’$¢ªG'MQ,‡K((„b¡¬LV*+‹D§XRüíâ¼Ç;.Ú•C‹t̬=»¾‡*‹¿´nÅæ‹OÞæpLÛz.þ}~/c6?ß5oýÉ'aq%„j£ ‡®/u®S=Aš Xwt¶- Ò·ÇÆ XtýÚò•׺è£öS†«F‰©š#øË6u9^qL}þtxãI»¼˜¾p€ïS·szõ<{îCÇÅ‹{[«#ë_é|zÞé Åέïö§úšÓÉ’ }&ù:í9ýzêòæ pNÀµg&nk’��¸ðåÑå¿í½õ*…ÒwöZµeawíÐÅm<#¦ûÒC�Ò›3šÌãî}¾,w‚Ã&ë ÙVJ*qéœgŸ'—ðô]æŸ;1®‰sžî]¸dÿðN§ÁóV/Ø� ‰ô]µhó¥g)¥B“¾ë/njQq *ÍJ_ŸÌ g|'XÉ·~÷Ùx&8&O`Ýû—õG7�Èû´Òôàö9¿ÏݲG¯»üïå ªõSþ| ?)¸"!1‰0`Jû¸ò¬‡Â�MãJš¢�`Àð5Ó§Á?ŸŸ1a¨ë#&±Â7óäуZ„Zø9¥bøùaž†ÿKþ]ö¿KÚ¿šÆ$‡--•ÑE$PÂ@"‚€Ùl¶XE\"‘”••²Xì’’’ôŒ ’üö-gÅ«]3×øÇk; eKdä™ê‰.1þà{5‡îî“îÜÜé=Nt÷úL›â¨[—ƒ"YÆNEU £/2‡ç[ ›7ìÀí-qwBe}\¹ß,è_Ëfø²%€³OT9Vù^ÕÆZ—��`[7nDÝŽŒ“µŠÊ7jg%F��k;sÉÕÈ”2ÝÈwe£­9��HÓÚNçCäÛBÜ\£b¦©ˆ|Éo<Þ”�œ{ÝÇkMòðý׎6Ê8=Û{ÊB«ÇGûtî zâÁ³¢Q}U@þ2ð)v^ÚŠ 7>´ìÁ–ù—T_ ó0¥¦+ŒH S}§ŽÜQâ½ýîaË’à­SçŽZ®{m~Á%ž>=7žÚæ¬-K—¨j¡R�� óƒWŒðy×wÏ…ñÖ\yÔÞÓ.˜¯ò}Ü™ûüÑS§mr¸³Ô�ØNËï_gH ‚Í�sG{ÎáÐ(ùPÎ_ö000üÿQe­ ´@FÊÕ?�� Ÿš`Œ1MaŒcøc§6.Ÿª >]ü3j‚Ë“GZ·mÿOKQ“ŸS*†ŸæÉaø¿äßõ`ÿ»¤ý<Üÿþ‹ r¹¢|×Ó4… åa �(,(ÈËË“J¥¸XU…Ïçs}X.“c ņMÝ&¬Ù¿kª# î:@©÷\yxóº­û¶ç•Eø_)÷ ú­9|ø¬ß–á ¾P7Aê“@çåê²ÿÓ‚¥ - •“E$ @R\LKŠK@ *¿9Š )’à’b …‚ò£‚ò£•+ÍÌ(ÒÒÓ&��çß>uƒ8oN‡šú-¼§÷<-å¶îéÊ{t+¨@vó^±K϶ÂÚ¤"„b~Irä»,9_ËÂB tÒåSôF¯ù¥S##ÇAË÷*<{2HŠ o÷‡A‹Wôw0Ñ3°t´Ô!��pñ«-£§ÞpXjq5Š0?ß(ÇiKÙik6t›1®Yʽûñ��"Xl.—Ëå° ��B[W³ #ë‡Ä±```øOQž©ç#}ªz`\…},òñµò\šq:```````øƒ1Ð ИDÓ4¥ hº<Ì<1M’$Éb©««««k` I1I*b•z®J$�VÈË]æ1EÉ1 ‚$»ù¤5Þ-ÔýælkßqÚÑH)••šZŠé\ÿIM­L-ÛÌ»'ÅTNFö7ǺW$%¦(€PÑÔøíE#¾PH”—/öqqq E"B(@Iq‰ò^`Iq ÅB$ ¡DRq´D©J¨:/¦érÏ[ sÒÒËÒŽ314Ñ341|<]–ŸWŒùmúºq\*–‡]¿žß¾§Úƒ°æX뿺·}+¾‘ŘJOË€¦ Êý¸Æ&ºÒéTVJšÂÀĨ†Oõ>àbX8š«��TFZzYðÂfz†&zF­>–äæ×ú3#¦™l _úø?e4ÃZ7êËÕA D•zT›àsÁþ3 „0Æ4]ÝV(¬2¹\.“Q J*-¡icº¬¬T.c׻˂T ÄÎqçqQb*ýÁ½H }#}¤¾ÛªË®Ó_]Þ¶xÞ¿E«\zíÐÕf!Äk=wçœö*��q ìYPò _„Îݽòd"EhwéÞì' ´X/<K[“‚‡Ñ´‹òè°Òª¯G§ÌF-%êm1¶UCP‘ ´²5âšÙ6➊Œ–>pNtD¦~“r¿%|--AnV. �„†ŽÇlâéÇK›W¿ΞîânÝÓ¹VÒ}eçºÔ>Hl?dù¹oý–Œøe’ÈæÎ]˜¬�U�”%%fðõtUI -”ú>E¦U½<Xv3OOJ˜9uø4ñ…½ý -mA§9!džiT–Ng±°¼L^å™Â¹Ùù"MMÞwÞR†ÿU×øõ,õ1Æç0‰ j€¢iš@@ DAˆÂÓ„\.—”Hd²2…BA $Š ôõõ…ÂZMÔ«Àuöò4çÐïoçܵ[‹ö³¯åbq«!­Y²àÕ=zŽœ¶öäÃøB’Í&ÕºŒt×#Šžl›¿tã®}Û7ÿþÛùtm•¯ÛˡޟŸë5´O—ÖMzþþ0Ÿ4¸Â§ÛW^⟅e=ÐÓ>lïÚSo’ãƒv­ó“» í¦‰ØNƒêÝß¾úêÛ”w·~ßrW}€g;ˆ;yö!/¯Û›q~Íž<<ª*H›Æ ""?Ð�H½«‡kÑÉ•k®„%ef§'F¼N,Æ��œCÜ^9ëêïÕ®úω>•ñ&[N§½y•^PJhÚX°‹ ‹qŸ!í3Žúl¾“–òúü²U×T kÇGê]‡t•Ÿ]¹ø\hbzzbdDb‘rÊMê÷Xí»ÖþѼ©;£dl‡þƒLŸü±èXPLzVVÚÛè €P71¾»ã’œž‘\@¥„G[5ùò\Ž UüåKþ ‡‚ê„úìh\MÓ41“‘¡"Ʀišf±X\—C²„H’$D ø"‚ÅÖÖÑÓÔÔD!-‘–”He²zÝÆù­}NÝËQ»41<:‹mÚÎkå¹Ãã,I,)Qà¬ÐkgNž Î3ì0në’ÞꄺÛúÓû¦u³"’ß  ÏætÑ[œ#ž†¶ˆUœðäÞ£÷ C'÷YÛ.ßÝÑ×ðoÏì÷}–ãwîtËÜØ¿][ïËêÓö®ë¥Ž�¸-æYÓô•O¯]<±YrÄÇY�€T\W˜cxszûÖýW&vܺoŠ]5SBϵ«}ô­[©4�Òé·ñÔ‹g+<š6ë>a]Àå­%­†ŒiQœk:ld³Î„¾Û˜¡¢kSßÌŒ8»hP[K3«Æÿ$†¯žÞšM ÝulºîÙ®-Ûõ_Ÿä²ñè²¶B�¤Ów½ï’Æ¿{µnæÔÁk¹ÒÇt”¤±çÆ?ÜRÖÏÚMæÛá.=>¡[+[ÇŽýçŸ —��·ÃôŽŠökժØÍ÷>PtÚÝ›ïìz¸êþË~@†Ÿ D  t+¨ˆ¨LzPît€Ý RšÕ)_ŒÛÂ@,’Dˆ`³Ø\.—ÅfÑ4MÓ4AȪqkŒ1A¤%’R©” š¦d2Ylø“Zr†ºÁYgǺn2;¸¤ùO™ëá3”>_ÞÝ+qæ£C´™Y:ÃÓÆ­"¥QA(߈PfBDÿ!„ItEö P‘ SF Œ�Á„A^}úöý¬ j¼C¨vpOUU…Étððs†¤þ9¥bøùaž†ÿKþ]ö¿KÚÏóèÁý6íÚÓ˜† ‹Éò¬NcšŒiLcŒ „‚‚‚ºtéR³<Büü¦N™Ìærä ›dñy< è¢¢bJ.çp8I²)MÓTnnnfVA²d2|UFi†¿¤ÝoÁ$ÕSË÷½SüÓ¢|Š·‡~óU¶ £#```ø:ª¦2(÷*([Ñ0MÓEÓ˜VzÐUþc\©8À4D•á? I²|¡ˆ/B€Š‹$EEAÈ)Š IR$K¥¥GMM],V|ŸÃýåøÂ¶žpðÌÚ!ÿ2VC gvO¶þ7EŸd``ø)(÷*Pþƒ”¯Êã”'=@ LöK *…•äÿ/Ta|zJ�,)Jˆ*(ýÇBN(Þú­ßv3.=òIDFM”¢èCFa]’) &ÿDJ+EqzzUå€$ôÏ5ûƒ²¢pò‚´Ìâ¿Rºð}hhüwTËr?dK D ÿ7üä­£,³¢ÿî~à¯åÇŒ,‚ÈÏË+,,”J¥r…0&A$I’"DšÚZºêêš")šbs8à‡ÔÍðWÂ6jl§MþÓR|-¤VãÆ†Œ’€áÛ¨Œ<€�ˆÆ¸ÒÊ@w�*c@yºFYðÿ‰"óòŒ _Ó�P|;`åò·9ÿÔ|®,hçÜ-/¥‰úv_,�ÅËU-õÚ¬ §@º¬u£)×êHí¤,(WÿVNEYÙµ)¤Â׺XŽ¿XE­‘}Ãì£1¬ÚÓ*×–—–ýX%ˆüþì& ë¡õíàÜs“;Œ?•ôÍr—ù2í°þµüG ÅÀðÿÁOÞ:ä!ëÊ»ñïî>Î97¼É˜Kùß¡‹Ìý!’”JKe2E)@¶‰$Ê/iL(7aJ¥¥ÒR)¥PP4�,ò_·ôd````øÿª‘£€@ª«�p•ì‡5ò|¯®@‘zwÃh—FÚ"¡†IKÏu*öWå‰çvm¤)é9zl|”[¥RIľA¦š^Ë*È//êm§#àŠ š\u÷“g�:ãîò¾¶Ú¦uÏÅ7ÒªŸ@½?éiÌ5ùàÓ–4òè„¶&*|±‘Ó˜ý¯%þ9ÝÕRƒÏS5n=rdz‚Z¦$²„³Þ¶j=d*?Ë?Ö—ªÂwÿ³à“BTÆíù­4[®‰ú¸ë, ?2¹ƒ¹:Ÿ¯nÑaòш«bEêu#ÚZhðyªFÍÿXqëŠNô«¬?èô·äIþ ><}IÖ˳ÿã²ËÈñÃÛŸ<…µt}Sµ%ç‡ê4_ùúÇê jˆ‹soøÞÖèÑâ›ÔûtÊžî:Ý÷ü%ó쟛ús©ÔÓ6éŒ{k=šˆx[·k6ùï,.»á­CV´<n§É4€$dï8W]!—§nêäµùqÞϺ‹Êð/‡É4HlßÇÛ»·­ðßÁ H‚Åb³HIA(“G)#Aò¸|®€Gc,—ËiŠŒhd²ŸUÉÃÀÀÀÀðߤ¹ ª¦�¡TTè�Ê!T†.PžùݸàÍ7šcöÞê¿È"dÙ¨åe�@½Ý>rÌEí…7Ã_ž½Îsæ… € ÂNùôkÙù×€¬Jûm:n×è‘— W<Jˉ=?´h›×ÜË5VàtÚŸ‡ìQŒõ ¿2•stØØï+WX%ÏVz,’³kù"¥OVxLì°)8úéNç7s=J�@²ÚsÚ³V»_ef…ìôÎgØòGÕ¶`KbýWsržä—üQjC}Óó”äF`Рß0WqÕBò”{[Æ»4÷:ð¶Š-¦ìÉŠÁ3Ÿ5ßšžvÈ5rî e˪”Á¯o¿Ö»ÿAøë«>/–Ž\v¿ ��qûÏ‘J¥R©4ÿÔà§%£ä¾¯?ôñìPEz¤ÕaúÊ)m5>ÿÐÕRðkÀr™ü/žpã쫾;~£ƒ#-+S0K‚Z¨§mâ,ß©ž{`Ê÷9Éwf«øŽž|4•þqŨâ"y»?b$R©T*-¼5¹ 0n1ØÇ÷IL|Ä%žþ:cO4 kÏ¥¿ ´üç­«iŠÆ˜¦h SVPʰM"X,AEQª*ª›Åb)cG“$IŒAÃÏKy¤eV¨¹ÃÁê iº­=¾il'; «¶ã¦õ5ÈŠ‰ÍÇ ÷=þÒqæÚÑ-Ì,;/X:Œ}ùÈ•l @¥ÆKÚ®81Z¿Òž\Åo; ·¥šHÏi˜»]q||NµÉ;záÈÝq«f¹X˜;OY5Áôáá³ñåSõâ eÏ:nßÐKåSÑdÁÇÿLíºhÕ@{[÷¿öÌ>yä¾@–`ÐyPGc±Ø¸Óî&™q Õ¶ëQf\¦õ¢ëWæØTY²ªjjjjjjÄóÍ+ƒœÖlö0 €ŠÝÙMÏdÄùl (?.Y{ü¹{kÛT1¢ï'Ù ›êf¦*6ê0wf—ŒsgžËqæéa ô{î§‘fÏu'7sµ·°jç=ݽüÖÐùy…"-Çãñx\öçLïåO.ì¼gt‹Þ=Ͼ‘ÿ™ ÎO»ºèôô6;Fu8¶fÝÛ´2ÀùQÛ:ú‚�%êºow �@‘ög¯½ÛoŸýzQ€ïUv_Ïvü*ÇèÄ­íŶ•o£ËŸ¯ïb¡Î㩚¶»'ô£AeAyüÙ™® 5xlžºiÏíQ¥çY ;ïV®ìpÖ‘Þ*6‹ž+JÂŽu6Vår„Z–ÃO( M‘kZ°BÜλRi�iÔəݪÔÌÚO9ù¶ �pÖ¥YílŒ46_Û®ßü•³Ý›«òZV=—ÝÍRŠ¢H½±´_=!W¨çè}:¹òÞâ ß@“Aƒ›° FíEþ£u5<Ï��³¡µ¨ÝñTþãMM DŽH×~Æõb��=œeF"„øçÊ�pÁ‹=Þ­uÅ-« ¯¤P�@ÅíÔH_Ïæõ›{-^1¹{c1_¨ç0x˳"¥ˆe1gfw³ÒâsU´žwóÓ}nœ{Ó§»£‰ŸÃU1ŸrCZkEtú­%½lµ®ª¡óò'r�ÀE/Lìh¡Áç©™¹xï ©¾+/žÓPØuošò`‰ÿ(£‰·JAþÞß§_suبÕÈÝ¡JqÑ«ã]ÌÔx<uóŽkžÖ³ÛVOÛ¤Ã"¤MÝ7Ñj6:°[Ͳè[ŠW¶G�\˜—5tuå-�@ÚÍ{tv0ÓUrI@šL8s†¯Dâ;Plç¢�� “¶u·ßšH×Õ:¯·õof®#â²yj¦NC×ÜNû»Ã²—Þë` .àòÔÍÛOõýL«ÅÙA›¼œŒUx|-«®3OFKçë«b2#P�Pvw²‘Î()�€<h¶…æs…U‹Ò{ËnMÐ7˜x[P¸Ìµ±©–ˆËæª7ì:cÅ"Ogs _ͬ㬠I�à,ÿÙ.6†j|G¨kÛ}Ʊ°.¢)‚ kî£ @€H&“eddÓA’,‡Ãb3j†Ÿ¥QœRCðÑp <¥iAÍó+^þ JÃîæ5íÚN 4:,^ÝÎ^© àØ9ØR‘aï€4zøl™×ÛRTµJv‹žÝH¿K.Ç主é`œë¸¦Õ&âŠè7Q`ã`Í� -í¹Ño"��%Á+§]m·m½›V-3w:=<<׸± �Ú;6,ŽK¢@Ü®—Kæ‘Å’ üþð• ç¦Zµ¿Í¬­K<š¨×6âÓ GמVü›§�BŬ¹³s>–½÷¦5cœtªn/“ú6Öâè«§Ÿf”*Êò?R쌤T9â5iãÜÌT¥ê=(}s;0¯iW-�tvFvé«ãKWm=q/®ž™˪ñÔ##wŒšÛ=^ü¬°Žóè¢Àß®øg™zÿ9zÓîV:Ï6mM•©Ú5’ƾ) ¨„Ôw9eñor)�:õû<»¦ßµ¹à¶ï a?Ï6¼ºOA*-¦}ü6úÞïM_Îï?ûºr-ZY°ìΪigÔ§f¿½½e ¯Y/7íëws0�”<x¦Ú±sã‚s>³Úo}]˜vyYW=�€´Y\$•J oL4$ÙoÇ`�� �IDATJøôŸØh彸„ÀeÆ·&y�KC_‰Ç]‹MM Ý×!fëÆWÍ7ÞŠˆ Ùßùýúé›C�Tì®áCö— =°e€iåþvùô£†ƒÛ’8§Fíâö½]‰׃$�€³Dš¹º%œ»$¶Û©˜¼‚”gq��»íÆ·©Tšb�ç\šÞÏç]ûObü'‘ÇFO;™Žç¿{nöë³Ä´„Gk­ß“Þ{ïƒèwÖÚ>ó™s …¬4ö’Ƭ‹añaW–uÑý´àÂè‡Aä0¿·)É·}Úrk«HñbóŒ­ÒQ·’òócÇœ{e†ûÜPÇu÷Þ¾ ÚÒ&ò×¾S/TÕÈnÖ£‹zÈÝ B��Ù‹;qûîÎdØá§E3ü£ß‡ìpz½`Äšçr€¢;óûÿòÔvåè¸Ó³œê åPOÛdÙöèapݼa¹ÙOÿØù´ÉÿØ;ï¸(Ž·?³»Çܽ÷ÞìŠbA° Š *ö^b‰-¶{Obï=Q±+ölˆQAˆT*õŽk»óþqG”¨Qó{÷ûA?w{;;Ï–™yæ)#Ckt|Jñêí‘ÉÏɧO-[òÛþË/ «é…²wøóù&MÆÞm¾ze£onÍò¿A=­ƒÉ‰½ŸÑtÕ㤴ø›»åo ê³æù×µfçzÿóNBVvbø(*lâ¼Sõ9Ú0oöè³úm÷’®,´½1>pî-±Ž¯gþý{É4€"áNTNá£û/�tjTT^Sß6Õ¬ðÞë?«÷`ŠÌg%=&dd¼<R¶wùU³ùgc’^œ$Ý;iéõr�,JyÍvæejrô‰M#&v›xöË…”ÕÐÐ I‚$)’$I’$�c3BˆÃá…ššš—«P(Äb±\.%HVƒÈÂÂÂÂò=TÎH¥'�„Pe ‚J]�ª^aÿ‚Á³<5l\èƒÅÛp&‹K˾€¯¬ „|(+©o‹ô—,j´c‚¿£]·íРɽ, œ¿·Ÿ¢(ŠRo¹*¡¤TÄòUóÄjÈJËd�Š—›gl´ôߪɇ<j¦—¢(Š£34ü]©øBAu!JË0ÖÃVͰy°n˜­Ë ³†ÃÇwÐkè¸_zoó¦gLòRΑa÷•§Ãfµæ×³»°çê?¦Žô¶Õ5 XQ@©óH¶™sôÔ².úUB§3p·Á’“I��Ò¶ÿò5#sßÞXÝÏÃkرüGÚÚšB§`{yaVvÝûâ¬Ä›ùþ³š{Ú ô]ÿ`'º'º·ÖÉ|˜Q‚qá“L‰½®øIF¥OÞdÚ[¹é6ð¢ÔY_áå°«ÚA!-?¤j œ|û´u¶²n2`õ¢ÞâS‡nˆk$š¢´ñ9r CGSp½ô3¼}âbYtD$é×£5#r “bß1{CÕÍF¤ZÅz°äÖC¹Ý,ïíh`ä9xÎ0»§—#”æH]ÏÔØÀÄ¥×è>ö4ߪ±“¹™K¯VéÏcK0rúÈCÛ‰kgû;›[¸vèÞÒ¸BmÄdœ »ïÜÏ‘ô^íZ]ö€Ë'n–ˆ¢"›tíîFñ„*/áij ¡máb£ò¸@¥‘Àï.î;CZ<ßÏZ߬õ¤Ù}ù·.E)#ž3#3¯Ð‘]õäÚ¶ÍìÍ,¼B‡ù ^>{)ytXXRëÙ¿okofåÙµ‹— ÿ^ÝJ¢(Šâwß“‹€˜ØšZØ™ ëªñ5ŠÌ—Ï3ÊÕt­,…^Üs­ZÕÏÓÒÒ=péºg÷„W€óÚôîª~óÜÍR�ùÓóWJ;öîÈ{zè`ló«‡x8ΛÐâõÕ«¯hÉíC'Jzþ²np3k3Ûæ½;9ÄKãcm“ßnÎêÀ’#3{¹Û´]’Þ~Ê�Îg¯Ö Ãnó7Mö>Ø:¼™{à¶Õä »Rô.5r“÷“  ïI>|,, âC­ƒÒµ°µ0³ñ X¸wAóؽ}U=Ò³odg¤­cÑvÂÐV²W oê6ScÒŽï0øûü®.æVMC]Ö§èÐÞ‰e'Çøˆ[¹˜É¼u»Ì͵øö­tçߎˆwïÒ‘ÜWõfÿ›¨«÷®&WÇØØÀÈÎwì€&®©³›¥©½ïè`’Ï3”|c[+3 'Ÿ1[7 %Oï ÿbz‚’²2¹\NÓ f0Æ©\�@¨�¦I‚”J¥2™DCƒÇåriù×6û`aaaaaùÆ`P†áÅ  ˜a0Í €1V(_v ƪتP(��`‚ D DħiÃåɇ†w™ñ:ôXØn\�@B!*)笸¬TMA=“qE쯃)ÿ)*9-óuä*›“Á·¥` ­bbbbbm£)äKÊDª‘ •ŠÕ5\pvùV4~A ~µãr¼f^ˆŽ‰‰‰‰‰ZÑQGÈQi !„âÈ¡;L6>MMËJº2Ú8öx^ÃÆå·ÿ8^Ümh ACÕ „±ÿ¢ð9"™¤(qs>åàâP{ž$Oþs¨ß¯‡œ86Ù«*eÞvÐÈñÓm9ó(b¶Öñ…[žÔ;ø`ònÜ]²wœ÷Ö±A‘ rÌÔ·ê“WVš†¦ªÛË1ÕÒ‘–½+E¦m­õâ^Ç—ÇÝ+uÛÌ53=öäåÝã¶VŸ±0‚ .„ýõ"üñ,­Œ¤9o‹q‚œ¶KÎljŸ4ÏÛÒÁoÊç¥ÔZ µ¹uðÄéóK×Ä~íù ¸þÌ"§;ã=Í]{Í=•$­}h\š•U\|2ÔÇãñxüfËâè¢wïj(SMm-‹Ë�PKåå`r²rÀÜÊü=›&íÔÑ'Á}íI€:júïË;ðâ;qäÅ[Ú=›p³‘{ŽOÒ<ìlÑ8dŵÌ÷FÜL^F–$cG7-ÇãivÙ‘%+|WZ3(©¦–°\T®Ôn5R±€Éy›«aaYs&LÚŒ:ü8&&&&æÑ¶ Zú¯º+"ܦþ#X´¡‹­µ÷ÈM÷ 0“û&“¶°³V©x(+;Kœ•QC¥Ñ>¤÷ÚÉ¥ò§§Ïú…tÑaÞf¼•Üœj§Áãñxê6SoË‹ ÞÑ¥o³ÅVêÐ@>Ò6™ô}£'Å…^{•š‘½ÛçáèÀåOå_®8 ƒ¦AÃÇMýù÷£÷î¯s½½xíõJ�ÁÓ±ö³aŽOÊ‘£_wÊÆò?Bí¾7¨uFŽšÙo²¾f@ yÚÙ¹M¬ ´„zî³ïÈhºE5•‘VvVª—ÏÚÖ¤<3³ˆpéÞÍòñ•ˆ‚¼ˆké~?Ïé˜zí¯œÂ—Ûwïj«WýÍ®ó‘Þ[…²‡‰1� ¡¦$â÷Grœ­™ZÝÔçÀåª!@4MËiˆ HC�ÀÐ4Š$1¦E"QqQ¡HTV^ÎjYXXXX¾GªG% BBH•å!Ä¡(’ H’PîU|f͸èæÜ¾3Ó8;¿µ¶j^¢îÜȶ0.ö­Òí=îY<éêîX÷J"ýêì±g®ýºð€cÐjâœýûŤ¶•«»»»»»›>ßÙÃ^>W��ЉÏb¥Î.”ìþ¹Kéwg9S!n—]oÓ6v0vž4vtswwwws1×2qw×M§œp‰bc’n,Iù“'Þ´ØÏ’®y§¹3ºËoÿÓ q¿üñ…«¢…ßµ6Š”kÃr=û9Õ˜vâ¢?õ™žzêÜoí:t\GOg^^vn}CDQrØ/ñdÿÀßïLÜyÊǵþI9a ÐÁ%9ŽÜò¬¢B._G¤³]3ý¬'ד¢“M¼Z[yºçÇ\OzüTØÌ÷s°qîù£7Íú5<Ÿ(91[ÝÔLjDšžÃ~»—µÄòÚ„«¢@¹žÔ&fçŽC'Η ö�aàýÃöˆWÉWà2r[ C$Ȥ2Õc††BýÁ'ßI”HeåçºÖœüW±V~Vzí&æ&8õUjm |òhLãà ¿˜÷jõvÇX_Û±çÐÉk:Á¡-Õ�€2ï<ç{)ñÇ‚òžV€IŠÂU"úƆ\»i7KU"Êä¢K£kY¶£êNBª¤‰¹±8%émÍ±š¾­›»»»»»«Um¿™ú*Rw Zz":5f{«Øùýæ^“š›’o’ÓT±)iɯ‘‰yM—õvC‚5¯„»vZÔkh- ù]wf”W\fiòï>jBSSANRrIû˜´Mœwåè ÃÀÍ…ˆÐj4xÁ(×Ĉ;ÕNÿ3‹W‡4óp×+ÍÉ×ÊpÀ¤gùçP|>§0¯ FGŽÔ:pqFF™¾±áWôz/9;gäjÜùÄ‚Òüصê·#MÍMpZÒkU')IK~«njª(¯ @“Èð“'Ïǵîæß¹kóçO8iÙ»·3YýÍέ£ÿ¬U÷‡U_�ÞWºÐ™éY``ü9ÚíšçFR€Á A’ˆ$r…D.#¡Æá4CcŒÄb±B¡ÐÐÐ0Ð7ÐÖÖ¡HŽTòMJÄÂÂÂÂò¿Lål¿rÞ¯òBP‚jÄ%ÀCµ}T> Ê\Ÿ0¦ã¶ÌÚ¯=s׬Æ\©D"‘Hå �å>`Hã˜óF§%ÝX»äˆ,`x/ýº×à S'~Ìñ=·ÒKÅï^žÞù­»s v¬Ïp¿œÝ 7ÝMI}°}áŽ4ŸáÁv$·ÇþwŒJÑ!½2ÆÄzÊÍœ=¹ÕÊ©y 5½¶òçÓqéñç­¼¨;pxu ­]QÔ¡}²E¢œèƒÜ–97²oÈ|–N‰º—ëÞ¦e•–�ç^šßwЯDõ–a¤%9IQG÷õÿñ‘û/[¦¸�eQkô[x­�»iÆ^íÙ»jRíÒAyÜÕÓwâÒ23„Í_}‰lשiý5†�,—*¸"Æ¢¸\&'!_L‡GA^AZ ¦Zˆ®¯{ô<µ,?þï?7§htwsã­üùÏ7ÝOö²sãó<:¿ÚõÜØ¡…ígø_ã·áaw¬ûõ÷PMö5øêò¤˜çEµgÜLqVjVîÛÄ[[ÆÏ=¯7dT'õš™Œ'·^d•înfœÒâ @˜ œ1 tãä-’~Ã:ò@šòàN|v‰DÍÌÓÅ——a޵yVÄɈ¤¬ôاI%¼C‚§~žñÇý¤·¹Ùi/%4ä!'lúꔽuÒœãѯsrÒÓó”Kùô«ãG_¶éc¡¾_;�é2z¦ìâiÇŒB‡xq�pIBÔƒäü2߯ÃAGV\,Az66‚„‹'¼ÎJyúìµT¯ûn¥ûf/<–›•\Ú©&ƒGxF¯·âb\FnöëŒÙGJ¡:+bòžGF¿~'ÂÚ¬ø¢âb…NQAøðœ¹'Ÿ¿y¾pö¾ò€‘½ âjh@Ö‹§™ Àm5bˆù…Yã¡à‘¾�NÓƒìn¯˜²ýfBfnΛ—bß2�¼#‡š\^0~óWY9Y©Yuå­FÝm“I û¡Ï¨Ý/HÛÉÅ(9|ߥ¤âòâ”k{O'è¹¹• ê3‹róÔç)™™)OÃW. Ëoâë­…˜Ì[GND¾|™{×ô5·Lz6ýöqØYþ[p·m._¿áæ«Ì·™oò”Ú§´\öâúå蔌Ôûg®¾i< Å×|憌TR.c*_+•ÝxU?€¬ûô}»múò+ ¯£ÏXpFkÐH_ �Ê+¤ŸÉ•ù¿<ôîã«©Û%¨EÔ¢ùW,û÷s©¥ì¨«ÿüGÐéw/܉OóòÒòy ; êaˆ÷ãÅ�ÆXƒ¯H’CQ\55¥¡%"Ã`B™øfè¢ââ’â¹B.—ËEb‘Bþ1›ù£å~f&¦º†æFV®LÝtóÍçÅ þ2ÈnMs5Ó1 Þ—Aþh©o#3S]C3#+÷v}Ç,?ñ¼èk¥ï¥ón,îfë¿áU­k‰ ®Ïö1¶žx^u¹$‡ÍìÒØÁغ±ßäC±*ýŒ<ýòжî&fvÎG­»Ç��HS.®æßÔÒÌÚºI¯‰û¾h¤K–ï\ˆ�cFén€a*æÏ5gþ4C3 Í0˜©ñÓgEäÂù÷ïÆGþä¡%>_'pÒiòÁ½ÙËý\=×¶¡O}�vïßÍ4 ÚH_Û¼ýÂä[ÿTkš0¼ãÈx´3ÐÍµÛÆò¡‡öŒ¶jК¯Õ/Ç6´x2µ•cóñ‘îk/oÏ ,Fíús½#Ð^GÇ®Ç&QÈÝãlr8EâË$ {Çja–˜’”G‘‘1oÄõ½vdW'»:¶ºî‘éÄS/Ïi¦�X”þôn䣔bŒóïE¾,¾3Ë] ®D+`_f âÏþ:²£›­gÏ…O\žÝj\ßâÛö›mS¼÷Ää6[Gu½j¢«§Ž€Ôkj#9zéøS†ÛºQˬK^#aûe= Òv†îŸ1î~nsß™ÓÌy��„eOóräÖÙR@èãàLË­œM?cM†É8s4Ê¡ÿŠ%{Ê}ÐÔ^¢­FV³Ù tœš¹$¯himjÕxÀö² V¶W¯Y—<;8­³“¡¦¶•ÿ.bôÆ™>��~‡Éc\°í ¡-Õ�€É‰Ú<®­ž¦®CðyóŸÖoDRM'­­q´Ÿ«µ[·é§’h¡ßÚs[Ú¥¬ ô´2³i0ãDbƒH ËÏ/qy8ÇÏÉÌÌeÐIhâi¡ÆÄ;–Ø:¤·™òúÔQ;� ƒÞS›͆t$€I¿º<´¹¹¶ÐÐcrt³+BM žßìÕ½K6úÛÛ4é·ôZ&c²3|±cÔO]-ÌZ/¾’Ñ 1å>óäéÉÚáã½íL-M¾£ßÂÍøƒ‹Ž¨®Š‰§êía¢¥eÒzivÏõ { ‘n¯MçÖ5‰žÝÞÁ¡ÍÔH—á[û �Açñ“îN»?®#Æ·.-°5¦%�@­éüÓöïÜÂÖÌÒ½óĽÏÊ�@£í²ó†Hw‡6µ6³nº8Á±¹ÓìTên›L~BÔÈØlp}[ãñpJ #M£¦ï8-=¶ÜW½ªA}fq\š|}ˤ.ž6Ön¦^ÔžxüÈG˜¼çÇW„¶u¶¶o1`SA÷]çVûÖ‹„…¥óa›¶•mëÝÈÊ¡ûîb/k>úPë`rÿZèeçÒuÙëN;N-nýP°_Í€Åë{ä¬no©-Ô²žmålÎGÕ»ñª~�YŽÜzŽÉÅÑ-ìœý¥tܾªƒ��€j4d˜»˜ð öפß#ØG!ö6È©VïTwÿùOÀ’øã|œìš<£÷ãñ]CÍ�Á¹0EQ4C3 ¦(Gá(‚ ¤2)rtk­ÆU£åtii±\!eÁ0 F¡HŽð¡£ÊnMót @Û­}3YV\tÜ[ e?îøí¥-ì˜õï ¬¸ÍoOŽŽÐ½­Òµ}3s:ïÕ³¯‹i5‡Ðýg×tm°¿å'!ÏŠÜ¿zÅÖ‹‰¢«Iw®Lu¬z䉻‡ôÙPZæ½åï­=Õ@úxE‡þ×¼7n›æ˜¶uâ”ë­ÜZÖš—¶+ Óö/ê(xµwÊ múëïoî!È<6gEb«aý›hgùeìÚ¼Q×.ÿäú‰¹ŒYXXXþ[xwñGA’I’Yák@ ‚ Q€�!‚@€# Êt€+ý*3#� x\ÿÁ½P•­ê ¨Z8ƒˆˆZZš-½}*­ÞÿPÉû[>¼åÃÜ¿{»U›vßZŠÚTIŤnèèÖóqä,Ç6æûhA,..ÆtΕŸC~ÌŸó0lÀ×8¯ˆ^àÕ)fF¹õÔ­(+#Yê±iýWjo}²ÅïüSþ¿ñ}>Ï,,ŸÉ§?ز«c­†£ƒi;ü?+ÙÌ?â?Ø ™´ êwcŠeMÍãÝÛ7½Û¶c0£4då†Á3 `¬\(!ŠŒŒôóó«}`„NŸ:5uê…‚.—Jùêêêêååå MS)‘H’"šf†§Î …\5.Aj\5§a6”Û¨ûŸ¹zwˆ ’§^¾ôB–÷×Ú1ÝÛ6¶2·4¶m°%^Eqaóz·v31³sj7xaxJEàIbø²A››Yš¹ùͼX€¿{´kR “µu“€‰»£‹0�ÐoÿZ5 ‡©©•…sËΫH¤Ég~ jcgailçÙ|Àθ«Ë)·Ñ÷=y1úIø¼ÖByâ‘9¿ýÛÁ[QqZ¦ÞÝçÔj$‹ß9u§`îªþ‰´åŽŸxÛaò‚žÎN]çýØ©àäÑH (R^ñZôìl«Å7lÚ·‹³(=­Â,xÍ–ùA-­;LÖžû:)íkYF°°°°|kT*Ó•óùŠ�@ „H‚$’$UÑ  š“ ËI9uì©§*Âß-ȼù#ÄZÇÌgqNè¡m!ß"+<úèɬözÖW·"n½Ÿ©Žu÷휙ÇÖ°:–ÿ$0M3êêšB!Eqhš–Éå´‚¦(ŠR£Ôär9I"@$‘„aR)I´âŸšd”Á!˜ü'¯ÜKTwhÓÉ*µ°6)ŸÙwê™<­w[½7÷#6K ¯®ë(,¸8»ÏØYj¦M;øë—KMµ˜Ô£.º%·jß½ýèRØü “›;üž®üaÓÕR+ß6²,¹……ß_4s×]Â¥kh;£Òt®™QÃ^ÑH«ñ?öÚõàPæÍ/ÞMþÅexÊe𢅀óÕÜ,µ}ö!“ùgú›ïݤÜÂäÆÇ™·u"�� g7[Ñ…—´¿W§ŽäŒ5«ü­§z¥o=œê3²—Eu .‰ºñ„ã=óß<–ï ¬4P¹TNøÆ  :€°2‹Ou¥Zb*‹(ñ*–σ°Ÿq·dÆ¿Q°w¹hܧÊõ%à´XÿîC;Pž •-øZâ°°°ü¢ÖyçÛ¬o-Ä÷a=õvéÔëèR©LRã©k�BR‰3 BcŒDa ¦@.—ª‘—ËeF*“Ȥ [nWÄî?ðŒ4#îyJæ8tïÖˆ7�€r±~ÏK˜7Û/ä"“Á;.oôäÙâ‡+G÷][ЮÕég³°~ŸÍWv(Ý8ÏWî,&ݦoÞ3ÑžyªÛyÀÞËg£Ê}I©GÓºEàMMypž\FðôÛ ×ÅCÿ»à›‘_TXø –ᙬãË÷jL<×ËxQ± ‹ÊÄ Q™q›/Ð�Q©#û.ófñÿeNŸÃEYe6cþìl^MKP¿Ì´¯§CŒ¿TœK–ïÌ` ŒÆ Æ€Ò‡�¡ ËÍ`Œ0ƒŒ1­Àˆ`¥ÉA¥#«`aaaaaaa��¹\Î!9 …\"KÊ%Ir¹\Ä`Ì`‚fäRi¹B&)/ççç‹Ee€iÀ¸¡až˜Â¿£îÜ‹{Çwj?tá§ç7¯y‚ÎÎÈf0iÓÈ…€tÝ\ÍH,ËÎÊ—g½Î 1eß²©NEULVÆ[䱿õ°³wrè¿'•ÆÒ¼¼b®ïÌe!½?x5î9+üµÍ[ÐËVzwØ^­z/¿™Ûà9¿$=í- „®Þçä?úD$7mJï=;¸†Y�â 4@\&V%»•‰/ä#Eüö±«%S/Þþâñå…–á£'ì}­:Éò¸]CB6£©·ô}?É1 Ëÿ,JO@¨Úd!„0`� B¡·â3 J³†aØ,,,,,,,,¨©©Ñ -‰e2ICÓ˜a�€JKK¡(ŠÇQ——1 šárÕÔ8DÃRj>¿>9:¢zdâZÎ ¤‰… ÒSbâʰ™ðÝ‹Ø qŒMõ9Ɔ¼I‰-Ãfš�€006 Al9`Õº6J BÇNP3 ùýVÀÌ{Ç×Íštï¬ßý»¬ïì9êÀÝàä[‡Ï\v~Óü-]n.® ¹·W­=_€)[?¿¯øO{õjjBJW« [†Ø”l‰Ýëâ¢ÿwvÕF NˆKå;¹šCò¶ðX§ÐƒŽ\@Üf#~Úr;º|´ŸÉ¹øãÐ-09lÿHç¯ ”………åÛóÞ« )£TË‚X¾�U¦ýf5,,,,,,,,ïÃÐ4�ÂÕl/±Ò‡JOG›$IÌ0…r9Æ0˜Fø åf}FtûíAxØø®)Mt3F—fC†úi“ÂÀA>¿=‰ç—ÔÊMW–'ì»sK¿w/||ÉÌäfNº¸$ü~=è]véÇÞ;óí É @RÎ>8fØ)ÚÙZG‘#Å‘çƒÃ@Eì®IƒÏËsâŸÅg‹1Ïuôک;L®É§ÙÂ'Ù U=[Õ6(}AüÖžj íß׸ÿ¦å\§:¾Þ¶þ/ my„ÜÁž¿ýÜQÆ7ág^=z=Ç:À ½·~I„ÛÜëClT*Åå°~,,,ÿ_@H•½�*”P… c ”šUNŒ«°Š%Ea„eši†Q9  JKKÅbWM¢Œ‘TZ®É�„¾”=;2ìýëã«…ê�� �IDATÉryÂÞ{+´n?~Æ’Ÿý´€EèÖŠe‹w\ztû:Zøt+!\Æÿy„Z¼êà•˜;×huC»fH†™Ârʈ:÷¸„æ8vþqþŒöTÑ>…âÕí3‘"˜zÍ[8Ñ£Û�¤¡oª§Q˜ŸpçV²º¦‘MÛ=OßÓYð ¹Ífì_ñnòüÍŠ„n ÷Ïo­�ÝïKùyᤎksåB«æ}ÖlæNrÿaVÞóižÓ��� ûïKÜÚåë«<XXXX¾>¨†9Aä…0`Õ6U UBD�¥î ºz�ã:,XXXXXXXXþ?ÁSçqÕxrZ¡Êi…3Xé¿I šØ0r™Œ£F˜(/I¥R@ Ip2Ò^~kÉYXXXXXT´éÚˆ ‚$‚$"”ùB„RC 26@�X™:‚ŠD1 F Œñøà!½P…º¡"½bÕ�ˆˆ¸¡¥¥ÙÒÛ§º_C­•Ôû€‰ði|Ÿ ®¿O©X¾Ø'‡å’ÿÖƒýß’öÃܽ}Ó»m;3Ê„Ï £üÀ0c†Q-™`L éççW»<B§Oš={B"—É$ZA“AˆÀˆÁ …ˆÄ"`°B!GËR†Q&>ƒÓeaaaaa© ¸šAjª˜ �+(“`@$ª¹åOX©4øÚÒ³°°°°°°°|OȤR†­À Cˆ¤H„(†a(±¸´¬¤!!L @ˆ I0Ìz¼³°°°°|_` ¸"­!VeDeÐBeZD\º” q-_ø¤¬ˆ¢î|Àˆ Öšà‹sÿîío-B|ŸR±|ÿ°OËÿ$ÿ­û¿%í¿D"e0®´©T9ibL’$%)—hj r¹ÒB“fh…\N3Ì?HvÀÂÂÂÂÂòx/¤�ÆÊ­€”º¥–àK¿ÁX§ƒoÂ÷iú}JÅòýÃ>9,ÿ“ü·ìÿ–´æî훟Œ1ˆ H€Ó �ÔH’$I 0­ËM3r$"0ÃИ¡üR! YXXXXX¾ Q‘ýPé8@’�0ʨ…É ’¨Hp€ÒÆ Â"@€Ìú°°°°°°°ü†CÆ @�” ZAQ!—“R(F3ÊðOßZl–Ú`Œ+­>¼H±Ê!eÊ,,,,,,,,,Lí<R•IBeB)Œ�!!ÀÆ&`aaaaù¾¨˜øTKIð¡ýcÀŒ20B„JkÎj XXXXXXXXê…RÈå\55@ŒBA€Òˆ�c�`¾©`,,,,,,ïƒ+#¢ùü+S`¬Ìˆ*S!�kSÀÂÂÂÂÂÂÂR?”L.QWWW(h†a”±¡†®#N Ë÷Æ�¨Â¹ "õA¥ßœRP= ËÿcPÅH ÕØ�B„T*«ØÈ�Ê= ‚u:`aaaaùÞ@•¯2¬ÊwX_r�@P‘åG¥8À•¿"Ö €…………………¥N(‚  I’�@’B˜¦¿µP,,,,,, PE¦cŒB¥PþRኀ�°2ÑJE Œa¨üŠÀX¥=G{UD:¨þTá«~…jž OˆÈÂÂÂÂÂÂÂòÝA úò4­¨ü‚1 *gNÌ0ll–ï¤ú§TTÍáQõ9}õ¹}UX‚Êý¿í°|1è’”ìŒ �XTš_,ùf&"Š¿O­Ùx%9ûåý¸œÚ(EéÛœ’ú$S|ó ºeÙÙÅÕ—ŒDѮؙÿ™ßÈ‹³rËþMy˜’×ÑÑ)ŸQ–¾{›_þ%baùŸá;oÒÜŠnü³û—/óÖøÀˆˆ��‚@3�À0 Ã0A)ó%~‘ºYXXXXX¾•!" ’¢H†a*Òô¨^vc†Á•T'DJ=¸rŸoq,_Enø”Ógž1�Pv-béâ¿ ¾ÕM•Fn™¹þiyÚÚ€.Ë£ä� xº¬¹±÷êXÑ‹Z9N¼(þPA¹Î§zz*¤RÅÇ÷úб+}Æœ©¦Ö(º´vúDJëµkX.‘~Y%ˆüætÏŸnÉ>¾ç§‚ߘÐ~Ì‘ôO–[zj˜uû5Ïä_R(–ÿ ¾óÖ!²ZÕv?ðpÁ‰ÁV#Î}†.òÝ‘„Ãå"Xe'€•9¡��&0åpJ¹+Ã0ÓEr8œ/R7 Ë—A9MÁÀ0˜a0ƒ™h´ky �¨ôŸ%€"ó¯5Ã|ôù:–̓WݪX_•§žéï «Á7òì¿îî»jB‰âvöµÒ =#­Ø O ŸÛÓÍPƒ+0mÜwÙ_ï-<09-p5ÐÐÐsî¾àrVÍèׇC,¹ÖSo¿?Â*y`l+Mu¡yË»ž‰*·þ9Ù×AW§eÙjèæ‡Åu\0YêñÑ®Ú]wç*+: Žª£øgñ{…èœk³[è5__¹ê,ŠÝ?¡½­ŽººŽ]û âjÍŠ™×Wic§«ÎÓ2oÚ¿êÒ)Áy—&ºð„ƒNKá¿Iù£ge=B:ªWn!-|†ŽÜÖ”�øÐº‹ª ¯à“ªŸhØté³/«'¨%.~w9ìšAßàfŸ4,d2¶w1ì²ý_gß||µí#m“ɹ±2¸‰©€§aèÚí§3iµ›üg—]mHV´qnÇÍo�Ñ“£|]Œø\žŽuËÐßﲪT–v-н×èÑ=]ùßܸ)ˆ¨2ae$h�‚ €$Æ I"GQB€a˜/ûÖaaaaaaù<ªFÊD‡˜apõ‡Õ‡ÕHp Ü¿â§ÚA}ZñóëÏõGì¼ùüé¹y¶O [|S �ô߇Œ8m0çj\Ìá üUÁSN`�\üâȼÀf¾s#òªì·™ä­Ã‡ž5[r7« éäÀÒ¡3ÃkÍÀ™¬?Ç Ø®yêEìùIjÜýºj†%~¸4ø§H9§Ñ%÷—O¾çùkTƒ-­ŸÏ ^pK�Š'ËC~xØb[LnÞ‹=_Í´øn%XqÒ¹åƒZ¶êMÕü\{`Xv¡’w {ƒL-zòV/$ϸ±~ŒOÓÐÝW³Å”Ý_Òêæ›¢³s_ìõ}9³ß¢{Õ§ü¸øÙµgú#wÝŽ}va¾Ýã_†.ºYõ³<aK踳żo>\údD7ÃÎA¯öU›~ûÉK'¶ÑýðIÕQðŸ€å2ù¿<àÆùÂ"Lûö÷¢>­<#“*Ø)A|¤mâ¼°I!Ûaâå×o®O× >á@&óåŠÐe¥ò¶¿%ŠÊËËËËK®N° �4,›õŸv?1%îòBÇó¦lO`Õ±°ü;¨9‡üòs_‡o¿*ÏÐ4V™XªÀc�¡üNQI’4MÓ4Í0ŒBÁ°z–ïTíz?ß!BcÀõ…åùÔXƒH¯ÛÊ?ÙÑÝΩí¨Ló“Š0(büñÔkêÊÍl:ÍY4ˆ¾ï|> 3SDm×Þ8<ܤʞ\‘ø"^½MPOmqËAne))5ïLæéý×F-›æcgÛzâ²±ÖwöOQ ÕË";îµimÍ÷E“Eýñg¦ÿÜe}Ý­\—ÌëžxÿÍr�Qü‹TÓNý:X …–t±ÊMN­±Êr“sç^:?Ã¥Ú$£¡¥­­­­­M<ú}idË¿›@'méll5äd>T”üÆ`̉+½Õ*ËÐi·n¦» šÔÍFKhÞ~æT¿œÇÉqîÑA&Ýw¤0H¯ûêÿòu·sj;zr êÒ)EñûØ š‹75ûˆÝ½üÁ9¶o¶yt÷û.}`ƒ‹².Ì=:Ù{ó°öW¬þ;K ¸(~÷Þ��Ù›½þ;·ÝR��(²þì±cӵϳ_/»À i«^m“¶¡°ýFÕ2ºüÑ?;OËÚ{äöèJƒþª‚ò”ãS}íuyžŽu÷Mñ’{³ø¶)gv8oOM—¹â{F¶¶ÔâªñõRš(^®hÆAq;mÍd�ÊãOíÒÈLSCÛ¦ÝÄÃK�çÖÖÅ\WC£nàÖ{öÒéM,µÔ5ôº/ú+O)Š"óò/½=Œù\¾±×è£oª®-Î9v˪_ jÕ^zn¸‘nÈñ�� ×¶´ý-….º÷kpcSššÀÈ}Ê¥2��Ý™fC"„ÔƒOHpñãí£;8 5ôºÎ9ŸA�|`xKGmuŽߤiè‚%º42ªó=û¯XªQšxlzg'}u®¦E«YWÞ_çÆï®Ìïâe¥«®ÆÕ´x¹¼ÎŠ˜ì« {¸h¨qµÌZ/¾/�\út÷¸vºê<mŸÑÛŸÔ\•—GͰçûïÈRnŸfh>îªä¯ÏÍïÝÔJGChÞbè¶h¥ˆ¸4f÷mOǶÊ1©þHÛ¤Ó^Ä•7ìï¡Ç×óØ·“šTòèSŠWµG�\RX„uŒ4x<Çå��È i×Nž6F:|. HÏÎN—ÍzÆò…õºÍ¢��`Ò7¶¶ÛÆÔ×:Ï6öibk(àrxÚÖ-®¸–õµ©%Íô4ÕÑàòtlÛM Kú@«Åù‘¿†¶´Ôä©ë;ùO=œP¸à`€¦Õ”[r��é_Ì ‡+�GN·Óp¢¤zñ÷zoÙÕ±&¦ã®É�$·ù6²Öp9\{ÿ)K憴¶ÕÕP×¶é0ít: �8ïÜt3mu55¾‘k—)_|Ép b©„ÕxI¹š‚1$A$¡ÜD„L&“Ëå‡CQÁ:m²°°°°|Ÿ „„‚ ˆzgûµbTÅ6¬vÏAòüúíÂÆ~>úÊãcStÜÝ•Ê�57/WúåóW @º]ço˜ÕÓAP½N³îÉSK†'½ýë×=ɾ£‚¬k Ä ÏãÁÅÓ™� ¼Ü¹ Ï_*��ÄQK¸ÐvãšnúuŒÜ™ìØØw–Ü4��ßÝ˾,îE: ¶=|r÷/Xñ¦8õÔoa¢~£ºiU/§î=mÃÂ`²ŽSdR¬<ª5áçc�„¦MÓÖ­=LÕPî£]1¢¥aõåeÒÄÅY˜pá胉BZô¶„æä¤gÊ‘º¹‡wë&ÖšÕ¯äùµ[…ý}ô�€üå†I»Ì–oµ¬K†PN&íº5bØÌ>èÞò¨‡%õìÇ”Þúùü¹<ëÑÿu[ ÃG¿nÈ”iš¹9–'=/f�èÔÌWÒ”çïh�&óí«BC·Æjõ«A_ »ÌïâÍ«¤ÙlÒ{'ÜXÕøéì>Ó/)ç¢U¥×—ýpLgNTvIþß×Ö÷µá5éÑÍàÉ¥¿ 0�ˆïE<ÔêЩQñ‰ùÓï¸ox–_’ý"|‘¿1�@ºüUZ^^^ryœ!¾=¿Ï„[ŽKo$§ÞZdyuüè-‰4`QZtŒpÔŤÌôèí7¬‹iºîj\Ò“]^¯™üû�´uð€]Ò<INˆXd]µ¾Åd…½kß¿¿+‰ jÕ.l×Ó—¸})R�8ÿvÄK__ó´=3&u>’XXœñøø-ù��Ài³îoQyyyÑ¡ ..8;¹÷üWí6?HM<7ž<8ü‡ÃÙpÑ«G±6ó¦e¥Þ]á¹f{vÏ·^Ý]éúpþŒÝ© €ìÉò~#ÏêN;ó"åÅùE~Fï7\’p'’têïŒ7q׿·áÖU‘âñïS6”»š^T˜zwÿ7 ð»óSgF{­¾ñ÷«ÈõÞ/çL:]Ý!†Ó¤«ŸÎ“¿"K��d¯ßÁíº´&_¬ |T0å\Âë'›[>ûiÈŠGr€Òë³ûüøÀuéõ„ä'G§µüX(‡´MʵkWÓ›«gzñ.ÿÁo[xŒ ­aÐñ)Å«·G&?'ŸN<µlÉoû/¿,¬¦ÊÞáÏç›4{·ùê•}Œþ»>,ßõ´&'ö~FÓU“Òâonì–¿)¨Ïšç_7d×küŸw²²ÃGQaçªÏцy³oDŸÕo»ï|”pe¡íñso‰u||=óïßK¦ w¢r Ý©� S£¢òšú¶©f…÷^ÿY½Sd>{(éy0!#ãåñ²½Ë¯šÍ?“ôâÄ éÞIK¯—`QÊãhþ°3/S“£Oüh1±Ûij_.¤,—«zÿA&��Áåry<žL&�‚�e~DšþÿçDÆÂÂÂÂòŸ@i?€Õ¦ýXù‡?äóˆP OƒOWÈSÃÆ†îÑ_¼c²3 X\ZÆðå[ „|(+©OÕô—,j´c‚¿£]·íРɽ, œ¿·Ÿ¢(ŠRo¹*¡¤TÄòUóÄjÈJËd�Š—›gl´ôߪɇ<j¦—¢(Š£34ü]©øÂB”–a ¬‡­šaó`Ý0[—Ag ‡ï ×г–ÞÛ¼éEÇ“¼”sFdØ}åé°Y­ùõì.ì¹ú)‚#½m5F–EPê<„mæ=µ¬‹~•ЩGÆ Üm°dçdg�˜Œ?ælãÏZÕϸ«–H[ÛÂBCCSèìb//ÌÊ®{¨‚³o>äûÏjîi+ÐwuüƒèB\œLèÞZ'óaF Æ…O2%öºâ' ”>y“ioå¦ÛÀ‹Rg}…—îj…´üªròíÓÖÙʺɀՋz‹Oº!®YhjˆÒ^ÄçÈ5 MyÀõÐÏðö‰‹ydÑ‘¤_Ö<ŽPÈ-LŠI|ÇL\ì U7‘jëÁ’[åv[°¼·£‘çà9Ãìž^ŽPš u=Sc—^£ûØÓ|«ÆNæf.½FX¥?-ÁtÊé#m'®íïlnáÚ¡{Kã • “q&ì¾Kp?GÐ{µkuØ.Ÿ¸Y ŠŠxlÒµ»Å ¨¼„§©%„¶…‹Êã”JD¿»¸ï 9hñ|?k}³Ö“f÷åߺ¥ŒxN ÌŒ ̼BGvÕ“kÛ6³7³ð æ/xùì¥äÑaaI­gÿ>¾­½™•g×.^Z€þ{u+uŠ¢(Šß}O.�B`bknhhag&(¬«"Ä×(2_>Ï(WÓµv²"\xqÏI´jU?OKK÷À¥ëFhœÝ^}�ÎkÓ»«úÍs7KäOÏ_)íØ»#ï顃±Íg¬âadà8oB‹×W¯¾¢%·(éù˺ÁͬÍl›÷îäü/µM~»9«KŽÌìånÓvIzû)\8Ÿ]¼Z{$ »Íß4ÙGPø`ëðfîÛT“3d<îJÑ»ÔÈMÞO&,¼'ùðI°°4ˆµJ×ÂÖÂÌÆ+`áÞÍc÷|ôUõHϾ‘‘¶ŽEÛ C[É^%¼©ÛLI;¾7Âtâï󻺘[5 ýuYŸ¢C{#$–üã#nåb&óÖí27×âÛ·Òœ;"Þ½KGr_Õ›ýo¢®Þ»š\cc#;ß±šp¸¦În–¦ö¾£ƒ=J^<ÏPJDðm­Ì,œ|ÆlÝ0”<½+ü‹é H‚¬î±‰Ì0X¡P(hšàp8$Iª©©q8�¤PЇ£¦¯ÿ9/M–/* ¢*’¡ÒF®JSðžàý†UJ„ÏÐ$ÖyzúàcG'»q�i„¨¬L9gÅe¥"h ê9¼"ö×Á¿”ÿ•œ–ù:r•ÍÉàÛR°NÐÖG11111¶Ñò%e"ÕH‹JÅj.8»|+¿ P¿Úq9^3/DÇÄÄÄÄD­è¨#䃨TT]¡�8rAè“OSÓ²’®L£6Ž=ž×°±Eùí?ŽwhÐÐ Eû/ ‘#’IŠ7wáS.µçIòä?‡úýøzȉcÊKå‘ëÖ¤ö_4̪!¦ÍLÞ»ëBöŽóÞ:6(2AŽë3zdòÊ AÓÐTuLŽ©–Ž´ì])2mk­÷:®¸<î^©ûØf®™é±ï$/ïæ·µ2ø Ój\p!ì¯ágie$Íy[Œkä´]rfSû¤yÞ–~S</Å Öbh¨Í­ƒ'ÞHŸ_º&ö lÏÍÀõg9ÝïiîÚk÷Â=âÒ¬¬ââ“¡†<Çã7[G½{WC™BhjkX\�€„ZB(/—�““•æVæïÙs0i§Ž>ñîkOÔQ»Ðx_Þùƒ߉#/ÞÒîØ„C˜Üs|’æÑ`g‹Æ!+®e¾7âfò2²$;ºiñx<O³ËŽ,Yá»Òw’ÐÔ–‹Ê•Ú ¡¦@*–�09os5,,k΄I›Q‡ÇÄÄÄÄ<ÚTKÿUwE„ÛôË6t±µö¹é^frßdÒvÖ*eeg‰³2j( 4Ú‡ôâ^;y£Tþôô¹B¿.:ÌÛŒ·’›Sí4x<OÝfêmyQÁ;ºôm¶ØÀÊBÈGÚ&“¾oô¤¸Ðk¯R32¢wû<¸ü©üËdÐ4hø¸©?ÿ~ôÞýu®·¯½^© x:ÖÞc6ÌñI9rôëNÙXþG¨Ý7ãµÂÈÑA3ûMÖ× ˆ!O;;7 ‰µ–PÏ}öY½ëätVFXÙY©^j<k[“òÌÌ"Â¥{7ËÇW" ò"®¥ûý<§c굿r o\~lß½«­^õ7»ÎGzoÊZ$Æ�€„šBˆßKÉup¶fjuSŸƒH$ÂAIQŠC$Ááp(’¤Äb1S-ò3Ÿ¯¡§§«©©Ùƒ‚¤””ÒÒ²‚üü/$'ËWBO__K(´µµùðnìýýÂÞßïŸÞ£Í7^%d—>JÉý:R}+šÛº™hMè`ÿÑ=+Í” @™¼§Vhå>�Påh僑 .º9·ïŒ×ƒN]œï]±®¯îân[x3ö-ãkE‚,.&žt v¬{%‘~uöØ3×Ñ'\x€x­&Î ÙìñP<i€•«¶jFÍö=WôoBø,Vê<Ð…’Ý?w)ýîagjVÅ‘:…¿=ÐÓÍXõ]æî®›þ"®{è ÅÆ$ ÜY’òÇ'N¼iµ®Ÿ%`ÞiîŒî›Çþ#öÿ¸‰½üñ…«¢«; ?ºç{(R¬ ËõœäTcÚ‰‹nüÔgzZè™Ë *.üÙ¹ I±I­xk*v Ò*Þ—u~x]qÿDÉa¿Ä“SúüÞGO½,þ×.O꫟0èà79Y 8� Ï**äòu„@êÚ5Ó}r=‰N6ñim%q¿óðzç©°Ù„ÏqÀƹçÞ4ë·°ÁþDɉÙê¦fÚ{¬zA¤é9ì· C½<2­ï˜B÷çË›ºžÔfãæ‡ÒÏ—¬õ�aàýÃöˆ Ëo4d¤‘×Íq$ 2©¬Â³F`h(Ô¼/u_Ïê!™´j_¤l=HÙR0Ƥ‰¹ ¾þ*Ukœ|òhLãA¨übÞ«}šm»‰c¬ÛîØsÈöšNðÉ–j�@™wžóGçéY7Wé<Ù*éxŠÂU"úƆ\»i×âÖ´ª®VQdÔ±J‰Wñ‘417ßOzË@u•’š¾­›~åiÖZOE�ŽAKOÍM=¿ dP¿¹NI«ÌMÉ7Éi2°ã€"-ù521¯éÒ ÞnH°fPع«Æ§E½ÖwÑÂÐØßu~ʹáÕÕv’RSANRr nñ‘°•JäO>Ø6qÞ•£7 W6" ^0j[“ wÞþÜØ’ø"Å«Cšy¸ë•&æˆ1T"ª ·À: ³üC(>ŸS˜W@Tõ&HhÚ€Ö‹32Êôí ?êöå(9;gäjÅÕÄQîš%z[®«oGÒÔÜ_Oz­�m �$iÉoÕMMµehÒ=üäIE\ë!þËšÏ<yê/Ò²÷"g’$«Þì�P»ÿœXgEƒÕ€÷•.tfzŽv»&AI’IHˆ ªº…<žŸÏ×Ôr¹<¾€Ÿ••ùác&¥¤ ‚èàë÷¥„dùšD?~˜œ’jWÿ,…½¿ÿiØûûýóÑ{´ùÆ«r†<8©Û×”ê[±àXÔ–›‰“:8|tOŒ1üþ8Ce8 Ìg@Ó�@’$ ïE7Ä”“£÷_¾‡ŽÛ<sŸö¬;³›p¥ B©ÆåPî‡4^¿qþ¶ËÚ•Z|D°£—~ÝC!ÂÔʼn¿þøž[ݧ¶¼9¿ÿò[»¾Î5<Ú ³>Ãýý´pS×½©K w¤ù,¶#¹ûß1û•{È®Žµ§öjC»êÓ5ï!¡¦Wþ|Úc¶KÊú•u†wPÒÚÅ:´ïQ›a®èïÃÜ–9ϰoÈ|–N‰º—ëÚ²JK€s/-˜ð‡îì]3ZÖãwÀHKòÞÄÞ<´zÞêGî¿\žâB”E­ý[Ù¨‹ýµc7ÍØ«=ûîOM¸R‰�‘j\N«µ‰ŠµÊÒŠ'ó=;¥.Ë9܇[ŸP �X.Up*ó[ ŠËeròÅí8< ò ÒrhSS‡-‡­{ä8ÏÍT’yfsŠF÷^n<�0håÏ_ºé>¯ïx>OÞÉøÏµQ´Yã ÛÏð¿ÆoÃÃîX÷[ᡚìkðÕåI1Ï‹z5¯g’)ÎJÍÊÕÅŸZ8÷¼Þ Ôß®^Éxr§ÐØÓVËÀÝÍŒs­¸fg XÙ{ò“ñ—;ò@šòà¡ÔÊÓ’oæébˆ/—a޵yÖ•“IV.’™¥W‡!&2…¥�� �IDATÁ‚¾?ÏðÕŸág+漑˜6sÖûèI6ýGuZ5kÒ͓ۙ1éyÊ¥|úÕñ£/[Œêc¡¾_;�é2z¦ÿú‘Ó»ýüÀ‹€KîÅsÝLø6:²Äb Ò³±$\<ñ d¸ñ»<ÒÅ£ûnó&Ï^Øjý8os5QV6Ø5¶û¸2Šj2x„çÖ•ãVØý:¼™ž$£@†ë}N”wB¯®ŠøùÏ£Þh¹:ék;4²âï-.Vèô4o蜹öLoA>Ý2{_yÀÎ^•ih@Ö‹§™K·Õˆ!æmfã œö�@Óƒì:®˜²Ýza€«“ŸV¬ÓÜÝ„×aäP“® Æo6^Ö×UP’UWîÑjÔÝ6™´°)?^óZ¾c´“‹Qrø¾Kv2(¸µ÷t‚^c7¢²Auú¼âþÚ©7ϾÖõrÔƒÜ'. Ëo2Ë[ 1™·ŽÞ#=›ÛÄÉ—W®¹eÒkFÓo‡å¿§qÛæâ…ë7 ²uR“'Æ��h¸ìÅõËÑvMtò®-Z}Ó<xA‹¯ùÌ1 ©¤\ÆT¾Vªºqne?`Ýo¤ïʹӗ{oå,¾½bÁ­Aá¾�àÒϤýü_„]wìÓÔ•µ˜1}¾ÜfúB—ZÊŽºúÏ~÷Âÿ¶8nϼ…Öö0DHôñb €¢(uuueDÒ¯“ahŒ`†$IMM¡žžž›»«Ž¶NVf¦B!—H>âTZRÚ¸I ¥ 'û÷ŸûkÜ´EII}áŸØûûŸÿcïï÷ÿ÷Ñ{”ð¶tq¿V Æÿþ–ôoŸõ¡«QÉûq̨´X僀Bèzö¡ ÿ8ÿÞÝøâ;³=4ù|>Ÿ¯¡¡¸/é4å½ÙË;¹x„ÓutcŸú� íÞ¿›i>´‘¾¶yû…ɶþ©Ö24a:xÇ‘ñhg ›k·åCíÝ ‹|àµú娆O¦¶rl>>Ò}íñåíù�„Ũ]¡wÚëèØõØ$ 9°{œMC§H|™¤aïX-b�S’ò(22渾 '»:ÙÕ±ÍÐuL'žzxyN3 �À¢ô§w#¥cœ/òeñYîu%Zûèþ ‚oÛo¶MñÞ“ÛlÕõvª‰®ž:R¯M¨äè¥ãOnëF],³,yYŒ„í—õ 0HÛºƸû¹Í}gN3ç�–=]ÌË‘[gKu�¡ƒ3-· p6ýŒ5&ãÌÑ(‡þý]UCBÊ}ÐÔ^¢­FV³Õ&tœš¹$¯himjÕxÀö² V¶W¯Y—<;8­³“¡¦¶•ÿ.bôÆ™>��~‡Éc\°í ¡-Õ�€É‰Ú<®­ž¦®CðyóŸÖoDRM'­­q´Ÿ«µ[·é§’h¡ßÚs[Ú¥¬ ô´2³i0ãDbƒâ†–#ž_âòpŽŸ“™™Ë “ÐÄÓB‰;v,±uHoUŠ:j�dÐ{Ê`¢ÙÐŽ$�0éW—‡67×zLŽn¶bE¨)Á󛽺wÉF{›&ý–^ËdŒBv†/vŒú©³«…™C«àÅW2d:K¹Ï<yz²vøxo;S‹F“ïè·p3þà¢#ª«"EâéŸz{˜hi™´^šÝsýžB¤ÛkÓ¹uM¢g·wph35ÒeEøÖ¾@Ðyüd‡»SÆîÏÆ�¤ëˆñ­K lGiÉ�Pk:ÿôŸýÅ;·°5³tï<qï³2�Ðh»ìüŸ!ÒÝ¡M­Í¬›.Nplîô;•ºÛ&“Ÿu'26[\ŸÅÇÖx<œÒÂHÓ¨éÄ;NK-÷U¯jPŸY—&_ß2©‹§µ[§©µ'?2Å‘&ïùñ¡m­í[ ØTÐ}×¹Õ¾õÅ"aa©Â|ئmAeÛz7²²pè¾»ØÃËš>Ô:˜Ü¿zÙ¹t]öºÓŽS‹[ ìG3`ñú9«Û[j µ¬§G[9›óQõn¼ª@–#÷Ÿžcrqt ;gÿE)·‡¯ê ��  æ.&|ƒý5é÷öQˆ½† rªÕ;ÕÝþ°$þÀ8'»æ#Ïèýx|×P3@ðE.‚\.§1ƒ ’$9$H¦P”‰ÅE%EÈÁÁžÇãåçç‰ÅbŽÕ¾}{Ì`±XÌQS;¸g÷zý¯ˆ ~!_D>–o©Gý:ùÖ÷+{ÿë°÷÷ûçÃ÷¨åòK±k‡|My¾-î³þx0ÿ#¦m»u!HB™á€ Bˆ Œ€@R~E !cåÚ€ê®p:`0ƒ¡Ü:¶ÿ€À�T¥\@P-¢òÿˆˆ--­–Þ>•Š÷?TòyÙXjsÿîíVmÚ}k)jS%“º¡£gXÏÇ‘³ÿ٘¸¸Ó9W~ù1Îð_?â¼"zW§˜ çFÔS·¢¬XŒd©Ç¦õ_©½õÉ¿OðOùÿÆ÷ù<³°|&Ÿþ`Ë®ŽµŽ¦íh€'Ü—â?Ø ™´ êwcJ-·¡»·oz·mÇ`F¹üÁ0Ê ƒ1fÀ˜Á Ƙ@(22ÒÏï= b„NŸ:õÃÔÉ|@*“–K$2™ !¤¦ÆáryjjJCC£¸øÿØ;ë€*–.€ŸÙÝ{¹Ä¥CºAB0±PQlìn}v}vwë{¶Ïîîn±°»Å�Q”Î{wæûco÷µž¨ûûöù]vwfÎΙÙ93s&•¦i##cS£;wîXZXêëë „ÅÏù ßc¡'O‰…×ïï ¯ß_‚¯óþõøöÎ57;�¦Ux¡*¢EBø=Ï7‚_íß}ǿͷ/*6 ŽÝÒÖoÐE¡W£‘ÛV·ý»ÒI¢ví{4¾‰¥¦´¥…TŸöب\ëÉ»çñ6ž_’œÜ\)Ër‹9E"Ã0 CÓ4 �Œ»»;�ääd'''ëꉒ“bbc4úÎ!ð5k;yJÅ·:xýþÊðú-ùh¥£¯›ÿ òý Yn"̽!BœI@ãd_Å€ÿ“·<? Êmø•´á?" åÐçdJŸ¯•ë{ ˜ûäsQ70þneLø¯Äáááù Ö[ýáýÏ¢äC9 ¹”>äGÅÎ0 Ƙ0:"¡P‡¦(ã¼¼<‰$‰ÉÎα´´HMMMKOÍÎÉÖÓÕ«Z£Z||¼Vqó­¬_-Ûä¼~Qxý–|´ÐþÚ%ô¿_7XŠT~©îv@À²……йl‹Œù ÀÃÃÃÃÃÃó'c(³ kI$)BB C3Œ.#ìí._¾T¶lÙçÏŸ—ñõ'„‹{—’¬7)ÂÏ[þEÑ®QÎë÷W…×oÉG}µ§½? B"òÙrsAÁ=óݼ€‡‡‡‡‡‡‡'O"!(„(Š¢iQˆ ³,fìììÞ½{§§§ohhÈ0‚ŒŒt)+yúôi^®Þq9‡Ý<¿+¼~oxý–xx3AAÔõý¹]ägü„sR ˆ ”vDQ@´ò°ÎÃÃÃÃÃÃÃó[Â0Lž$P”¬ÍIç ‘`̼}û677×ÑÑáéÓgy’œÈ¨—:Ban®T__¯Øx¿  ›q}éÐ¥YÝ6Ž�ùxzö˜½¦CWôõÓf÷fžŸEÉê¢ä=Þ={ÃçÐi}«èþlQ~þ#ýòŠûþã:(}¸qØ¼× Mi`ªí"�’±rE\íÁmK«ß6+çõáŇu{ ®kñ|°¡Â~à>j@0ÆÜN ²[!€`‚D!�D#„ Á,[‚Þo<<<<<<<<ÿ9RV* �a1–åüb!�*66ÖØØ˜eÙW¯¢“’’I$� im6&Z$ûÉ©='î'²�ð§;G÷Ÿ{–L´þ ™±·N¾÷IùŒÒ;³í<þ:ü‰ütÙÇê7ûñ¶!õ}lM Äfö —>e¤lyî=~ó«’úÝ] מ}-)tsÆùþÖåÆ^ÎùéùYÒŽŸ¡ß_Gqì«5Mœ[lxƒK°Ž!ø?<$¯.ì9z+.Gë 8õü侓ÜOÖx¤?9>gÀèɸ訴챂 æ¶9”M†!¸‘rOCÙŸR©”Å��Q4MQMÉ}r±ñ;ðððððððüáŒ1Æ\ëˆkCQÜ"SS³/^Þ¸q3;;';+W –öò02§¥e)Qwd^úŸ—XÏwô•ì|ç Ñü÷8ò®èÞvô©â ظy¸;šK̃¡~Ù‹zößZ.ÜwöÜ‘MS;Q?P6iÌÎ-ûo|$-,p¡›C;77[Ã)ϯxüýþBŠ]Kg =(Ñ:Ò‚ÌK£| MýG_ËÖêö¢Ð>QB!Ò§«§m“¶1*@"¹?½’™¾‘ì0ª<펄}Îé¦wpƲ¨\­/6G”? ¤º˜@Þçç¢" ° Š¢d»!¨ä«bºÏ &!@0–-ÆäÚNÌ»wï>N¦(�@îînR)ûæuLVV¶›~P$êÚvøý®…ÛR,Ìs·,Üû¿*e›ýÊÚmÜý²¦(hÝ0ü‘=šJQ®]ÖŸí¢¼ô O¿äãµËOuC·m¤£¸ü“VJÿBád…åï9?ø ô;ò#ôû )Y5Yxº ü´ôµ‚@±ÖüaÏ¢©f9[—ìÿ_@ûoÚ_(_ÎÚÜž{míú‡žÝ—×s/øô´ ¦æ”c³êé� ‘•M€ˆ*ýÕ»Òº«Ã‡¯ U¿0�€h)·ÜO!Bâþ¡ÆDQÀ0(¿p€û Û‚1gY@ÀO'àááááááùÓáZPŠáYS €ÉÎÎhBˆ¹¹yvvR)B(//O›X 6'%÷×.¿äØ÷ФÝ;-[ÿ¨Í_F~§âþMQòùæêÉWº›käÔ~äŒ ­<ô¤fT¯¹ÜgóËõMôHâ¦f^cÖ¾ØÜL€Ä¯mê5^¼òÙ¶¶Æ*¼ŒÇ;gNúg÷åç)”±cИmÛúzѹ¯ŽÌ?gKøÓdM¹†½§ÎhI€ôáÚžÃÖÜxö6>%Ø•m4pÞ¼>•Œ¤“zÍ8ò(:.)S*4s«ÑqâüÉM9“IÖ³½Ó&,>tíY²ª6lþìnþb�@’o­:yÅÁo3æîa3výÛœ�HÏ©.ž�‚ŠÓo^Ê,¯Wq¾ç®èå!B�’ñhÇ´‰ÎÐsªÜ|Èô©ÝÊ¢"’¹fĨ%GïÄåèX¸ÔµmK¯Òô7”mPÕ/ÎÊÊÆÉ[[o�ʼÛþè¿M×ö¿áêÃè„ ;÷Üte~°®† ǯwë¿ðÜã×’³‰¾}@»þm¬îîÙ}62:…± h7uÙìÖn‚Iã„uMLÖ�í<øtÔ\owmVmÏá¯rõ*µµ`F'o}�iä¤Juކ…ßœRžÉ~²cü°¹ûn¼IgLÊ÷_sdd%2Rüf÷°~ Ï>|õ!%—6r,ÚuÌ´ÁÁ¶ �I¾0à´7¿MÊ¢MÊ ÛsnLZƒŽ� ÷õ±¹çn9÷(‹íÊõ]{hLMÅCH’·G¦Ÿºõòód0´õî¼èÈ샬Ku­ßå! 9À>ß>døòs÷_¾O‘êÚ6_ziMksåHîPœ†ÂÏ~¼¼|ÂôáwßfxÔî>eþØP;FýÍêâOÛÙÚy Zñfw[�Цèê–ò®Ósúµ¬´Y’õ}TTœ5Urÿ—ì{ߣûò [ö–½r“ÎLí=çðý—qŸ²ÁÀÆ»V›aS‡7qÑ€ìë‹zŒÛq÷ElbYºWoÑÒø¶>Hùž&DrgFõ:›Êm»¿².�@îùÁ>íïô>a”—âµ#¹uäx¼w&®'#›œœªgïë_¦Œ¢2B�m£° 繜U/T£ -M5šö/}Ø_8„�€fh‚e+�!ä˾‚W/ž} Ížoäú•K?[5”L©xJ>|Éáù-ùµ ö¯%í(æ[*Ú0\ÃŽÉÍÍc!ÿ‘»@Ó4Bˆ¦‹ïƒr~Õ3VoŽ šÔ«J½OÝ=kn^{eР=P.ÃåîWµä=ZԦɌ„Z£ì ¿;µhr¯†oñÅÍm<kYÏ?yý¤qedÞŒ¸›iróQ^³JȺ}ý”_ÝP9­pÌæîE–î;~ílWÔ˜,'{ 'Ԩ˱Rݧ¬Ÿî”soËŒéaÍRNž›PQØ‘çnæ5Z´©‹ ãÅÑùÇ´cœî¬“ôç×.¿´ùßšjˆ¥ 7ÖOݳ—ÅÕSCÜi¿¿oã~‘•ÆÌÞUÓ,áÔÜ‘ÃZaëÛ+B AòxqëÆÓ?T2cKM[*)—±Bð€ví¹a{_O®•ï•Oc6uk4ø®ßé›k[%_Y9}hãçYöwg4 &½=·Ç˜p÷ñkOԱĉæÖ”ú¶îÚµënÞ¼UàdÕªUºwïV¬B‹Ö/dÒlñññU€h&ïHøáët—»Z82Ó |…š3œýôðâÕ$¿[V”ÓI»¿qâ¸Q£\Z½r”yνuã¦ôRºò‘^öT¾¤2k±ìиÊ@:fN ‘´UÕÞZ»f=Ù5cRÿŽ7¦U*‹“ôŪ~ƒv ú->ÜÒU˜òúÈžÊ×#`“^¼šè;yýâ²z¹ï®lž7«Uø½Ô6")÷NŸxh1påßõ,!5ÛÒÂo5éˆ$ܨóa³ãWLò6ÌzÿÉÜY@X ÅC_H8a÷ˆ^ÿ~h³h÷*_ýÌØW¬»ˆh±Lý¿Ö¯æ¾½|ðrFݹ»úä~b¼L•5ñ‡(N}áϽ3¯U‹L—™«¦{âû&ŒíÒÍ0âä`ç;…oÖ¿Ê‹(éxQE7Ñwê†%eusb—OšÙi Ý­]JÑüŽ:Âp‘ÚÌ k¶Åí^%øs7àM® š[S��§?»vé¥Íeó $ïí_´°[ƒèÕçWµ°¦òÞÜ ¿«×mÕ¦æðùɉó7z”qf7šÓ#‚iïàZÖK‹Ì­_M }rífJ© ê.ÁrqØ77o4+WÁ ‚€¤}LÌÓ·ÉŒùjge(TÍ ËŠìÓOÞ|–WÏ_£ÛZí¦pZ#@Á!�( ÉþGQ@d~ B,Ë‚ì“#3P"Áòu _Ð¥¯\­†âóYø‡ÞLð}¹~åR•ê5¶)™Rñ”|ø’Ãó[òkì_KÚ¢¹réÂw‰R‡!òv=# ) ±,�†ahšÎÍÍEììlе€.òñàšC¸ÉÚfVˆ±èÔ«ú¢I«O¨ÑÊ ©Ü¦ÂýÈ<·xQ¤uß“Gˆ� f%ƒØ =ç¯Ð|B¹Ú5ÖF\~ÅVöÀ÷.]'FqW¯¼‘VrÇw/\ÍôîTJ%ZÉ­Õ Î0í·ïœÓÀPþìóUów§ÔY¾ K)P»¦{VšË¸µ•1�ÊÜ7(¤–5-¢ÏÕÛs*27´@@è\9´vM!@P¹ÜÞCOžOäfõlÓ¢ÃtÇýk†ÕÖ€òvqËÍݱ ´!>·xQ¤MßSÛ&V)²B@ÐÜÙËK6¶Xùà’›«œ´Û¶ejCCP»j©¤€ÿ,»ÜcQmJ“`$1!‘˜5¯U+À_ ¬"3 Ñ«gÏøøø˜˜XÅûîݺiÝÕ _@€6vðòò– ¦�m_¹qH ,ÃÿÑ”á�€ÄnÕj–g ZéÄGéÔíÖº®1‚Îo›~ùVV/û‚’c/o/ùÀd�Ðεچ�Ô¬ÌÜ9ÑáÌé§“*•QJˆ?%$²†A5êTó7Bà_I]!CÏ zÁå€ZuôB«Îþg÷Р¿l�Òq«Ñ¨Vu.=ɹtT3vË‚]ÉÁ‹Ï-é¢ì*²O4ºjDbSâ?æˆªÖ ,oE_P'§&þ3ýJnÌ*²”Z–¯W/ȉRWò½'UWøÓO/Yñ¤ÂøÈyí)€€2$êtû½‡_  æf‰Úø•/"öŦ"‹®¡GZå€@÷¤K³_Ëlß¼ˆéßQGÅÌ& ¯;„­nl´E§®ÕÏ\wlT`KS$3É «„ÔÔ ©nž\eø?›ž6íEÚ¦|hpM€ Úµsªu^²êjÇy \&(Û žåú³§ïåU­(À 7®¿2¨:ÉŸQ‘FúîÍ;°ml#›K�lšDdKN Ü–f>¡MŸù¿`kîõGÙØÛAÜ›–øi2?k¹òCþCò0PHöC€ÅõÔ À,&�’Y ç?!„ïÔóððððððüÉ ˆ(WbÊmˆ"ggçäææ … B$7/×Ô̤b¥òöŽöÅGJò9»b_ïÞxÞ¬u× B²nÙ£szÓ¾XVÙÒÍ窊B$/#ï¤W òב]0©QÛŠŽº›‚u«…ÖÖ{~1ž•>=)£Îÿúú> ¿˜€%Ï_Hp ­ëB©¤C—¯U]¬"PνÈGħV y³×³f ëœ{‘O% qdWhw'*91I*N~ ]Ü, )!‰%9ï=Í‹ÛÔÂÞÜÄÌÜÄ̲ÜÄ›yYïã’±äåí¨Tã*A~:$?*ñä?ÃÆß¹K—¯(—V/ v€(éΩfÁ„5û ¯š°¨AÅFçí‰JÈ#E0~Ü8CC17Vlh(?nœº»´(4êåÏÃe¸,Wml ؤ„O˜B…¥%MNNÇjÄÓ|Fèäf>}üÄ*.B˜²]‡Õ#ÛÛ÷œ±åJ¬:‡nªqÐ.uê¸Iîß~(PùtEè(çî­‡ªÉ=»¦â¡N$ʵõvçúU®Ùaüš³Ñž¼Xþýj_JÕ¥«ùÌ×(N]á—¼¼÷ -'bL933s3s»v[¥ñqXu7kˆ_!˜¶E—P¶n.:’OSXµÏ®äûé¨(Ø×{¶^4kÑ9HŸ‚J…um@ŸÙtè\8ÕðȪfˆzu?½à0ª øx÷Nœb@.×Z4²Ž=uì~!é7"èWåK>++DzºŠÌqê¶åvÔÓØ×/nnêöðï]?’ÈÒÐÕ!IVfÑ^ ‹Ïù$�™)€³¨dç‡Å´E(îÛ‡1Á,«²Q¢ìª6Ióðððððððü®¨º ”íw aY‚�( �–5µ0ññ÷5µ4Ï“J´ŠNyHîmÝr3+zIˆ…¾ØP_ldÓqObö•M;ŸIAÙh+蜀pZ s,E€ƒš k o ÿø&üll@h‡Æu¢NžOxyöt´C½úÞ´jêc�H5åX¡jºù]ŒËÎÓŒ�‹‰2”ü6ša�c–�ÆŸþ»""®]¸v5âÚõë‘×V´²”§?iùTV5ò(tPØåy‘‚éVøß¡»7¶ öý°e`°È”Ëi¸êóæÍ0#˜7o®¦{¾P¿rFÍ™â2\qR ‹¥˜{F!à ûå™ ¢“0 `–͹нÛö›QÆÕÈ9:ºQ…ªƒÆç¶@a QH]äEéˆÓwÇÔ\<Ô‰„ìÂV\‰:5·±nÄœV•:n{%լʟ¦_­KéWœºÂO0A†œ—eøÕ+7oÝ81ª£öf ñ+Óºèša€eÙÿJGPT·Zò`û–Ȭèõ­,M-MMÜ»îIʾºm×s…ÁSŘˆ¢�ÆÜ“•˜"€/@ÙiaÅ–ÍÞÚ'7ëÆ¹«lÕzâ|â€HWr² Yãc·š}­Ï<<r<šgeåFWWP„€h‘�0!˜B�@„ ,ÆD��L€%¢�� „`ù¾—ˆ¢Eшf(J6†·ðððððððüÐÅÍÈTx.ySPÖH ©ÔÍõVpmFHNù,Ô)~§ƒ|ˆìk[wF—°3"âòUÙq~U‹GÛ·EæÊŸ˜å:†`ÌB—²~â”k—îåÊz[Ÿ¯\¼‡ýÊ€aÝÁ«Ön9ú¼bƒ` ¯z!W­Ûzø±SX3ÿ|VdåãcÅF^ˆHU9)ô-[]Œø(KTòôÒÕ:eʺ3\  ÕT~îéx—qƒW÷ßè»z¸»Ë7#Š0.¾>z)W/ÞËÉ×þééBzJj¾ÎŽ,*dåïgËF]¼’&;ŸyëâÍÓ2eì©"#ÈЭNïÙ»¯êð`õšðüN:–-]¼léb7h5ZC'Qý™¢3\õA öÖóÿI€ÐÑAfJªDsr*æSŸ®cõNS¶\¸4½\ÌöUGp¡i±ñׯE#Ï2t¡È‹Ð‘ŽŸ›êc)ªxhIXªRë‘kÎ]^Q/óÄŠÝϤ¤`wü`ý~I)ýW¨ð3.¾Þú™ž$Û)2ÜÝÍÙB¤±¦¨_&É]MÏþt„ Ñxä\Û¹ç•G¿í§/\<+;Î-neñx綨\‚åvÙÍ8åöµGÄÑÛC_áì@OvÔ»y&¥½K!‚Á,ƘLŸŽýã÷í8}öäùìªCL Ÿ�`ã`CâbÞIÕŠ'3ørJßÅĽ ñ8D›«RpeÆ�µ¸¸­ÕÄ �Eã�� �IDATúB�õ‘ððððððððüÞp;@Aþ†làcLQ”ŽžNP•ZEż}«'Ö …é™ÅÇ«ÒÐʼ°ã@¼gïNuý½•‹OÝ;6˜¶ïÖˆ±•ª˜™‰¤×ì¼]¥}+c3S”tõБG¾aÞ!ƒ–­7¯O/ãI=*êÇY2ù0]Y?A†uZ×uû{™NíêY�cÒ´±e£…Ë¥ž#—úÒ$_[På¯AUwØ^<¦_¨‡±4)Ê´ é4¬åÚã:“Žhì”soóÌ%Ï=ûþÓÈDÑCQí±*þÌ7¨ø—Ð¥;j¸®ß¤v3†¶(o%Èx÷ä³KÇbd:°·WÃÅ=:Ò£{9éåÄ'èUmèèï+Ž?øÏü@¨—KWjWK³ Rïaµv ÒÝzê€Ú–ÉWVMݘ\iJ¿:ò4 &}vxM¤ÐÏËZ/ïýÅGŸÁÈÌ„þ’vuaåiq‹jCº@ÎäËîoÚ]c†Kóe¸lʺ,ªÂÝF�@æ¾~69—NßaØÈ"õU®gû†¶ù’“…Íov`cÎlÏóôµ7À‰‘w?²º~¦"¤-!ÀÆ_ü·sË öÔÛcO»,n¼¦¥UPï�ŒFÑž†4Z×wt›!ÉCÃüÌ©”·ÙÎ-zk($¶°Htyë¾Dûr.&TÚã19Œ‰©à›´©FyZÜR¤~5ç@ÁRªÊQ«®ð„ìåÕpIŸ¢Q½‚] %I/_ kölà jnÖ¿¢vk[tÕfÝW£õdµ—2/î<ïÙ­}ˆ¯rïpmWÎÁ}»®Œ._“BÒ.®š¿>­Š‹îÇK+ç–T™Ñчæb”ÞÙ<s ªåiœµyö¦ïÁݪ ‘›šógö\­Ó³š¢Ût­³pôر:™Õ'×5ƒü¢PöåËšºõVZÝ•Üٺ躰¬·­I~q~Ý¢ã¬ÿ¸†Î!ðǨ¨ƒ²=iM ùŠú<Aq ,л—9"�$sZ ‹VÅã Å ŠVð–ž? B�B�!Å �0B¡ÀÎÎÞÑÙ!�DD"Qff¦P ‹µÙ¡MÑçK>µãxréþ=)ÕÆ¯¨rÓ†–›öo;3½vÈ  Λ<ûtÃ=],š ~dð¿#7XÕ°ÌÐíûE“'¯Úzf®¡KÕÖ+MjcCq‘ˆƒ;·(µgG™V ÌË.Ë›têàElb3n}·Ô›>}ùœ>k>Iõ¬}º,nR·NãŇWÏš°`z×uÉL)¿Ð‘{¦ Ð%…äWôU ÿPÞFÙ´^}„ž5uÉÊÿí‹Ï¢Œl}Â&7n`€@§ÂØ=û§M_7©Ûâ40´«0`uó@ëЉ‹úÆMü÷¯–ÿˆlÊöZÚ¬–ƒ2fʱë†ôÔI‹Æ¶_˜©g_¡ÉœƒSz–fT-(Ɉ‹:¼`Ô˜”<ÚÈÞ/tòšqíFŒ‹Ð6÷¨Š¤>gg¥¦ /œ½j£RA0lÉÈ#× nµ–¶ôn7/´mäÔèNúñѱ+ÆG'åPÖ^AÃ×LkbT0 $~¾°xàßo³ œª´^zpJ3+¤2J¬C³Ž(›–+ Y“-¸+!Whé6«v¨¯†â¡N$6::|óœ3cã3‰®…Gµ+æt²£¾¯‘ P~j¼G³~µ-¥ùùŠÓPø¡â¸½L¦ÍØ8½ÇŠdVdáZ©·o·ú.Ùjn–F©‹?M)Ì]-³W›ü/îMf’|n׉dϾ¡î”ê €&õ,7Ùyv|2�ÀèfÝ[7zí«T¡MùúÓvMêæDÉâCº8zÏ”mO?! ïÿm™> ŒÂTè5²ÍÍY‹§=6Ð0kØ·Ý?ÍV¦„Í 5-Ô“T¨_Ï|DZãoú t¡IZjÜ£ƒ»7Íz—†uÍ\*Ôº{dÎJêèƒàîUEEØ´4È ˜DTý"ŸãA€BS²K4· àôÈr@­I€·ðððððððü9pã.ˆ�A�$_ëyøx8;9 ´´Ô<IžPG‡¡i©”•bé±ÝûŠˆôì¹ð aŒ´ô<?Š¢N?R'XÓ ¿£~¥QS«…orúÊÄr·eûMø#õû‹Q¬Ž*Ï<qqB3nš/¿]Vu¹ûÖÛÿ \JÛÓÃw(Zøl]ËâmÁäãön 7]žRUTøªôñ‚& vTØviF`Á}J”äFÍ®ÛädÈÁ3“*±’¦© oŒoP´<ÕÔ¥( QQMQ ÅP"�ˆ¢BE! Åmw@dr ! „ûÁ@ôjÛ©iX’{ë)°Ó!÷oxx¸‘‘¿!âO¡dî\U2¥â)ùð%‡ç·ä×*Ø¿–´EsåÒ…j51ÁܰÆÜŒe>™&˜B!R0<Böïÿߨá,Æ”Ì5bR& D1nnnBii©:::4Cgçä`†Ñ R|Gªˆ ±<%m÷Ûé—"ªßé±ÔñGê÷C›ÌÿÒEû…cÇ„43p—ŠŽ|~ÿBoÍžù úäY:jo¦J÷Ñr[¿)KZ㯫6¢¼çk'nJi0·o¦°$* /yLù§LÙWçþàj8 €‚ „p¾ÕÜ›]§1~¾«ÿ Á¦½JL3²²3C$3ýM ¶.m$ú9ú“>ÛÿÏ)ý–mì?™øXå[Ó"Mÿð‰”²2T+°ýÀP{JÝ埀4#>‰µ(e¤XДµuq¤Sï^æ%¥nHRß'ÓÖ–?LœööîKÖ£œË×&Ar?ǧëZ›«5òðüÉ”ðÚ‘ûññD³�+ê›ß?–ï6Ô§ÚìQ&¡ “ž‘!‘J"@l` Ö7`Y6''[‹h üÊǨßßõ¹ŠxØ?J¿¿ÜQ¬Š¾Mዚýprf»:¡-F5´zaËRHS FAã®e/jÚÖSJYWë3wzCóbåËœ= @ʯšÜ` ÿ[öä@�à¼ÈÊ›R¹‘ À%ò³ÏóEH?|àà= �g§O}öéKJÒ÷$7bùˆEw²ßÌo:óª�¤wfT*UmîC¤QSªxô?žUT@‰É×Ú¤¹¹Ò¯Z-ìÃÙ5Üÿ:˜¦ÌÉ”ó‡mzÁ}m‡Y’“û}g®I. óó}1ï»Fšòyo¿ ¿vÄ|µÜ¹û»:Í»Wü¦a<<%¼vH"çÊ^ãßü(òio'{Çî‡R¾þ›E>Q¢…(ŠR€áNTjj*�ˆÅ¢0ÆR©477W@ D¢âM<Z¸úæ’{üyúeÊMºžp}BYú§Kò_ž~½C;}ȡסØÈùµ˜Â—Ä-×¼ŽYÝ\¿¨ÐÖ6<~÷òÅÝÃËþ*+.J0®ÑwlûÒúšî¹„TÏR“A5&-Q®@ÀíM*[q ßÊGå3„ȲP%¸,’‚ÎÕ¥¢æ‚4îܼ®5ÜÍõõL*µ™s1IµäÍuÝMõô­ü[/¸òYåq2­néhÚñ`®ü„äÍá±},õt lʵœq.¡pû'œ›ÚÔÛBOϬtà 'ßç¿}»½­ƒŽÓK…[XÙ7õ®îh¨+¶«Ü}ͽLÅÙ­ƒ‚ÝMuEFUº,»™ª&«ó^ïéåm\íGîZÊæ¦ºHݰ­©…± gF˜Ušõ„U<êÃý‚\LtuM\ƒúmzT W,;;·suWS]‘‘]…Öò¬ËŒ\Õ3ØËJ_GdâT¹ãÂkÉ?«—ÿ­d_Þu(¯QÛÚʦm_£Ë_m((²¶Ëkãª0YûÚ[V˜~ïûÚ ˆK>ŸÜyÆ¢e›Š0i~·2Ô2tåig—lŠ«S7qÂùÙmÊÛˆô,½Œ>ø¦`•ÿÆày'{YÒò:®S{Y,þê#O Gûoþo û6éÕ«±·þO° )šÊo%àNcŠÐzúº4Mq. # (¦@µüôV6|ËÁë÷÷>xý–ü£xýA_b¹<"ó?H”Å8ÿÜ ÂbÙ,„MÓEB0f1&*!¾fqI½ö¾y÷Õîß92Î%rJשr€}¶¤s÷cN?º»½EÒœ6ƒ|"�$õÁŽqaƒÇ†'*zÒ€£WtërÈvÚ•÷Ÿ^îkŸ¾¤ãˆÃzàøýÖ>íVJ{ìððè�á¦=Ö¾Uö°²nNo3:B"P#yÎõim]óÿûêÓË«ÞÑfÂÅL�FÎl;ðfÀ¿w?&>XWûùøS¯ä‚Ízydf‡ÊUûîUØ1À¸ýÎødŽÏO×·°±oÖ!X¬Hòîü¢¿jTè¸öY†2ßó®Ok=äf…¥Qñ¬~<¢Õ”k¹*aHê½3÷Ì{¬¹ôðÞ±ñ®·'w™r!�ô*¶¿óú‹WNNò¸1nðʧ,üŠd^Øyš´ ÒSžBæAƒ¦÷¯nZt)SðK ’<‰Ö•ç+“H:¶3ܦeë²_éÓçåJù.Š©›$q瀶+¡ÿÉ·ŸbÏ3Ü٭ߦ8üý‚°é’À^dfgggg§îgOý>õ‘‡§ä#,ÝvòÄ–î_e~ý®°˜•²,ç­Œk qm$š¦)===)Æ„�…(†a� ///;;G*-Þ8ý³›ØüñM¯ßßûàõ[òbu„ü9G±¹Ü ?Wxå=|Š’»5à2U~ÈgP€�ê^"„È<ùk›(<§�™5˜½õïµ}]={lj“øâe éÃ[î”2»{Eg÷:c¦tÞp4‰�°q¯2çŸßÞÍZ9Ÿ\úâÁÝê-»”ªÜ!Ì'ãÕ«Oùï8îÀƳV=g ­áêRµÿŒÞN—7ìy%kªgDLé³§ìÒù ›wuËÖ¸ºcg´ôuô›6®aÒö²2Ÿ<xmS§U-±Ø¡v»PÇѯóò£ÑK=qt¸—J'P gdlllllLÝZ8=¢ò¬…ml(`_.¯Wʱó¾$(%:Ö⯽çgWSº¥dß\¼ãÓa@g#±]Ј!! {wß’»:Ø[7\õ #³†s·/ììëêØkP˜,ë�YT¨_ÇßÙÊD_‡dæêjZÄÜ{ÉËcê¬ìVqY¯†{7œL)¢CRÞ»kPµe]ƒ6Ïšûì}.”' ª­ßt�äÅ®¯»úß‹R��éû­V-=ómó×ÓÃw4m¨: ¿Y\S´D6Œ.¹5/ÄÕD$2rªÖce”bB¿2 äÕž!Án¦"ÈÄ©áÒ'9×Fºë×ù—ëÙ‘Ä ½ÆÞ’f=X×£ªƒ‘ŽPßܽÓ6n¢‰ôñ¬Š„Nq ûÉö!¡el õŒkößþ,�Hâ¡¡^v¦zB®…O³QÓ‡…•w0ÒÕ3÷l8å\"'Š4îääf~¥ôuôK•íµ+V™·$áÈÎ‹Ž­Zû1P õô#ݬLÛîI��öÅü*ÿ¼bS®ýݦœPh`å;ø·µvÞå¡Î4BH·ÍÞ\�’z{e¯Z¥­ÄzæžõÇ}Ç�½©[ekc]PߺBÇ Óú…–±ëê—òo½èf:'bî‹ÝÃêyšëêÚWyªð87ù|j|hYGS]¡Ž¡Kÿ“ÙjÂñ§'5ò¶ÐêÙVz]�$ýÎÚ>µ\MuEÆÎ5z­ŒÌ?*/¹:ÜM¿îª÷Üɬ#]-íúœÎÉÛ#ã›Up4ÑÛtù7Š‘¤ß]ûW gc‘ÈÄ¥Ö¬ÅL©.¦n²o<Ê.ÖÚÏLß̯}Ë�üúe¾™E_\YHZr 1µ²Ò‰D"‘Ž€øÂúÈãŽÌ-Å>ã#¥��8fI¸æâ7XSíÞ[Ò¼¼‹¥Ž@dìT¹ý¬3ï¿ïÔ¨âÉ97Â߯DOGdâRsÀΗEÔZ’ñwÇʆ"]sϺC¶?ÍòisSCÇÁ%��¹çúÙYv=’ � ‰æjÖnošjðBoï¼Ó½­múœÉȹ8%¸Œ“¹Ž@ÇÄ­îàicÛVu1ÕÓ5v®5ô@ �$ñȰ^¶ÆºB¡¾•wèàÍ2¾§í•µ£&•­'Ò¥(„1f¥RÂF ÐÑRH‹WÃÏofóÇ7¼~ïƒ×oÉ?ŠW‘¦5ÿ¿_0%™„àyfæÿÌ)æÏq+(Åeîsˆ‰(‚œûg/%— ©aŽ ûÉÃW&¾¾œ1@èSÖ›}|ÿ¹iýñ‹G6vÏçúHP±a=zÿ´I‡_¤|8÷÷ºèàž-œò;»{zÿ xù—f��h÷²¾:Oï?–�d]>ðXà’y ÌÕ|žqüÇŸÊøpNòô}˺e<zÂ8°Q'ÌM}½ÿŸ™­z60R §[mèâImüLèÂQ~½iö.£~Û–B�@:W¨ZÕÏFãÛëïYÝ+[ª/ÓÖ^¥ÅOíº‘#ÍMùÆ bâ$H×ίZÕòNù|÷åÜ?s1¹\Ý2wx$~U]}}ëò½¯Tš;»¹UƒïŒg™»¬ï:¢9º6óêÍ4 ÷áô‹ItêµµÛßÿXÞ ÿ{q\ž¡­GöËû©€}÷üSî«ûŸY�÷áy²¥O¹"öá(žÔ3;Oê7k[MÍ~ raÅ›®={z~N¹;£š;ÁõE•sÏθÛdÌÕø´¤ggµt•oÔÀ"òĹO�²®…ß4ªU§LêÞñÃ.û.¾—”ÿà𔺥(��ÚkôÕôììì´“}l©¬Kã›÷»è1ý|ôë‹SN÷íµü $óMÔ]qÏã/ãb¢V½X¼àn…§½Œ\Sçí¼A #¥�ìËÚ­Ém¿%2úiø¢NÊñ-üþð®+n­[{ÓäSÔÅ5S—NDd�IºþØ98Øîͺ“^ÖÛñ"9õÝí=ÿ«Ìí"¨¾àYfvvvʶ:äÓ¡AÍÆ?¯¹ìÆëGúÒ›» ÜO€¤<¿õÐyÜÍ7ï__™å1oe|ãU—ž>¿2Ûûæøák_c€¼È™­z2zðÁ«G§„X®$íéåºÃþgïb_]G]BÒÛ /Îîz:&%ùõ•Ý} ŸUvîùgÏ#U{<®é€I*¯Aùú!&‘ç"Ò��ònŸ½Lj†V¥ÌoÓi—Áà#OßF.«|otçY·$�égG5ÿß ïégŸFGîZ¹8WÅÔMÆ»~}› sGn{ð9éÆ?Ëoøõè˜oBÇ×W­8)!‰}±Æ´6ž|œ¬bÒº>òðh†Ú^WaÎí—ož\XÒ ii‹æóîÿ·. tÊöÝzùéûø‡{2;ûÛ¯i¡ ŽÝнùÜ WßxùôÔ$—ó}ÃÆ^Ì2©ìŸtýZ4 }zùjBò­ë¥�ìë«W+WW™…Wèý©ú“ÆÝ»™ÓxóÓwïïi›±~æiÛñ‡î¾|°·CîúÓÏfÌW·£ô»|ü::jïÿlÂû7è(éûOÒR´Š¸¶Æ„‰D™™Šš¦)„!R¶˜‰F&&&„¨ŸÞÒæ/=¢0&&&&¼~˃×oÉ?´ÑQ93î…ü{C!ÄbRÎÁ¬è(÷)Pš”p¤t[ ƒBB�ˆ„¢(ù‚µH´Ð´Éë½;®3ŸºjPiHVzÖ70à¾úÈ@¬išLýÈ<lÚ”ª/Wõ«ëáÚ`¥´Å &IZßHŸa†Ñ­<çiZz¦H¬/ë }±^^zF€ôñ²‘ûÊLŸ¬ì|H®Žp×a†˜t9ü9=ôÅù„HÏ @9u3ÜùÆ‚®5\¼:²ìÖ·–™¶Ož{mÙÒµ‡(Ëõ‘eÃÙvެªióKqã¹[ìhæbh`U¦éŒðOŒ®ˆqõ1»öÏU:È—¼ÞñWûµÓV*-3M R}N¥|~±´Zd¿¦“®åh–ÛÛëéŠ=Ûx¹I’ßÇ«7.‘÷/.ÜÔ¯;²’¿‹¹wéN]3=z”'ö­jwó]!É‘q9n¦Y‘ï>aHŒssô1Õ2SÔ¦—|rçiãm+ej`<ƒ›–vt*ßnî”fYû·ÏÊ20ÔË|óàI‚DÏÒÝÃF:ÕÚµ²¼´÷x"¼¨ð:¤QU‘@,ÖI~y÷Ågl`íåf)S6¢…òñàœ‹›¶}l0af3 +ÿNcººÞ9ÎM@ºf6¥,¬½šôjîÆê;–ó´³õjÒ½©cÌý‡i„}u`ÇM—þóGÕ-mgï]«aåRr³~wpçu¯6­<h@…R7 mßNî½y5ü¶uý†>ŒHlÀ$>½ó:2¶÷r–­¸@#‘"Ÿo8Hw˜:>ÄÉܶê€Q-õ/ž¸Ê¹Ì¦ ,l­,lËvìQßLbìRÑÍÖ¾lÇ®u ß{,IÔÎ/«ŽZØ7ÐÍÖÑ¿~hY#À>›[E—a†Ño¸î#�ÊÀÚÅÎÒÒÞÕÖ Y]BHßÐ@÷øþ»l¡©“§ƒ‘äãëöA‡9sZù;8ø†M_Ð]ïкê pQõfõu/¹ ¹sôTzífµEw¶m~XiøÜÎ~Vaãú¼=}ú9›siÛÞ´Æ“tªèdëR©YÒŬÒ(®nê×37,mLj&¾ÎÓb‚·ó|sp•úHY6¿tP ƒä+ºUô û÷©¬s¦}}äáÑš¢jcjïboë\¶é¤õ*=\¿ùÖj'@fne\­ŒMìûu©’÷üi¬úî/~³g}¸Mÿ…ãë{Ù9Vèø÷Œæ)ÛÖ‡ç8Ô©ëñ$üâG‚ã.^ÊðñN½t1“¤KáO|CkÓ”_ög”º··Š:&¥JYX¹÷nW^ cSÚÇÁÆ-¸W¿´÷ßqQú¥\mí=küµbqúÀšÃßÍNÀ5Š Äít@Ó4�°˜eF¨£ƒgdd¤¦¦fgkðÉ+ÇÈÐðñ“G˜`n kþøu„ ~üø±‘‘¯ßßñàõ[ò­täkc¼ðäL€¦išú}šÆþ9ùÀÏÎ¸èŽ ”o·Åòœ£^Š¢( Q%sr( ¤âÛ¢¨|ç¿Iô¶®õ†ÅtÚ½k� =±•™‘ÁõYIFz&jÚ=IúðïN“³G_~÷6bŽó¾6íÿ}ELZ¬¸u÷îÝ»wooïål(ÖÏÉÈ”µTHfz–ÐÀ@H>š¹õ¦º! ìˆcQwïÞ½{÷ê¬Ú&b}ÈLÏTBl€ +bBÇUÖKî¼~óþå©¡Ì’°Þ{µk[d_Ú²'µA—0 m³ˆ*UwÊá ™y9)/–…ê3î^îûI’è­]Bþ÷¶óÞÝ\Ö)‚ŠLœªýµxLW;vin"âÄóW´]ß§ÚŠÞ-"žJÖ4ꓘ‘ †–62= lŒLr3>§#›@'³Go¥f?º–îÛ»¢w\ÌÃÏ9¯$” t´ø†©ÕäÓ±ç¾ÀßÈÁÑ*7áC*ÉP8íàÒ —ãª9¸‡ Þt?€0 KGç‹›÷ÆæÞ?q&+$,H Üây¹¯¿w“±û_挚¤¿Ÿšº¯£¥H$‰ô+ÎxĦ|þœÏ˜BAVV6��‰!;;pÂû°s´+4§¿Ù¿+Ò¯MK7@MêâºÝZŠŽn>þ9+âøEãFaå”mu{îjSÚ¾\ÛYgâ µ¸qâ»÷9ïV50‰D"‘aèª÷yÉŸÓói’24ggfsÖ ±¡AnV�NøðQÏÞ!O˜vî¹ýöÝ»wïÞ½õo‹ö/õ Q>öoi“¹8ÔÅ©Z¥×>ü16޵wu’™xGWòþ]>”^PÛ&:göO—Ü9p$9¤m¨ þðîCÎ…!®z"‘H¤ë<ä’$åÓg6ýC|–…£½Ö[¼S7q̆^u<óüõ»wQkkÜì6óŽäûdQ¡E·>C&.ÜuíúïKSçŸUX´«<<)øn&ZÕÊÊÃÝ0>öýéCòæÐئå,ŒÄf¾£.籬†™ìûwïÁÑÕQöQ9¹XgÇÅ¥P^ 8Ü>þ)1üLLÈÄ1µ_Ÿ9—|þäm·†õ]ÌT¿ì&ż½epoèÌ,�Hl(†œ¬B;긗vÂ^Sß’ÀB>¢ !LNNŽØ@œ““ƒ1–H¥y¹¹R©cõ«Tqss}ñ2:*2299ùûÉÉó_`bbbddäæêRÄ=¼~]xý–|´ÑÑÀ:‹Ï=¿ûæ˜Oÿ™`?…rf~vÆýk{hs³ÌF�Hö=#RL% (D8›€ª3…×C•X�@>:SºHÊ…±-‡¿í°ÿøøjòq}]/_—ä ?à`GòÝ}B{·ñP?’È>?´ûžw¯½^"@¢*ýÇ´]V7üfÖ€vŽÞr# úyÁ¿÷ŸH[—g€}qïanéö^LÞõ#'b®l/ÍŒ”ÇTË*åð‡M}JÉþÎóõ5yð(ø™ È|x7ÚÀ§Œ-¹½wol•­�»:c‡7\ÖûÜ]I›ºÅO±—Ü>v:³ÖÜÚâbï,„ôÕ¦ù;?úlᙯÛIRÎn>ìMǃ''TS;›[Þ­Q™Ñ;'?¡7_ØÜL7ãÉß¡‘šn¤, LHlÂ{ ž�HÞ§$ë蛈6u­hþ0òìK6ÚºFeY3‚�� �IDATUÇßË7ϾÜWì÷- °ÉÇ£».Ø¶š¤µ‡¿Ìèñº6¶Æðq·j@dèßõŸc§<Þ1´å_íľ÷gVðí5 ú’e«¶ÅÍh:¿®�(‹jW†÷›y{å_-:÷°*{¡MC^nž|¦¨¥¥Ø¼Ó†×«ºDÄoTþ@7z$³¸Bhk;kröùk)”Ë÷lô¾]wËuØ"[S(õ¡.5ûÿå¸jÝ6—3&möU�cWoÌ–zÃÞ_˜Û¹u›AŽ/÷Ôb¢‘2/e©ã:ôÌ£yUTÍ*ÒwùDT™%$ûI[ەʺþòGe Í]|̙Ϩ†„�<ZLßÛbìë£Úvh5Öóå;:6úM¸Š�@ú&ú-²¶Ë¿¤A·fç6†-v9]ê@f“E¡F@Y–²Ô¯?þÕ‘nªf»œtƒ„—Ñi$ ·•’È"ë&I<µë¼eØìJbDA™Nzþ[þØåË9Pß%¸*´­Ÿ¯Yú‹„,"É‹©<<êaôõɉŸX�åÛ‰m´¨$õÝ» s7Kukà~i‡ÆôØÄÌ:ý¢§¯aÚ¦f 4ÝHÛØY“³/ßJÁ˜€œ7ÑtmlŒcÙ"̺áá}û¤ªv®[/£Òˆ}û÷Š"šM)MÓ´òË�ߟýÕ&„äC Ü�….l\Ì{°(õ-Öí‚I�…tÂìœlBHnNnVn!D@3e‹w"áîæú$ä)‰ðúý½áõ[òRÇóg‹PÂog([@€!™-€» @�"€ &�ˆó´Ãõ‰–ß©ÒüÕrNB؇ËGn0yyTyÜœ„(Z¨#`|Ûw.·hÉøM3j¦m›º#¯éª&æê£¤l¼<õíYw±á�ƒØ£O~pmY:ߊvʶy·)£'-­¿¤sbÒª75&´q¥uÜ7~ƹ;òN÷vꣻóù⚪ÝaµÎmêÍžxÀo”׫E³›¶?\Kh'/´Û†[Õ»z£gÛ·\Ê+=ÜM›þ,ûêêµ¾++­ä㉠ý¶˜ŽZ3¼²†u87-1öá…msÇͽå;ùä`/ ãê¼^ÿdô\5µ®ñÃ¥Ã׺2º¼NnN�¢…:ˆ»¸ëí_ÉÙ +úäìy­› ¯ yPžsÄ,É• ÍÄèèà„§IYAV‰ŸÞ$°66îµnï\pËcœMNÜÁe¯ô6ñ€E•ºúÓ—^Uî«/’Ô)µuþUÖ¶\ —oXM>ÞyÙ©Õ,?Yg_O_Wòòîý”&•òû™Ä©ï_¿ÿh–ùdÿ¤±GÍ:«£ûa»j@ü.òrr)# _[Á™Ô4@Ù¶Þnv³AË­ûž¬­�¹¯nÜÌuôwзõ÷²$ÇR3ˆÀÉÕîý©}á/½ròÊÖêÜÆ åÄáÁæÃC\ rbsl*–.~5åܺg9#Œñ[6¨¦-ŽIä†òÙç{v=èÙÜžkN€öê5¢î¢CoûL¼QV�@Òž^{"ðð±Öwös7É{‘šƒÌœ žß{£m·RŸi/¿†Œ4jR•E}ªÙ 3ßǃk9×âQLùNÝýWÌî3ËõïnÍrÞ}Ê#:E@fêÒOº5ÖÈÛÓÜØ½Œ£þúÔT©I£ž-Æu3¶Îºaôå£6d7]ÝÄ¡ ==xÿàN\Ž—ƒH§J÷ÎvÕGö‰Û6�€ í;¸Öž5x¥Ó¤¦Þ&8éMªI%_kQ­]¬ëO軬Ԍ–ÞiïÕí=ª‚úº‰ßìü¿3eg®êåêée}xÉö“êX|º¸þÀS³r>”¢BÕù¶àu__8ôÖ´¬‡|ŒÜ:}gRù‘ÕŒþ¢úÈãA¹ÀJY“-îàÔÑS761‹��Q;Hƃ³'£\Ë›$ž™2÷‚]› ÿe™Ãœ›“‡Ÿåk\GñpjÕ#xöØa3«-ïY:ëÒ¬ :Ö€²m[YŸ,®¿jƒ¡in‹€áÃÆKœ‡Mò*`ìP÷þü"ؘ+Ç.×mäB­·)¹ÎüF–eL;( aLˆ|~&Bˆk)QyyyR)›-•J)Š¢)Š"•JµÙ逇‡‡‡‡ç?CÕÿ�w†ëñ+þO¶ÄNæ£P%��PD>w€\‹§¥¥€$]xœzy”Ÿ¡¾¾¾¾¾žžq؆D´çà-ëÃâgÖñòk»Ûtä®%Í59�@ÆÍîay¸Ksc» IѵVl]`š²é´jG_´:ÌÇ»Á’ì.ÛÖõrÔjÌ@TeòîÅ‘CªxTêá;ÏÌ }�ʾ皭ÙUan&&®–f¶Ý´¶³6ÑI_<~©çæ¡âf §½ºq76KSã&ïô oê]ܲé¿ÿæÉ1õ�€dÆÜ¹qëU*!I×"§^ék ËaÔtC"Á‰÷÷ÌêXÚÉ- ÝÒO ×™¬É÷è»´场~ï ê+zÖ¿ôÚÚÔLmV½£sή{î`ªeBÞošö8‰ƒf4njñfuÇÃû\ÿX)xÄP;��åÐØË.ùÔsÐ×p/ÍJ›–¶ù†1üî஫î­[{Ëš„Œo‡!M2W´Ÿ¡2W›2ñ¬è=«²“c¹v+3Zl:6;H7@’voóÐzž–†ÆŽu×P½–Œ¨!��Я5è//âÒ¡Ke!�à„«ËúÔt134uosÔnô‚¾eh¦Â€Ù½ôvµòvòi0lÿKV2ÿÈòš¯f‡ù;Ú:—o:|ï ­Úr”C÷ÍG§yÝâikëÕa”÷·âG»w¿¨Ú¶™-—?jR�dÑlp'kªb—ö4�à˜Ó3;V²3[ú Šª8kVGJ2jn³´%uݜ˷š~&[µ]}xªÇÕÑõ¼ímÝ«´™zêVSgßû 2>Ü·š«}™A—Í|J9èˆÔ%$}q`t3?k##ëªÓã/šÔXŒL›,=² |Ô¨ w÷êC"¼f^ÑÒÔë;ÈýÊàÞã �íݽoÕôO.=ÿª¬� ¬0þÀÖÖY«:¸Ø:øÖë¿þ^�èÎ8ºµmîÚŽœl*L}êQɳˆy*êë&NzzõrÄÃø<Щ1u÷<¿›ƒ¬ ­*ô¿ì9}÷Ì`]e…úÆà$=úìò¡þÎN>u†7î¿gÇ`¾¤>ò𨇲ëºôßÿ6+ãhïÞpmª_Y'}TTíÀÏM +ëêUÆÛ:«öO­Z„+ØïŽaÓ©‹%Ì r09 ‹r,m§T_ãÊ÷�rè±ñÀëã½\K×òªöÊÃsj��0e:wõÍ¢‚ÛÔ5dÞ¨M iVÙ®< ¼Ô¿?¿’ódSŸž®•z4ûßž5]l)�ƒï” êÝBP`Ý4MK$²˜cÁŸ<pè{%ÏÃÃÃÃÃóÔlÜ€’ûwPx( hŠóÞ@S·¢�aVŠ( pž �ÁÜTk �˜e @¿ŽÝš†…!•=AeDîßððp##£ÊÕj6%hs†ç[¸~åR•ê5¶QJ…_/®í¿³ñ툑_Öæ+6 ÉJM%l©‰mÿ—4ææÎvÿ½ÇyiÔ„²uîz¤»†´¥©Y(ïõî¡­g¯ˆ\òëSþ4JfyæáùF¾¾`çîíØ m~³J‹•pß‹_°â7‹k•ÙßêÑùÁ– ]¹t¡Z`ML0çžcîÆ„ŒL0ç(:"""$$¤`ÄØ¿ÿcFŒUÛ=rïˆbY–eYJÖ,’5pH¡‘žŸPÙÙ!�‚eŸFŒ1Á³˜ÅXŠÙÂ; ÑÅE€g=PÂ÷ðy¾üjÿî;þ2ß5 ŽÝÒÖÉĶÆÔ„ŽÛþmû3v¥“DíÚ÷>¨]KMiK- ±1qj¸R0b÷<ÞFÀÃÃÃók"· (PlDÅQ+•‚Ü[47ÞÂÛ xxxxxJ c¶ÊÈ?A€"„È=³�D¢iŠnçD‚�)¶HP ^ð3÷N xx�(·áWÒ†ÿˆ€”CŸ“)}¾V®ï `î“ÏEÝÀøO¸•1ῇ‡‡ç7DXoõ‡÷?[ˆ’å4äRú;ÁXѦQ®Ü$�� EQÀÍ/Ÿ%ÀùvâÍ<<<<<% ™?^¤ØÔ!š¦ömD!‚1 D0ÄmñÃù5D²d&¾ÃÏÃÃÃÃÃÃóK£:Âuê)@@„½e‰l(…"³È÷°+¼>sLñžBÜ&²Í (DqN önPîw€��Ŧ…ÌòÙ…7Dä' ððððððð”p’?ž=u‚¦‹òÃû-Žn†e¥²IAß'ÏA¾‚ÉÓ!Ù":yÏ^¶Ù0‚¢eó dŠ¢ !2{8·¢<®‚©ð–žŒ³‹«®žn÷`–Å„<~ôHÓ E&ªK1³š¦1–¸�Å̾‘ÄÃÃÃÃSÂ@ŠiuòÝ€�A Ûæ—›h�ò%vQ€�äSêˆbºÊ炇· ðððððððüq |31e!�ˆaYV"‘ȶ9@¬……�ê4n(Ř ‚1ËJYŒ1·Õpí0ÎA”¼­Æò‹Ü¿òDIþ@¶Br§‰Ê (´ø`Œ• CEcPöa‚1ÆD¾Y¶²9(¿KÞjDù§J|? WevY”s‘E“T¿ÊË#ÁDáBç†Kíý²%ªÏ¢·Vº¢Pù/ßEîMÀÍìUD„ —€1á ’GK€(›×r9‘Êê_¥³ Õ§T<‘<E¤œ1KáD“+—ËÊ0!€�14MQ4B!ù²dyô˜‰T*3H¹ºJÙÍážcÙP¤<¿åŠG(_NæÏØ‚—™ÊÏü )®¼Ð2(3A^¹d2)}’ÊE)Bp~A¾*òÅ©R{”«¸åg.L >ˆê3埩Í9£dëÃe ÁåÕ_–Ó„(·(_p�Š¢…L!ŠM™·H^n !˜‚…¢)ÌbE6ªÖbù;C1»\6o³¬ü͇(ÅÝD^’`L0Á�€(ŠB”üȽ€{qq‚È»ÈTÂe)EQÜ5 áeú¢(îUÆÍÃ�š¢0«ZDòU\Uï{ˆ¢š¡iŠ•H €D"Á BˆT*E±,KQ”P(äNJ$ŒYÙ#*ÇÒe“õ¹×{>½*Ûy�P=¹R ¨€(Þ- ï‚qofùÉ| ÉÏ"—M©5î EÓÊt1)¨šG¢EXÌ•Anfü @Šƒü¯^~NÏÁX6–"GÞÍ@ × D±R–°¬¢ÃÏÝEÑ4Èæs"š¦e]eB×üUé„ÈÎ+Gy¸¿ÿbESœ3%pjDÓ²Õ¡²6·¼;ŵóuó‚hJiÎ ²Õ²¦4…(BaL"û6òÞŒâÑ(ŠRžUi™#J¥¢L!JÞrUR¦¢e—�ÅPò¸ÌúO°üÝlåy›^¥ëƒÔ'Q M«pFAi …(Å3qÃq¹eIf5¡äâQ´JRcLQ àÌ\û^'mŠF®c©è)( ²`ˆB¸Í?8Pvù€BÓ4' �w?š¦ `³˜ë€a¥,…¢(n+QY¹ bhË,_HÖ¯�@Â,V˜ ývBWd 0…שP„"… $(<ˆ�”üÎ|…“3ßÉË�×!FP2MQÊXeQѲ.·™ªL¯*…Ž(;ÍòÜW±—È»pˆëyšf8us•ÿÿì}w \Eõÿ9gæÞÝ}ý¥7’ÐR(Š R¤¨„¤ˆX�A¿Š âWlˆŠˆ,(*¤IQ@è½=!’@ ý•-÷ΜßgfîÝ}/!"úC¿{\›Ý{ïôÙóùœ2ÊËMWÃêóœšeÇñø†Ô÷ "2ƒ±•RÄ`5iË”R ¦©VHn2 !‚ò€ÐXö”ûǺjHuÙ“PÌ@J á%øÑÇD”¦)€ü—Ò4QZ ³ BÕ!f`aH8Œ"�¡�X(6$"E̬•òÃXã–›ÍIÈH1@ËŽ^$$R¤H1¸MÓTf»L!"DD­ J"Yk1Ò8·ËL$7™­”Nä˜3Ï…x 8ÛÝó#XO Éu Óž,‚Û"Øý&…IŸ±WaiŽ�€‰••Qˆä‰™ d³BD´ÌärÑa„$Bc¬ì8Y¡o5È®É#4¥)MiJSšÒ”ÿ\af5ýÿEyÖ(&G‡Þ1p¸ˆœ! 8x‡°ȱŽ<p®ˆîsöfôØ T†yl(åzÃ0æU7D‚Œ{È?ÂZ6©qv?o?$¤`5“ædÏÊ´â: OÍŒÅXÿyÎ5"Ç f†8 Ü&lL818pŸd¼M½þíjZ×�¨3×ßXïd ~÷oÊý«®OÖ©IgÚ69×_Wì ·PÝÇyOà`=tÏæ@3 ÈÒŠ,Øàæ sWf"ÅqÁØÔ‡åáJ;ÐÌl™1ƒ<Ù²•rù9˜‚|ÉzqÀ@d �>LJÿ #—uNîcÌw—¿qÀ¼â�û¥$’|ï<`¬ªB9´œ9 äÁxspÖ* åöÜ0!å™2dù_nÒ x±ÆY”[”$°\fÙZ&DÄ Æ Eš‰ªÕ*x, À–Ù2!Z”l¾·6ëiÎP)ºðuW oòÚ”H9¶ËZD@B¥5ÌMPÁ«,³w£ðN*òð¬~€Á«%‹¢¯_S…¸`Œ1ưá°˜!Š"IÚéH)eÙšÔ˜4µÆ*­´Š­µÆ¤� u¤”ŠãÈ›¦©1©4Ÿ”xC˜@Þ³YãæUw54Jý'KCú¥ýL ãË(év¬ß+šÊù„æûf.xÂÎCƒÆ®9l%(ËÜ××ÏÇÁdPØß°JšÒ”¦4¥)MiJSþ;d=êF�FqîEë"2û›I…äìÂÒx(V¬O<Hû`}§î‰†ŒàËÉ=(¯€f~ðz-=¨’ˆl™Á8[;‡^eL½3mÕ`Å^q7ýC²áw…š»Ê °ey™®÷¾Ù8ðŽðgC*–ÿ$ø™çkµž{• TçÆe]’ïä îg·øáR†÷‡Ì$FpæqBzˆÍ‘µÆRí3™Ô€Í|ÀâpA¾bˆ(FZ€<ó’aËõ4¯ñoîó€dò<WãÖñ9g³ÙŸ ‡SØÉÖ}.Pì²¹Ãå}kÙÛ¢k‡ú¹ú R×9BÌ· „A&CÃp×Í|tÝZËȤHkeŒa¶HDD œ¤©±6Š"%$…·–+¢HEÆça€bí—ýÀ‡BÉÀyŸð¾QD¤ˆ­8ˆç‚@¢œ7›„ú$¬"´¤7.uý³è_N%ˆ˜Ôj2^J«�ߘ­t¤µq©°ÖZ :Ò˜šÔKŠ Q,³ÝkŒÄˆ¡ÒDÞKÜ0‚â<âÇßMºz*'¼É®ñu8Sê >7ÊY,�zé²¥‹Èã|ðÿÉ6yW rCGˆˆÂz¸‘ÎHß<À!NŒ¥ „–Ö›Û„iH½ÜÏ/hrÿÉ÷ü;GÞ™µjÊ;_š3§)ÿ•òŸ5±ÿ³jû¯–€#Š–Ml™¢cbi*Ê%Ú`«iTÀ²+ö¦QQ¦ý{gòu`/ðµgöÔºš‹ïèì;Ç70’RÚ#UNÛ+™·îOÚAJk”õx 4ȆÀãR`}. õ¨5\ï4íºNËh‚¥ ¤$Â5ù¯-(ܵž¦ÉWÒÿÿPW úI�T¾t‡?8ÿu¶lÓ$EBD Ã*0ØZR¨”B"fë<Mr\@ ZˆÐ„à‚õv×›6j]’"0“çÞ²¶ƒó“qeøØéFä__“†Z­âgô¸æ3"³dd@ï\�>˜ñP\އ߇¨ä.ðóÑÃ<ÈÃ]B²lÁfV}‡ÝYcåCÓ=¹ô.v@‘eN’Ô2aš&¬”Viï[aÙ"¢òãëüï-X·Piáy‰r©rÐQ>"zv „±Özî”V͈màÌ䙾Ÿý23Ü„h…³PŠŒI5á :ÒJ©jµš¤‰\cÑF:JÒDÇ …BÇÆ˜Z­–¦©<ž”RŠÐXËÖ"ø…àæ%"±5n¨wûú¹Ð€0r³¸qº œ|:Q‘ŸìBO¼‹ŠŸc¹Ø¿‰¹q {Rm@ÙµˆHè‚2ÙÀˆnÙf¿0×ùúnøÒ~ïûÞŸë‹&Mðo’ûï¹3ßóïygÖª)ï|iΜ¦üWÊÖÄþϪíúåÖ›þö6<e}436È€¤Øg{²À¤ÐTÖWBb°Hü´ëò‘!E@F!\jÃÌ‚ žätC§4‡*åÍú°ö\D†c½X°£ÑZ³KÖåüK­¨ÅyÉGÉ×7®â @ ÀA‚7“ ÑsHx0©³¬fÐrv~öXѹ´×…G8?Æúd1ÉõÔ º”rˆ!ÑZ+ÁÏtw7^ƒ×¹:ø¡QÖƒ5Ö—KÂå b¶2ü(Ñù;?{ù‘¢8v>(ðÄ_Ü££H*eØÍ1®‚Æ nA"Ÿ}hw¾Ú¡s #îɘuŒfÃdðí��aN†E‘=¡¾ÜÁú³Ô á–0«sT|ŒCYîcßRã™eYçã'šgˆv’J¸zç@{”̵0À‡!„)ÜþÁ]OH,- û "š4UZ@’Ôc´ÖÅRQÇqš$išš$ek5)"bËÖqÈ÷ÆsImHlRÚu6¹…$»†Ò*5dOb$RlÍ‹À…ýƒk€ç10ïzò¸"¬EBVZå§ŠP*âÞ�­f6qd$Ël­•LD¤•´˜¹V«Ykå¶lSc©¸%Ö:B„$IÒ4ef­µx×�°1Æï»DH̹á@–µâ °w6.¤¥n—Æw–qyÒ_a+Êæ²cÀ|ú­sß{ñùPµ"…™åRs`£,[dtL2Ÿ÷”KD âÈ@�ŒÖZR*ïƒs­çÅ`€4I¦4¥)MiJSšòD4{ì è¸ f0sà¯l€•’\r†s8Ë2PJ�B^­óZ àœÌór‰9÷F ƒpFHïIÌ&µZ£$ïÂê8Žºè_„Jocí2_ˆL…]—v8ÈçáƒubjC½. ’. éf¯o;ë.æQ¡À© é5põðÔ· }Ë1 à�y}Ü'_?aUÐÙ×Ùž|: 9<<H„œŠyŽÀC9„Ñû¡–1Ur“e¶ÆXk¬µîÐDd ’i�Œ1Ö¥-±ë¢'}|£(Þ3„š³Í¥gÏF¡ñy×lß±ùOHÚ‹nØvG¸+8ð×!6t…Ïmÿe]¶ŒÜDŸÝp@I§zpOì]ìë}Î}58»³“Ë$çº'´Âß‚ƒ± ÅÕ·$«žÏUÉ`™Ù¤Æ :µ¦VKâb¡{È(Ž*Õj¥Rï½")�5RÇáÔ@°’£�$‹‚/ßM= P7¨[ÍÌbŸ—~ ²’IR·‰ÈY°¸•\®ßˆÈZ«´b�°ì½œ�}æI¿¤³½�…Ž4–™µŽ¢HkíFZkkµ3GQ�I’�@G¡õ‘¦i’$Ž#��Ù¬ÝIã¶`™³–ÉW&Ô<·Ò3J1ïX$UÊ=³aj…ý. žñ«*ïŠÞ¸éí_„ùçX-òœº“®=ˆèÜq‚Kˆüq?Cè2À³«œó}ÊW�šÒ”¦4¥)MiJSþÏ‹¨¡¦N_òНÄxçåŒÙ>À[ЪD:g6¤€ýþäK ½í³î�·ü[2žÕÉ«§Þ;3��KP3�"ÅÊÛù”¯ƒÇÁ=8äÅòäø°Àà›«ÕúUÇ T,s^Öy¬!Ì;3Ò†[83 ûF毮³go™³¯ü¨`xŽ)¾ò>dƒ‹4äÇ…×ÙNg1FG/“ྌ#ª’§¡Þ£\o`f<WÊ{’{óbD€&¥,pd‡8—�"¦&µÞËš I¼7�ˆuU¬ÄÆ$4©!RˆÊc*7*1ƒÃs˜ûç`ÝQ×oà"ÚæRôÊzr]a„ÂÎߞÓœbÈÅå¨-’`_ÿ© ÚGp9 ÏßÕÏô«K–(!ºó8+)Ðs0Übmpiø‡[™å±~¾ ˜ÔµÖrDjjimiïhgàjµº|ùÅB±T,F:’‚Ñ‘EDèòâØ€¾scÃlælýyš °•sK×J뎀•fÉ„Añ5@D›ùø� R ”Û Ã°:>O!XÈŽ$ijÏi˜Mˆ8Ž™™”Pd­­%I’$(¾�Šɤ’ªPGqG‘°’ Ajd>#j"�•ŸQ,Ù‰€ÙB8àÑ'¹U8¿ŒÂ­{ã˜ð°Õ$n�� �IDATS=»¬ž ö,[Q~ÚúÝ~<g…~²Kþ[PB7²rÞƒ{#és<±å;>„¸C|Ë]2]Ÿswàñ?¤É4¥)MiJSšÒ”ÿfYGÄÈIƘœE:óçQ’ÁJ6x°„ÈÎ ?ßÚì UÎ(íÇ ´QpìÌEF!–¼³ë�(e9Ãq@%^¸Ö KV/Öì|¬f¬xÕ›Ñ$³=û 䃨†ë°‹¯G‰äÎÙ9dNgçž9¢£ïPÿÏLÍ Å…<úòÿeø“³H×݈ƒÍ æ;Î@½Â …® çŠ÷ÆEt•Rr è @'òupÅP(ŒsøAÚJD…(.ÆE¥É�KL»"B$wz"`¥R©Öª  µF¤$©±eBbkµI’�0Æ1�Xclb ”"ÃÈæÆ¥a4³ëŒëc:ú?ô68ó(®k¾Ykó *»]‘õXçÁuI0»{"ÉêE"td}Yöy"ÃXÈssµ¯"1Š“³âº8RмóEpÓpÝ>g×#"E„,=OnÚ° �€µ“²Ö(­Ó4%E%)­“â¡CâB\î/—ûûÓ$‰¢(Ö³5©QDÖ.ãçè�Dr+l’«`Rò9 [qú`¶ZkE¢IjÒpiN\(¤i*]*Ùÿ ã$y*gaš¤ŠÈ1#ˆŠ24îNpt× ’1FEZE�’45©†8ŽÓÔÄqÌÖTªU"jïèÐZ¥IR­V®¶ˆÒo�Ƙ(ŠdQËÐû!�c]wúXÜf‘Û' 3B�q8j˜™€Ž¥ò¹²¥Áˆ?yªã0‰TàƒÅÉÀR!gŽºŒ‹ì¾@_'ïà\BØ}+9=7#åxÎÀýH±'.Ð?G.¯:h²MiJSšÒ”¦4åÿ¸hÑù½÷¾K À9^žP)¶–Ý!ô¢a;+XŸEÞqδH€N#F› Óuá ¹¨o�pÀY ³uеÙˆw]f¯þ J©ÕjòÞ¡JÈÔc0]ú„m ™ö ÞXÊ>¾Ë—n-3+¥ºËݵžÀÌÃ5œùA`.S@þWºWXCU3©|±â›éjVϰÓþüæÀð�"ªj1"³Å`®oxƒ04VFð°ï7np$Aå²ÊK:r% '©앳ܴÒZk…$çXk%Áµ °5ÆDqdÙZcÁBjSB"­¬µJiD«H‹²lU­ZµÀƘ4I•ÖÇÌ,ùä#Òap8—Šƒ} ƒs D.AŸ³y¢‹e@@QìÉYò?Ìèc˜!œ° –LP¤;ߎȹsån‹œa•2˜Ê¾ƒàïtTÊ/IrÃÁŒ€l ¢sË·l%z_)e­Ñ*RJ¥&ÕJŠŒ1ÎWßgÌp )¥”BD9àÐCl"”¡DEàXБFw‡ª%IoooKkK!ŽË•JkK+XN’Ä�‚ àŽ¥p©i·¦™…HBÃ$sŒ´ÒZ“qÍq¸Ô™©µµÖ°eä(ޱZ« qÃìÎÑT�:ò›šµJkET­UµÒ±RÖZ"ªT*Ö¤šbÖJ[…65š”Ì.Å2²ŽakÁö—û[¢V¥URKÊ•ŠVQ¬ãZ-‰tœ&Ø–ŠÅB¡ ¥&±l,8ÃEF��€"m¬%EÊíÖ¸Þøž‰�1ImÁM �jðp¡A ï9ÚÈ÷ì‚'äFo¾÷d £r7“'SrT¨ìÂr¿ ó@€ >­¡ÌXç à‰i£@r”�‹‹s­ mÿ(/ÐäšÒ”¦4¥)MiʃpæS u'¹£Úó­uÖHgæ$'6D`–Üãì-übžc¶ŠÑsç®ìÎ2†S³&{—„#êü½ ‰rZ9�XFð‡›Yï6°x UJ‰<º°lÑ·È:CzP|ÿ ƒ;Ð@°Mæ�Ï9¡×Õá¹Ë¼ûuÝx¸r5ð~b’ Ûì«éA«á:2";¸.”/=“”½E/3ksf›Ë—'çÿþø±úû§ã@2ê„YG@l ž”a@RÀ`уB¶.z<; ÀQj­IÓ¸{®!t3s´ÖD¨µ¶©a¶q›B1±¦V«Õ’ªµÖ$)#(¥¢8ò–ûÀ´@¾KëØ%tu¥ÐÑ F¬Î Q1‡,kžJ#¶†eÕ÷0 •§˜„#è #¸_E1•�Ë .Õzò/È%ùDB-á'þŒ@¹œˆ4)R$Aïb3'P � ùDêOH¤”Æ#a­Vs¡òJGQäJ#(U $?ð|*ïfNJ+Fè/—׬^CD¤T-ISc"¥’Êy¼ éö1 ““Äà ˆª~GæJ­} EDh­–ù%™‰(MãJ¬5Æ2°ÖZk]«U‘P)MJù†Òº¤‘²Æ�¡­5XŽ´ìåö$ÄŽÎîëïCÄ–ÖVklšÚB±P­TãXGq¤•f6Iš“ZÈ›Cæþ½Â|÷�‘;ä2®Pz@ù”Š�?•-ÖìOn5±Ë*" Ÿ…ÙfëW…{F8‚”TÝ ¦l»@O*å›ó�OÆ9qüœøp„Ô:YåÜz!äp#3ùØuF¨zšrÀ6[×¹éÚ”¦4¥)MiJSšòß!ëQo´h`SÀ r~ºÄèÖ¦bÉGek­1©D…r€EÞLD¹Èj<bÎÊjà•-ôrRîØ3k-!RZ)c xû3ä4T…YÚ1ôQ ¢Á{c{ƒ°RB@ä:Á+å¡:ÁÀ� îÏRsqi×WGj4 ˜ŠyázZÀÇfXЋõuòž)È:p`Š/¯öæž]GÇŒL»Çºj23Z—…~Ýè"t5ôéî’,ùÖÖѾ¹Þ˜s' ­@D9aA òN'a,ø°s•Ò¡mYKJÉ)qì0¡ø—[[­V­µ‘"0ÆMxËVΓӱ2ƤišX#�Õ‚Í›KÍ›Ër˜!)ÜZï}aw¥©KÇ‘ã¤Ð@@Œ€–-Z †¦àq“¡ÁÐo�îô=©£0Z(äC>S= ²d~xo!Bj º$q$i"S“;ÆŠHgHI–ʈ"RÈÌ–*pÓ H XÖDh-[6∄ˆ@Ο½oä±¢$ ARŠªÕª±V+ÅQ-©õ¬í!¢5kÖ ¢&ÒZ“œ�HDLý@¹e•yKÊÒt—§P¸òeB´ l­dĬ%µ(ÒÆØj­&÷*¥Á˜‘Hkˆ~溄hLÊ̲u1€e«”²Ö20i¢…€T¥Vnmkak«ÕZ¬#…rŒmB@˜ÔÔ’$Ma/üPÝ3áô¤¤6�t»PdÎ'¢n‡)ã‰D÷dÏeù!ó{CQÉ#mfQN’{‚rQ<ìR¸ç GÃÂ~Ê�!>’ycIU0l‡¦$ó‚PÍì; Ì(ÏÅù¹ÁÙ‡uASšÒ”¦4¥)MiÊÿ)©CÄ�� 9œ{çÇó¶8ç©ËJBý]Ž:�`¥È?Ì� ɲe˂̘ÁXc ˆ¯2䮑ÐZ¦ÜiäbÊs1Ã�yã"0"1ƒe+VP9XŽ“<^HÞWݱi$˰3]ú.iP ½±ÊeJ÷j¤kù†*‘Šò@¼[g©óÝçMÈNß  xP×Ç€7ÊyÜ €ƒ0.Ë·³Ð²OÌŽ!vƒógºÕ  7zVÀZβ×aî²:ÉóA¾Ðz#dΙ…r°¶>¯a† d,Œ±\"ŠºØ„á`Ÿ”Ù@  5®®Jù»˜9©$ÕZ­fMkk›ó\`6Æ2‚L™~˜¦©5ÖZ" +|ŠgxtÿPÎ,è2IdtŽ'Àì=¢óFÚ:ÖÆ‚ }ŽáJ�…È�ŠÅ+ÝqyˆÖšAŽ `zÒ•ëI Á·è)½ltÄ3 3í:×t­•L'EÊ¡j?‰ˆI’b_M%ˆW²€e&a)ŠÜ£}Š\?„h p\!�7˜-Š#Pd­I±IR(ˆ("EJ¡ °l8OJ‘-ÅZ‹è‚F<Åä@¯µ6µ©T}î@DLÓ””R�Iš‚Öš£(²ÕªM¬7Σ±ÖZDZŒ²Kßà@ˬd^)ÀÈÆ[`Ù=‘Hk €ÖÊ!޶Mj µÔ ÕjµEZ«Z’ÔjµjRcfÔZæ•ÐgÈuësD’ÄÈÒGĨ@"¡ðÏœ«™ë?™?yÃ{ŽÐ‘ϲ} ?p²i³x E2"Zɶ?š;Š‚\Ê·ï:¿÷Aø >€îtÞl·ÁŒ]p̆ƒ[½ßšìª!wcCZ“/ø³öÅ7ÖvŽ7¹¯gÑËvôÅÿ?ã—>wõnj=xæFo¬úži#ë¶ê´çÕ<jdÇ 5“ÿìÞ­óx ³¤½¯-7ÃGu†»ïÑ?žÿÈÄO¿ó°wÊÚHÖ,]¥Fhû—ÕÇ®}iî|³Ù»&½Õ"¸ºòµžÒèa¥··^MiʼÃWGõõ§{cè{¦¤zø×Š}óK6X˜Q.3ŠÇoÎÓ¼©ˆE"åNêfF%޵ÎÛ€0�,ÊŽRcg~ô A2Ÿ9'_Q�ý}€@„’º[‰ý_ØD"rš;)%·ráʤ"1°µlŒ aíä�|qè͘Î,Ýin^Ä�(±äóiCN-vJkÁúÅ7¼|IuoB£B1_ŒïüúÒ×)@‚YÞË 9|,œJG¸o{å0€§H¼GGÝeõ-¤wÜê»/÷œÐ@™.nœP)RZ‘;à�•–—áöçËÒ!7iåÓ(ÒZKÀ¸ $ �”J%D¨™4µ©5Ƥ)3èHKw’$årENž“)Ĉ@,¯¬„¨òã ŒÌÈ  ˆ4ƒœç·Ïõ ·�ñôfo?M(dtwsFù™C I!¢"¥”Ö:ÒJ+Rйú‘”LJ†!)Prµò3Qk­ƒI“Z-©°M!)TšäoQ¤ÄaÈ�0¹#똵¢(Ž´VZ‘¼'kM’&IR³lu¤KÅ)…ÎM>pX’ÓÐñƒ¡cÄy§Z«‘R @!)E:Ò-ÅR©PŒâHºB•G»è" \0:‚3¦+…>Ù‡ÄYȤ’¦«z‘IEJ€$ªwª(ŠZ[[‹Å¢óÀBŒâ}N;¥”ÀZ%Ž#­µ4Pi¥ãXGi¥ã%²w12ƒÖP-©U«Õ¶ÖÖ®!]Ʀk{Öö÷÷ÕjUDÔQ¤¢H¥–±a×™B~w&"N%Áe€Â&‰Ž²rüª”X/~ˆr+šÂžŸ•…DIuߣYE¤=µ’Sýñn‹öó\ h@«ˆÀU…r{JÒ•qþ#™�Â\ËÄpä€ß˜\±þjæ[Ý”w¦¤¯_wÊ5yÜ@ïì9gó¹¼ÝþMR½ûg§þø±ò¢s÷ßûÛ÷&�>ö­wÚéœyÒGÏ|ïf'þ­}7&Ýo•#H«Õô­VzP1ó¾»Ë¦ŸüËÚ¬'WÿýÜ/\ò‚î|«€9©TßNu ¹ý [mý•;joëCë„W^y®ŸüÓËo¹ÞÕ«™¸ë÷OÞÎJ5¥)ÿò_É#ç¸müŸÞÖ#¼âÊ£6šðñkW¿õß,^ùöÔd 9ÚÃ+ ÊUãtη��¤‰õɱɇ »s¹F¯å{Ý3SòÈÒbÆ ¥†”éèѽć¬ª.žüJ©(Š¢(RZ‚1&MÑÈ%N8hÎA‰ Æg‡L) 嫜Ѹ‚èÉ.ÕeêwPOsªò@ n¸ÑåB,o°Ïé]'|—ä&ðÕSŠ´V9õ™r° `Ü=$×iµowã+÷0(á®i„W¾ˆ—\ˆ€dtÉÆò÷zâäú.$ñŽv ½žI‘Õ‘ŽtEQ¤£€ô´VuÈ#¥TÅQE‘±¶¯¯¯§§'M¹S¦b†*A®÷\‘J¡"T‚RdN²u‘8Æ>†wGQEqÕñL^Пx¯$ªŸ”û+üMþs"åö“`Ýð"R„]»ÅH®H)’OT�k¤´–%,Á>2 ‰(ŽãB¡��¤TÇ…B!.H³M’š1FÜ ˜%­)’¢(Š„­ñ2@d�c­±†ˆ,s’$išQGâÁMH„J«(ÒZGÁjìh ·CAGZkcM-©€Ž´ÒšÓ4MÒ¤VK’$‘˜é0"-ÓAò*(¡5I‘ÐOH3ˆŠTÇq¡P,ã8V®[½c‚ЩIÑïW€ ´ŠãX˜¥4 K»(MÓ‘V=G*Žù®&ÔZÇ…Xø)ƒJé8BÒµ ëB±µØÒ¨k“$éíëMmb‘)Ò¤É"Z¶Ž"�¿)øWÆxð^G cèä#pË.›JN¾(“'ÀzO,æµ;¿àɹ¸ÒOg¿dXIüVL~‹€ð6«Fn'õA"轌�ýá‹~sË&„SzѱÕÌ’2'G–׃ÿü&‰õ_5Jºä–sŽ~ßä!¥bç¸íýÞËÝ|M]óÅ=7é.µŽÜúÐܳ2÷ƒÛ÷Ô/ž0äÈ¿TýÉ¢ëNÿð´-…¶1ï:ø[·.¨Øe·~sÿ©Ã[Z†n±ß×n\Zyé²ÃÆ&~îÎVùéK>õ¾ ¥öq;|üW÷…Oÿxòî›);Ç¿÷èŸ>¸f•¤¶ðŠã§víóë×å»Õ¿ß¿„y)Íøãš7™e³¿üž¡ïþÎ3Á'ªoÞïNØuRw©Ô=y×.yª§Kn9çcº®ï‘‹>±û”‘­…b÷ÄŽ<ï¾Uÿ¿Pþ?+å».¿¶ö¡Ã>YÇÔF»ýÉ£vC�4°7îÞö–Ší¿êðÛýøÛË4T—WÞ8köðƒgn½•‡ÙÅî=bï ÿ%zö;[Ö7ê"o²6í²Û¾;sÛ1mÅ–S÷ýÊ_5.ùòöÚÇêdá?}Åþ­Ç¦¼ÃåÍWǽ`ûôü‡§¶¾lu㑃ޢÕ)A ¢³OˆE�‡?ÖiÀŸNIíßé (­ƒî…á!ä\É©•RŽç(—¹�Ó@D ²d´OÓÌIݘ…P½Ü]ù)¯âæ!œ‰Ã&‹µÐyÚ0ì»×áN0XÇgé[”UÃ3ÒZ8Õ™ë³#úr…g]/ÆLÑêÁM€:ºÖQygµÎtô¸#€8—)»¥¾N¹ŽÊÑQΞì7ߗʱþ@ÄБ™S3:¬+vi¾Œ2X–%ñ|˜HGXŒ"�¥Ö–¶¶6­µ@o,5Àˆ¨µŽ#G7€Ïú.nÍ üZé^çé¡0ø¶ˆ]Ú““¦I’$ÖŠCºM‰"9b/&ËSZJŽÏC%öÛÌ®ŠÎr.P†×#-òôž'qØúÃïÜ·È‘`¹EeÈ‹!ÝË, +­ …Bkk‹Ìã8Ž£¸X(ÆQäNXÀ@W)$”¼".y(cccRkS�«´*ãB1VŠ,[c  ãœ-<%çýáÁ‡’ †'Â(ŠÈ ù�C¡X(‹Åb!Š4°ÆËÖ;Zˆ›Mè2„@‰ãHG‘_,À!h„dB(E--¥B¡¨´.–J¥–a¢ÈúS0\j Ä0O„/ýGÈ )×"[t'0¢±Ö°µ`eðT¤¢B¬"]M­Å¸¯j+)¶w ï:¼¿Z]ÝÛÛÖÑ :Ö¨PB«\—¬uè½Br‹Î§Bhp¦fÊVãùô!*a2‡Dœ~¡:RQÆÌŽŽSð^QÎ ÈïÚØÿûâf¤TÂ]� q¬‘4’8¾ˆŒû $®0è©@ß×€„„ ÁqCãOYEx.…gÆ\ÿ&ìiƒlÝkŸ¸å‰aÇýêÎyÿõŒÉãè3o¯€yî'G{ÍðÓg?=÷²ƒ–oæ)׬`�^ó䟾:cûÝOŸóF@Ò`üüØ£¯{Ö=KWÌ¿êðžŸyêu Ü.ýã§?zazÜÕOÎ»á¤ø’#ŽûõKÂêðì™_¹;‰Ñ+*÷Ÿ5óäû¶þá½Ï>ð³Ÿ8uæ×îè€ô‘oöÙß󋹯¿ñäo>ðüG|óž:lÿüë¿}Ä;~æêW]‡Ïzm•ÈÊg/>hÌF±{{þ¦dñm?þä.Ûùëçz³Ÿ¸Úýgú¹·»àÑ×^òâÝŸ>õ3ï«æîá5Ï~|`×µŒßþÐ3fÝÿ‹OÝøõÍøê)>kà?QúnŸu=|ä°][²pØ®'Ÿ}âû†¬_äÆD8©%ÿb…›—ÿuÖœ1º~k÷ÛZ5mB‚AäMÖ&¿1ë¤Ã.„o|iÅ+·|¡cÖ±'\²Ä¾}·˜Þždç½ÐW.—Ëåµ7Ÿ°ý÷¬Ç¦4å/ñ‡}ãÞô-ѯo·}§1ÈÝc §`z§8{ã|†û˜YÎ6:¢SÚÐyùfþÓ`îÔ+tê¥`BÆ�PÞßµÁ¦�HÈ�Ƙ$Iªµjj �DQåƒ\‚1p!RŠ·/S€ŽêÀ\÷P�[Äî:¸x¯€†WpœöîÓâà ¹¾E" ¸l¾¯È©þÒÿAû€¬Þô_‡ À™j%ŠÜµËëöùò³Ò¾ÄºFÇ[º°ùPd‡¢ƒ¿@c­‚Ѽžc©Ÿ<ñá‚'ѹm«<I2 IÓÔ¹dQ/n>k‡öµ46„\Ôj5"ÒqDDJ«¸PPZKF:DÓ®1ư@R9üà+€H."Š¢B!.‹ÅR©X*•J-ííí---¥R)Ž J+D´lkI’¤I’$IšÔr’Öj&Imjdµ€@È@rT) FRb^‡@O±Ôu©^¬`ï ÂÎÊ îŒö%[¡Ô/©ÕjýårOOOooo’$…B¡«««»»»£££ÔR* që(B”¬„aˉ�:Š£¸P@Ä8Ž[ZJq!fæÔЉ[Œòh’YZ5ÁSÆ1‚òµN’µ´¶¶¶µj­Ó4µ–‹¥b-IŒçΦ, çŒ!r[PØ ¤;”RQG:RÎY�Ñ¡SÉj‰iš"�“’¢8Ž IRk )* â’$5�( â¾Å‘$‰°–]Ö�7ÏI¾Õ‘–í‹H¼Ÿ¢(Šåe,¬íë[üê2Ð¥µådÞ³ó—­\Ó=tTça•$1€Æ˜$I-ÛX«B+EÀŒ(©X27�çòC’òQ)¥•΂ ü´ú€…Ÿ$TG&ŽóÎ÷¯†Ý8°ùµé÷íÆ}@Jòƒ«|¬…þñ¼³§rËYÆÈ»^yg7ù"—ÉÂ%²:ïƒ,]eX*;Î#»}ƒ‡îwÎeç}b÷é“7ßùø“gŒyã…ù«ÒyúýcÛ|þ{ß~ãM÷8íÌ#¢ë~{Ãr0K^ìÛùÜÛ.;vtFï¦/<ùLé}}xÓ®¶Q;1cZï‹/®¨SÞí’k~wËÈO|ëó»Lž´ã‰ßúÔÄ»~{Å‹NUï½ûÌO_±Íç~¨c`Õj÷þáKö:ý[OŸ0uÆY_Ýoùe¿»½ Ð÷Ì“ ÇìqÈnãÛÛÇà£{Ox}ÁÂ:+?¾¾àõ-Nÿû _œ’QKgWWWWW=tÞÙwïðófŽ!0óöÁQ>vÕr\½à•៼ò¶ïî‡{Ì¢;nyÚ'í»qgû¸]OýܞˮüóC ¿~ùÞï¢íà]8|»}öØzã‘Ý­8tòä!ëñ½O¸ë´=.<vûŸ¿ß•¿½qõz� ¯^ú×Ó/?y§Ÿ³ëï¿sÎsK«À«ŸùÁN_ò°�¨½rñ^¿üÅ)�@ºôºè‚ÙÿœÿzÏœYö?lç|¤­]tþûÛwý‰3£'}ÏÉÝÅbçÄŽ»ðÑàПݘ¼xÅçvßdH1*vOÜï‚g*÷}iÓÖ=~!ÈŽßøÝ‡;¦œþPÚÿäoŽÛq|g!n¶éQ—Š£Iúôw¶±°ÇÏ—X€ò3—}nï-Çv´tmüþ/{® �üƵŸßyʸ!-qT>í€/Ÿý…ÛŽï,µ Û|¿3o}Cª’.¹ñl5ªµÐ:j›ã/%ë[^vý¬;&rèVJï¹þØ‘C»b-��˜Î}oÛÎ?zѬ¾ï‡3ß5¦-ŽÛFN?åï½��P»ëó+D,ͼ² Àk¾ðøÝ¶ÙÞ2ló}N»a±�³à’cwØltW)Š[Gowä×Î:aï-Ç´—ZGm}èì‘*V_øó>¸ù°R¡c£÷~é¦vn^yÓ{o3aH).tL:ñÆò Ù×nþú‡¦o‰ cwüæý �pÏc¿þôn“‡”Š]ïrü…Ô[å“{¿¸Ië^-•û¯?fĸOß\ä¥ëÏ8`» Ý-íãÞsô/•*rÏÜ_r—»ŠÅîI»}ç7q©~“µi=ùTù]3ÝjhëЭ?ø=váü:Ï¢·r{¶xíªÕ<däÈǺ‹)àYMiÊ`Ò7ëàöig<’�Ø—²kûûÏ_d×µ:ÒÇrà¶“F´¢b×ÄÿÎì¥o¯kÔ›KåÖS·ÓÝR(÷ÉRì�� �IDATvOzÿI³æ¯gÕòò»xäã;Š¥a›ïõ¹Ëž-¯øýþN¹#�¨Þz¸Ç\_�HîþÂ䡽rmþö»wíæOóéÙ5€Êgî¾åÄam…¨Ð½É^§œuúa;NÒRêÚx·Ï_ó²�~ãú/ì2elW)Ž[GNÝû”ß?Ù;`|ë¡¡Är¾¼µâãoÄ*~¼Z;ÕN»(‚«�rZ‹)@DIÙ¥”RZ¾ˆ¢X’¨‹Vˆ)˜§À¶ˆ>2ZGJi¥#Éïå S^FR¤$™$É'äl´Ö-­-QI5QE©I]"}f9mAêÉÁjï»'Cž uÐDƒŠYÿòõú—R*o²”æ@¸ó¾w5!Bqœwz0��cBD±ÜèAµ»Ñç>t­ŒH+@´ §X†¾þÄ2gñÉ„AŸÏ+Ê·ŸÁNF•‹0ϹùŠ•Û_ ÎßRv™g–"€¤œÎ{M#¢lÖ†)ËÌÆºÜäP"QÇáÐËyð,¶}ÁG2ÍK--ÆÚB©ØÖÑ®´&BTDZEqLDB (-çi08zE)! ˜ØB*ô„ÜÉÈŒIÙBÔZ! )¥u¤uT(;Ú;ÚÚÛK¥–ŽöŽö¶ö¶¶¶¶¶ööŽŽöŽŽ¶ööŽŽÎîîîö¶öXG6MM’‚±È€ X­T+åŠñe¢&à8Š¡µ6Ž¢BÉ0Å�€Dà¸$ñ+ñé-ØZc¬5Ö"*­ãB¡h 33)€D:Š ‚áeØ%»¡1ijRk­À?kmš¦€ÀÖ&µZR«²µ  ´RZ[kYëHûpIN‚2Ó”R¤âBŒˆµZ-M Ëhj¥µF¿•„ø R¤•*µ´‘5FªZ,• q”&I1ŽMê*&QL*Ž,0EŠI²¢ÒJG7"O\kl8A˜”*â8ŽјTÒ1‘e«Eq,«B+ÀÆZa%=FÇÅRI¸K"’¬‡:Š�¡P,jå’kHòq^`Rd˜(eŠ@‘ÀHÕR³í{Þ»×¾ûíñÁýÆNÜtÅš>CQ¹–FÅ"¡’HÀ†ý#º&äìM·ÀŸWá×$³• µò¡[o²Ý*7Ç!½¢½X<‘Î÷ üƒ`Œ1ž û�ˆg‡_œàéU¿=‘# ‚ñ�˜mjŒ±FÜmPà¿Ç÷™ÿIpHq±>¨”FŸ™•­Ë–êGß »"•wËjø™lx¿n©<1ûŽUïÚk—aågž|±{út!âiÛL5O?ñ| 8dŸ3ÎÿÒ‡7­K}m¿ßÕÕg}ýºV¿zë³`÷O4±>ÙݳO<S¶ÞB�¨M·™^xö‰§S�€þ{Ïþì_wþÉ÷÷6ˆæn_›7oåø-§I’¼ÖéÛlÒûÔ“/hßùC»¼þ»¯;ç•5 ¯þѬ¾C>±ogþ¾ÒNŸ?ÿë3·êV vá%ß½¼ó„ÿ=l�ul¼ÝŽ;n5¦„ §ÿÃï||‡yó²=e‹ögÿzùË*iuõ«kM´ìå% –ÆmµÓŽÛN¬ËÝ—ë:��~í¢½Z[Goû©{Þ}Îw¹žÎ×›oyÒïŽþùœcN=ïûö½®]Çu¶çŽÿ½áú7&ÿÇcø‹÷ŒxhÎÏ_Rë;m³òü'ÖX�³pÉó+ª/>±Ò�Ø%¯>¿jÄ´wÅëxÖÉšÙ³nl=à°Šë¾;¶?é’ûž{ö¶ï½ë±/ø…¿ Ín¬Þò­Ïþ¹û´{_[»ü¹Ù?>xãâ¶Úwø#¿u@ÿ}sìÜm-×\yÆîš~þãË×¾öäugî5Š��Ô”¯ÜÛS.—×Þøé±ÔçžpÇfgß¶`ágŽ¿ù3ÇÿìÜ·èÑ¹íŸøÛü%/?úË]_8ÿs·ûÁÍOÍäW{¼ôý“Ï{$0ó~ÔGU=ü,xvΚ˜Ù·ìÒë.¿g“CªxECéíïÿðîtçßïî�^~眧7Þ}÷q‹~sê×çðO/¬Z³øá+þg‡V��ˆÞ÷ƒçúÊåòêK*ðŠkO>àŒçßÿÓ¾pýgÔïýìe¯1ðêçš·ñW\´tá=ßÙìîï_øÚ‡/ºóÙçïùîÔÏøâ¯Z€Ú#ß>ä¸k‡|þ/O¾øä gî9rà"àµÏÞu·:âêç¿òÔì3ÞW¬ ôáóN9¿|ÌÍ/¯^µðžß}|š^yÃ)3N}t›sn{îù»¼ÓÓ_Ýÿ¤k–çtåhÛ}öì~äÖ»×�Ô¾å.~ÿÞ;ª'ÏyÔåm§\ÿìKüt‡Ç¿ò±ï<”�ôÜòåÿç©gßòì‚G.ÿüo–ÊáMÖ¦žºÏ>cn?çK—>¹rù?úÙ[wdCÇ[¹=¿íòeËÍ Wë¬ýîÆ§Wåx¡ ^MiʆË:V‡]6ïþÅÛ}ïáù‹ž¹ý'û.¿à ¿ÿÄ¿7eAa›Ïüñ®g—¾öÂuŸÐ³NüêÕë ´±¯üöãžóê~¿|`þ³7}}ÒmŸ™qúýÝ»ì¾õòûï[`�ÒgïºwÙª‡î:0 ï½÷ív_Î oÀþ™ßÁÒ%?XùðïŸ]¼øé+ë½øÛ7=ãÚ¹óŸ¼òˆêÅ'}K€û^|øÑÖcþòôÂ^ù?c朸ï‰×.Ûˆöª/ú“€ˆ´vGdAàé~µóyø½ïh^ëÌù„{ïn—z ¼ëBØZœbšóðNÇ™mÊ_î4ùü‡è½…‰(5©S…ZPºPˆ‹Å’ƒxC8}—ÅLå=Æ]­=Jüµ®ÄuÊ�ƒ~(Éë±þ师\c!À[KöªRþß@Twê¤tfB1ýÕs¹‹ƒ%Üámè3`8:Ü6ôH�óè™ k ûùþ.ò“¥®§BȲ/?kv®ýÁ<_õ èC½ 08®‡Iè|‹åB�0Ö¤i*€!5Fk])—×ô¬ÕZ·´´HÍÅ‘$!¡)Ä}!ÌiÉ¢GŠ™S“&ib¬‘n¯T+kÖ¬©T*Õj5IÓÔ¤µZµ\q ½û7Q¡P(•J¥R©Ç­-­Æ 9b䨑#†>lè!ÝÝmm­­­-ÅB!’œö²\•"qÚ×Z'iZ©TÒ4µlkÕª1†”*ÅG¾¥X,– /-~QE‘VŠkÕºà ? ,GÚ) ‚µ¢(ÒQ µZ­–Ô„/`¶È i …b8à'ž¤-Ì »~8£%I"@â>æ¥v™êŒÕÂ%‘VH”¦iš$n‘O[¨üÆâ&-{ÔÊÀ–Á§¢”Œ˜.…Kq4øP#—ÖBk­µ88(kezkƒ£S¤µ ­`f $‘1Š£(Žc©^ˆñ+T¹d“:BÄB¡¦IEÅbiá¢EÃGŽÜkŸ}÷ýÈ^{ì½ß¸‰× ×R“Zfî4ŠâHk’^"MEÉàˆ9›{Ø’ò=é)ÉßÂ({3`ïBhtZ ;Ë•xWåûoHÁ%ŠPI\€Ï«ér>rðÿÀl‡’ ž´’˜ôq 1V¥'äPÕ쨋àG¶ ¿Q€e›%-¨¿`ƒ%Yø§Oþëágýòä-pO¯mmk“)mí­Ð»v]T?›qÖ™;ο脽6›¼ï…éA'd<ñò‹?ÔªµÖº´Ã÷ž]ÛÓWlouË[Û[j=½5€ôéŸ~éª-ÏþÆîøHî=uÓ‚ÖZGÝG_·²§ZÛë*ÑÓË@ùÞ7~àÇì2iÊ׎8ö3» ÝÐÆVïûéO~à‹'m#˜Gì÷Ýkf}iÇÖu\ÞþásþpJÛŸ˜ÔÑ6rËý¿5g….´¿ï´Ë¯þÖÞY‚üº®“úôM«W.¼û‚9aÿ¯ßWYw°«k£ZZ:Ú7Ÿ9e“dÕÒ×wç¥/Üþ`ë^_z÷Ö“Ú†MÝâ¨ÏNîûëSOÕÚ§ïØ½äÁÅk™W=²¤²ÉþG¯°ÐóÈ+K6™0mÈvÊ å­ºqÖÍ]¶Ãú¨½ùîî¼Å„‰Û~ôœ3è¿úÒÛúëo¤¶Ž–¾EO>³,i±éfcŠPØé£‡Œ¸óÊ¿½ÁP{tÎÝjÏíXŒÚÛ «æÏ}a¥m=e“n°QÅÞ\¹ã’K_ß÷kß>`³á#·>ê´c&?vãqÀÒÐ1£†žò‘ãÜÄ´Nx׿ãÆNùÈÇ÷ŸðòóÖ²yñš?=8éÄs¿¼×ã6šºÛ~;Œò´‘]ü—Y÷O™yÈf p@é{þ!¸ñÊÛ{úîóðè}ö›¦‹ímúg[¸–º6𲱋¸@Ò®ŠÄ+ÿöÛ¿¨#¾yÆž‡Ýñ¤/ÜzÇßï-��µ ;røØmŽ<nŸ¡Iפí7»Ñ6G³WÛÓ?@òè¬YówüòyŸÙy“±¶Þgïm:À<wÎ{KZk­[÷ûÍë �Ô6zÒ¸#6š<¶mÕ`akG[ºäé'—ã!7ߎ¼êo¿¹ ŽøÞ÷Ùzüøé3ÎþÁÇ[®ýÍuy¼ø¾ö)Ý~ýí=�Éc7ÜÔó>P|ìÒßÏ{÷ÏùØV#‡o6ã«'¼ç¥›o~ÞTî¼ôʵþÆŽÚ~âØIï>`-Þ$JãÍÖfëûO;gÆÚ?ú‘éï|ÖË»žòÑ)Ñ?}{n=Òˆ}ϸàä]ÚV=ðóc·Ÿ>ãÏ:p¶áë±)MÙ`YßêÐC6š´ÑØ·ÙÿëíÝó.þýCÿVž�‡n²åä‘]Ýí|ÂÑï­=ÿì+ƒ»©ÙEW\<g̉ç±Ï”q¶;ò‡ß:põ¥Ï©Œßc¯Íž™sÇël—Üqgï´©kî¼ãeËËïœóÌô½? ~›ý²?GƒíÞ¹jºG>ròîŸúè¶QaÌÓÆÙd÷ãgnµöÉ'K¨uÔ¤ c7Ú|—Oþüü£Õ5¿ºîíã êöó*“¨×Œ�$#ø`ÝìÏUïBîQ©/@Úà#|«–ó˜Á²„àµ^ÑCµ*ã�'埵Z’¦)³õ±Á£AYËÖ�$ YÊC,}f½z³Wª¡N1ü•¯Øæ®©S¿½E&Á±¢‹ðŸ¸‹šôc÷ÿÌG#Ë!—} 4(<Ê}ø3x-s%ë?~œÃ{ð˜Á}…¹3´á#[ üEÈþ†·2qÀN"˜ÝÓB,´ã5ÄÿC‘RäÛîM>~YùÔõ¡D”XÄ•jµ\.#a¥V]µzu¥Z…Ì0 V̘|kX,üÀL€U¤´BÒ¨4’&R¨hJB�´ –Á²IÒJ9©VÁÚ¤Z­–+•þrR©&•jR­ÕÊ•þþrooO¹¿?­&þÈ@PH¤¨½½½«³sØ!]íâÐÑQjie Š¢b¡TŒŠÅB©µÔZŒ‹ij“Ô¤µ4©&µJRî¯ô÷•{ûzÓZ­V©%Õ$Mù¶Z®ô÷•‹q\Œ 5ˆ”fÃI¥–$ÆáÀDbG €ijÒT"]€\è¼r,ƒ÷óˆäE¤Ùý€Ì(±ÖB8ŽDðx˜åèÂl?ac­BR‘f’Y…\``Pà<XÂe¶v�VZÉ18Oõ^0bKÏb£r,¨±V²$^CR‡æ3ºþvÇ�`FX™ âBˆ QjÂ$MªµZ’¦WßÒÚÂÀÅbÑ�[T-mñÐÃ*Õj¹Zéì쌢°Ë3Â�–Sɪ )ÝôÛ§Då;è,·øEÞÔ1 ò_²€_²~÷®ã 0�€„žñá �î0 öǃ�(ùDÞ0ÈÏCø6ü&å÷šŒ”v\µ(ìQ¹zÊ·ë8~[¶/Äütª÷£ ôMcê >Ì}ž,øãÑ{þÏK»òÏ'O+��¶´·Q_o¯`Vîí郶Žuž”ÎûáQß(åÞ‹–¼t÷÷6¾jæá¿x‘»úùCsçÎ;÷áˎ߸£½µÒÛç4îëéÛÚb^qí·ŽŸùÚŒütÑ6§þõѹsçÎ{ïw>ÐÝÞ }=}ùJ´·!ôßýµ#/ý“Ç.Z:ÿ¦ÏëŸÌøÔol˜nQ¾óW¬Ù÷èÃ7”V Q{yÝ“Ëúj•Õ/ütïV½é”MqRc×…[‹Ýwúäù§íòâŸ._·Šh߸ížvñ§wúù§ºûÙ$s p]ï*è1ÆýØFc:»«½+{pÌ·>õÒSkÊOÝ×3ýSÛO]òò¼••§ïY6jç Ãÿ ×j^ñ×Y·þþŠã'Œ¬.{u ×Ýí|Ö_.ØuþWw¿éž§\òDCüž£ÜøŽß_ùJõ‰¿ÏîßsÆ®­Ð1ãÇ9só»>³õ¸©9ýêùÕÆGsÏÒ¥kÖ\uäˆb±X,¶nÿ­§Ìê•+ëÈêèê„þþ2��¶w¶C¹\»lé27aÜ�Ÿ»èêËÙjæÁ›(€AJoßëØƒ‹7üþo+ûïþÛ]š±mDcûÍ'u\>s‹ÞuØwf/ qÛ7/­,¾hßÎb±X,vì}ÑÒÚª•=u#Iíå¾²è&ímÕþ €]öêë-¯GÂjãO\öðܹsçÎ}è5ð_ƒDÓ¾pÙfö¿÷¤‰;wÁ}+ؾþʳÑ䉎âÑ&祋먖]ûHaöU·õ$]sýª=Û»Û¾ºøÕÊퟛÜR,‹¥?wg²zÅJÓóêkýÃ'l´ÁG¼½ÉÚ´/ÿöø“ž:röó /~ô×»<xüŒo?–¼}·ßî c?ý¹ÿ=ïòûîÿÁÔ;¿yî-ذõØ”¦¬S÷fÞ ÕA#7Û´ãµW–þ;b$‹®=}ÿm'ïl:ýËwÕ$út1K/… “'¸µâÄI£ËK–¬¦)ûí;þá›æ¬xcÎì—÷üßÓ>°pö­ËVÝvãÛì·Ï¤¡ù_öî7Ù½ÈÝ×Ï�€ííP‘Ý:/…M·˜h¶©RØ+EAÕ+ùPÞìð0ðîÂèÓ¶³ÏHÈ)ñ"uñî*ƒwÞÈ ÞÁ��){å™Þê ùÝYd5a00ÉD³;¦rØ9Å?<ˆNÓ4MSÈé|’šÿ!©Çýáý  ?\ÿ 2CWÑKÞx6!Û!gtoä<zd/Ö“APÌÅàßSî ©ä®Í•™÷Fp˜>§X;ŠD xFgxvÉuŽôTßÌPX¡¹è Ið/.2ã$Ÿè™Ü|!d›R*3cæÉ‘÷̤i­VCDÉêßßß_.÷'µš8¢ƒA™¨š”Ž´R.ƒ¥œh §©MkƲE k V©VkUB,µ”Ä¥]`…r‡ÒÒoÖš$I„¤(—Ë•J¹R­V*•ÞJo_oOoo__¹ZMÓ´§§§Z­&ÆÈȳi)µ0Cš¦Q·µ·uwwwuu•J¥±ãÆŽ=zذa]]]ÝÝÝÆ ommkkimiii)¶”J-­¥Öb±TŒã45&IË•ŠI’(ŠZK-Q¢XD”Ø#ñçG"ã’€è8Š”ÖËÃõa&ÙHäb\ÿS)¥#­#-k“½÷‡¤/ì ù(­µ6‹ ˆåJ@da­eï/@D‘ÔŸÈ‘('D¢÷²¡ì�R’Œ’uóç¤pFAúI‘ÒZ\–ÈGÔ»Ùå]úÃ2’­U¾Ô9ëºÜ%û`©T* À\,•jI­\.3vá¢E÷Ü}Ç‚_~ô‘Ç~àÁáC‡t´w¬Y³¦¯§G,ãÌ~¢ø�Ùà=„P·K�dˆ9Kÿ è°°B‡‡uÉÀaÛÊâ²­@&…ĤÉ.ID °üÏ»Þȸã|éa/p;¹¿FH)÷ƒ’‘ëäÜÏ–[�nöèQ𒓤FŽ`ÄWßö•¿°èÈ«¯ÿÚN]îæÒ”-'­š7ïU {jî3jê–› nI4Ï_ûçǧzø”"DÃß{âi‡ »΃ýªkÂÔéÓ§OŸ>mò°Ö-¶šO?ñL �`^x|^u‹­¦èÚý×ÿýå{¾´…FÄÂÞ¿zuÑOvyÌ jÔfÓ¦OŸ>}Ú”q£§Oòò“O àê›7wAÛ´-Ç«ä‘+¯|彇2^Caܧq¿äÎ[çnÞŸ<ü×›ûvÛÿío~i£¤/^rî¬×·>ä Íë`ç`]W'2|ë$1úÌúÆ3êÐçÝuâ/¯ÞeêºA9 oëæµË| w²tõªBkw;¨-&o?lé#·ÌtÁèmvœ°õôåso™ÿðcíÛïþÏ`óë7\~ûØC6<Ã_ß‚^+Ûõ7bÇÖÇüè¯O-¼÷¬ñ³Oøè÷MAO?þ¤÷ÍýåE—^yCïþGíÕ�4|§Ï^8çù76ºôcÇýâEKJA­Zó~1m#F´;ꪕ‘j­üàéSëÁ¿lE™ 0³=n4/|~acT°YpÕåsß5ÓÇÅ (Jï?ñ“g_ô›K¯šÝ=óÈb�Ðã>xÚî{ñ™?´ü¼™'ÏZÁJkΪHÃF(Lþüí=®Šµ¤ïïÇ7x¶ç·ÿV7ªÿÅù¯Ö+Æñ°IÓ¦OŸ>}úÔ q3ë*¨´ÙAg_ùè¹¾wÞ‡œ>»6bÜõÊ‚E.7EºhÁK8z\}HCéý›ÙqÓ¬ëožuMßGŽÞ»hĨ­ûürqÙwsuÁy»ÄícÆ´-›¿`í:gp½¼ÉÚä7nºü¶3w;Rç–G}íS_˜sW®ùÿäíyQc·š>´gÙ²þúš¿ÉzlJSÝÚ­zcEÖÇ Z¼fñâÞa£F ÷/’µ×žvÜ%úÓ7¼°¢gù¼sw[·G˜3n4/šÿ’Û$+‹¼Z3¦ õ6Í}÷uW]uÃS;î»×÷y÷»úÊkïÀ[¨ü/{aýsPqÛŸsëv ]½˜%//…á£þv»QlæJî4BÑ!ÅjK^×’š9k~Ш‚¾• è%w ‚x+“¿,˜Ô¤‘Úsî�mŸRüO�3ÈM9¬.uckScĺ—ºsVV*åþr9D­:Óá–A�ÂóÃUmë |^5T«ÆAÀp=�Ï>xw÷uýš}/Ý“ K>V N\Ò{_]Á8!º"ç§C.µD¾ÕþŸ|ü  @>‡¼¯3ú¿¡!¾±YÏe*}½zòä0¦Öº\kù6¢ä’ðáÏš“ãÝe 5kµ*EQ’¦---ÝC‡a¹Z­%‰5ý‰›¥B¡X,¶¶´ã¢V‘¦H«8R1ÚÔ°±lÀ6©Mjiš“Z6Ö— иGi¥„"‘äpZ¡Ž¢B¡¨#¤�ÐZ[K’JµV©U+•JOo_Ooo_o__o_¥ÜW.÷—ËÆkR¶<íçÝ2Ë1�@¤ã¸X,–ZJ]Ý]]íí]Ý]Cº† íÖRlm)–ŠqA“ &5l@r‹D.Ñ ‚RH É]À(\Šrç6DQ+­Ii�´–­'}Žã‚Ò>c½ŒÛ(`Ñ Š+’£íQiÇ@‚E©1Ö­#"ec#rGE($°Ö¹ ÐHˆ’ñÄú¹“ejÈf z 2ˆ´œ«)\�h­‹Åb©Ôâ¢êo¥¯}"�Ù¯´Òq!Ž¢(Ll¥sTbÞÏJ˜&%пR©Œ5ŠÁ<xß½?ûñ.üÙOž™÷ØÐîNLk‘R­íŠˆÜÙZëXëÈåÈoùo G³X$åpw B¹,[ž7^FŠØŸ¹€’¼Feî!žf%ôA™×’_­Rº;*„…<�`9¿¡¸¿¹Ø_+ÿeدÑmIDûƒ”1Öp%¼©˜y|ñâ®/ÿú+Ûª•J¥RM,€ž~øÑïš{þW/ytÑüÛÎýæŸjûü#Ù²yëÜ+~sÇË=ý+Ÿ¾æw7¾:yúuí4öÀc÷\öë¯_pÏ‹ ¸ðë-Ú娙“UáC¿[éW}õ¦OŽžxÊíË.ùpÞ ïô±#ÇÌþîÿ^óÔËÏ\æwÿ6äðcw+š8e3¼÷Òß>ôÿØûòx[ŽªÜ5TõÞçœ;e @’@H 20&•yHa’I ( àƒ‡†QÔ÷‚ ÓQÔ0 Š ¨ !2<¢@H€„ $poî9gwwÕZïUU]½Ï¹7#`¯ßùÝ»O磌«úÔúÖ·¾uñêê%gÿÕ_²»õínyuüÙxÞYÿ~émïq×%ÐKÿéÅ:ù}vuÇH»ë’oõ®Suÿßùümÿ Ï>ŠvŸõGyôK?z¹nÞurá'þî½gžsÁ…ßýÆ'ßúÜ?úÄMò°;íÙÿ�íÛ´<v7™È%çþp-‚Ÿ:¸ìòó/‰pà÷>võcòù¯|g÷¿ñïü³ó–õ裧�|£»Ýå+¯ÿÌ·ïpøÑ+ÓÛß÷&ÿùçg}å&G{‹k U¦?8ã´OöènŸœýå•¥þ[_úÊç=nÙyÑw.ºôßüÄžù{ÿ°ßžzߥñòý/|â«þxntÛ£òWîÜ¥�tÐcŸ÷˜+_wÊf~Ò}V� =ﳟúÆÅ»fÍAÇu€îÚ¹[ýa‡|Ñ¿¾ï_¿uÑw¿öÅoíšÞû 'n9ý>ï¯?ó­\zñù_ýü¹—_7n~ÂSï{ñóEï9û‚K.ùîw/³P~üÏ÷¼ëœcOzÄÍlJm¼:�õ´çßÿk§þö»oü¸'ÜÁè®sÏúì·¸;¬Üü³¶‘ù�� �IDATöGìÓíÜ9Ãýn~ó-ç~轟½à¢ó¾øå Úý~õ ¿råÛ_ðÒÓÏ>ÿâK/úö—Îþö•W§‰îŽò1g¿ú¯úÐ׿éÅ|ÿòî*ŽÂM/$—}å̳/¸bUwq»CWVwî û<è©Ô¿}Ñï½ï+ßûÞ×Ïxé Þ¾þЧ<äFˆ“åe¸è«_¼p¦�“»=ù ÿãï>ãoðħ¿Àßé±'þÉW=ûM?÷ÂK/ùÞ9ŸýÚ`zï§<ñ¦~É3ÿìßþó¢K.úÎE›Õ­ló¹)çŸö[xêÛÎ ¸ãVGÝøÛg¼ýŸ¾µs}çyý‹÷Ÿ»ßÑG߈ʄºŽ‡C<ïã§ÿÛWλðÂó¾xÆ«_~Úïxü/nÇk6¶°ÍÌÿÂ=ï²vÆkÿôãÿyá.üÞe†>íevèî¯~ìÃgŸ÷ýï|ö/Ÿÿš|âcŽý¯s“v¶ÞIù³R^ãÃ{�{ôSŽÿÁŸ?÷•9÷ûœý·Ï{ɶŸü”ã—ÜNzôM?òâßÿÜ/>âømû>ð‘Çžõ/þÈ!'<ú¨9°c³÷ç5²øÝOÿã§¾ñÝïóO¯üïøÑ}O~Ј“«>ìêÛ¦^¤‘q1»Æ)ï\» 9B”ü BÔI •€bÉ2Ȥñdã7]¼`Î…5«) áÒÒÒ²å^//¯¬¬,¯¬,//O'@´¿q1»Î—Âs>èÆNJ7[ÇâF‚ßcTri‡ ÌW+î¼è¬üȾrÁD†}æ×â£Ý Ã)Ñçi}òÍï7�‘rdOTíL%®[;ås}óÉ‹3Ž7•n–LEVÈ欖ð¡�Ù±7ò~é†ÒªôAÔHÓbj‹–]‚IÌ^ èPz5ÝÈà2Û°”ŒÁ(fÆ ªªoëç¶ïgílËÖ­ûï·_BŒ1æú…vžE$º”J>™4“奥-+[¶mݺcûö;öݶuûÊÊʶ­[÷Ýwß}¶ïXZZFB&GdÚœIÿ?„0›ÍÖ××g­ÑÌC‘¥$ÂB¢¨ÂÄÞgQíTšiÃÓ: }èMëQU×g3"rÎõ}¿{uu×®]V`}}}¶¾Þu]è{19pÑ„ «:£Dλ¦i¶o߾ϾûpÀÛ·ï�€¶mC%ÇÜ4÷ÞJ�¢ˆZV¿ˆX­ÇK/Å4MÒpMÖ™1ŠÄh©fæç(´郎π‰œb#œœcíûB/MšÄ{ç}Ó4 3 ™0zï @°Æé}ÃÎÍ¥äØû°¼÷�À^g1D‰Iæp˜Â¸PAˆ6ANÀŽ»œn HèØA!™ój ÌmÛö}OLëkëÓé’B ¡»Õ‡yËC|ÙÅ[§|çcn»2áÕ];·oÝ P’¼Ã�ÚÚP½vr{¸‚bKÍNÊRöÂßä1úÃPk¦Œ^n`utó›L’Rf—ÒæÒÑg»†µŒJÂZ ø§)KZ�°nŽ˜¼N:åºù}›)Z€oÛn®õÓÜø÷ îôâ‡ÿ~æ9;?õ»·Ýb#KÛúöËøVÏ~çÛvñ+Ž¿õíOz÷¾¿û®×=bO�¸ãáÿçÝÏ?àŒ'Þnÿ÷Òoßûû¹04øø7ÿÝ3ñ-;ú6¿òºõ'þÍÿ}Ú¡W+f0½Ûï¿ûOýÂsîvä]žyæmÿø=¯<n€nöÔ·¾ó ñÍ»å>ûþ ×¯žôŽ·=ãæWçtá›ç|kù–GV2K²ë¼ÏŸyæ—¾7r¬ûçSnsä=žø'Ÿ?ðY§îÃ/ºó2�èêw¿øé3?ÞNݼë䲯¼çU»ç­»å±yýå¿úÖ¾æø=iÀÊ-ý‚›ïü‹÷žr7>õ—?ù›î»ßïwÇÝ|ö®zÏer÷Û=ð‹Þñ²svâÖã^ñà‡Þèü·<î/Ÿ÷ŒÏ\z—ãŸÿÛO�èuð:ý€C–�¶Þëˆ[ÇþЇÞúÀë“‘ïà]gq 9dïn{òs²úÆÇ¾ôÌŠ³AûÜêÎG}ûUw=ìÀCá1oÚýÈwüã«[¨»¾üW¿ý€[°mÇ¡÷+=íuÏ¿—�X¹÷)O?JoqòïÚ�€\rÖŸ=ã—n±ß¶}8ñ~áŸ<óvìîô›¯~Úò»}›ÃŽþ•çžþ­¸õ~üÁ7üÒy¯~Ø1‡tó;>ôyïýæÕÒ §CžüWÿð²£>÷¢ûÝê ƒŽ:ù}pÇcnÖÈ×ßýîoÞý¤‡dý³ÉÕ�oôðg?þ¦tç'>öH�ùî?¿òqw9xÇÖnÊÙw~Õ«w Mï÷‚×<|×ëîË›ßñÑ/ÿè…rã“ÞrÆ©GžõÂÜæfq·OýÈ÷¯uÖÝöùï{ÿ);Îxæ/~àÍnwʧö?öè›ì5舛](|óý/|øíoº}ûMïþò‹üÚ—>x+îû×ðOîxö Ž;âˆ{<çÌ£^uÆu#Øò€gžrħŸýëy±ðmžüÌ»_yù-žúô»N��š;½øýï<aíÍ?örÛ<ë/¾¼�–ïùŠxçIíÛw§Ã:ìN§ž{ä]nµžÊæsS~xîYŸ:ókw0¹×©ïþ£ÛîÙÇÞxÛïô¬OÝêåï~åñKÄºŽ‡ë•ßþØ~óÇÜü°£ïûœíxÖ{þîÙG2\“ù¸°…mntð“^ÿçÜýç¿Ý¡7;âWß¶óöw8l÷6;äÒ9õaw8ü¨_~Å÷}óé§Þ}/R°×»m{詯}Ð%¯9î[·öܳ½õÁ+X¿Æ‡÷�ò”¿|ÿ‹nú¡§{ø­ïÿçÝçMgüá½·��¸Û=áI·]£ãO¼ÿ6Àýtâ½ÂÚžtò­æÞN›¿?¯‰éìïxƽnuø]žòý~ç=o}âA°åzê†M,­ùŽðý��4­’“{œý2óÐTç¹Ð ˆSiê:>ªdSË 7/‚ˆbŒ…K Yî.Á Uõ²¡q),¬¦{/"¶â IjÙX¯’¸Ê“ÉÄ1Ÿ\U£ˆw®Ó…ro nÌ[µBî«ZAn„OöÔ¹õ’tnc½îœÛan{½Æz €˜u½¼¿0l¾4Ðì——½UÊ"yÓû¨vOQUš!Ý`®Po,KyÑt”ikyvs5)«`”›55òä©b b$A"jØ;ç��2À@>GÄ ¦Æ¯!Æ"BV|$"dß¶-3†º¶Ý¿ýV––ðƒä¤�€(15’hꦈ–OÀD„Ž<Šù$@Ì ªÚ‡¤‹!B®Ê—z^ÅJí% !û¨&=¯ÄìÙiN2ܦšÊ ‘>Œç¯èœCMõö$FTðÞ']@ÌÑQµ‡ÂVú‰Ë0vÈÀ*�àœC€õvÖw=S ÁO&ŒØ÷=ˆ²c Ò¶mÓ4“¦ébèºÎ"êA¢¹©&U‘9D´$…zTgœb˜YµpžkõA@Õ *¡Äh()�€CB"ï1w]'1rãEA%2óÄ5ÆJ@@‘B$¦i¬­Qç×¢%Ÿ¢ž°Î¹¾ÌÃû*×… KAÌé7ðž‘BÂ=Í-7”„Ê.Óx(JtìWWW·mÛ±ûÊÝ}”;vìܵk¿ý÷!¥Ý»¯ !6Ωêúúšó ùµ\ÏÔRÉeã}iÑe…ÖG{Ö¯µ²¿ Â<ùGïD^­tBÁ,Ò¯œÛS¿Ùʵ�DKÊÃ(U G/ƒЦ^n˜ÃˆV_ $‹4€ªF‰UÂA6Ã~ëIO{ø#±'äØZþ±}lûöíw»Ç/ía¯…ýí3Ÿþä °ç‡VÉwþô>ÇœöàÿwæïyÍÖ|Wy ®íÜ©ñ’üÏ“~ç‡/úÜiù¯Wœg¿ä÷ýÒóÎýà“÷pí°{çvßy÷oŸðêoüÂîw-òS~Þì†9ž¶°ëh×~`wÿüë‡þþÕùo¾ÿu*6sì§pÊùzïÛþè¯ÿÛ³#ÿò‘Ýü‡/-ïMôAbÕs¾þõûÝï~óß!¾ÿôÓçϳ¨ ‚Å€M Ñ¿SHYs&gòõT6�™ÍKP/!+¡ê’™ž6d7²,ø ÃæðKÉ1)l…cè{pÎY´‚Š˜OeKr�hÛ¶@ªšyê…¤Z\[k¬n²l­®+uqÃõos0A}Ú½\¢ôOñLæÀˆbFà·åvrœªëjµ€Î0ÍÞ?Eº‡Òî˜È-Ÿ7ª{K—•J1*Ÿ“RXAáÃÔÔú†0U¥Çœ‹Q~­º†0—ydU@db�ð€š”ä»ä6;sVStÚ|bæ>t�JY¬>Ƙn3ö!öÌÍòò²g^ŸÍ@aë¶mëkk6xÁ‘+íéCOÌŠC³  Œì·}hšfÚL(RTQÇÞ9 nÒ`bjX½T^¡€ªú~6›õ}/ª ÚuA4‚" Š€H?i–Ô’{Lk” ,c¢¾ëbŒ“霋}¯ªÞûA/^UU ˜ÖÖר(6�A!236ÓÉl6ó¾YšLMÞ_U×ÖÖV¶n¥\€�˜b¤h©ÿneÒ,-M÷€8kÛ¾ïEű³Z‰}ß·]k%*MG�Sˆ@“hœÁ(*IÌUqÎe‡Ø¬·c%DrL "’äTCß«jc¾:¨'²š®Ì%BVLAú¾cç&ˆL$ª  „ „(sòT<U­ £ã¦i‚Іhx2+‚Fe¦4Ø‘�‚À­ýP 3fÊL�2éVƒ`T5„Þ±[_ŸM§S�`ÇHصëûíØÒ­­®îºr¿ý÷_[]xj»°uËJè;DŒ`Å:Ç9C€¶ƒÒãßhzç*3•Ÿ_r†É§`bÊKj¨ÞœZD tmÑ ¯˜¢¥$ØÆ„G¤?  ¨¨¨ I@7Ѝª& ‚•9Aù/ë  G4€ÆþÕeh#ëkìÉöô_ØÂj“óN÷9ñí·¼¦q¡«<P¾÷×'Ýþ”O4G=èwÿæ-'ýwT¥ëÏ~×û.:îÅ9`O×_íýîñ²s¶ÿ ¿ÿî?Z` [ØÂöÓjˆ9JT¶�"¢ƒg@ C(ÕSP%TË·µ—H!¯jàwW¢Øv°jbÞ»¥î Ù¡¶Î&B+Se‰]oE ”œ³Ó…ûµõLW°4r"Ùºeb$á‚9 Fí¦Qä•ß8ÌU�Æ>( «õâr®CKàtÝ[ÞŸ?}ÎøÅ•@UAC°e1 dE@ëÀ±ºœ‹�šû6­„‘@UÌiÌç,-" êж«HßfÄȆP]r h€x%' " ˜Ä,EÔ{W2[욇Hž$©â+8$Gì›ÍÚô\DÛÀÄ!;žµëÖ Ì¤„QcŒ–Ì.]ױ禙Ä1WJ¡wŽ o×»®Û¶m›køG?ú‘c¶ÆÆ|и©SÕ‚ F6ß&Æ{‘ÐÆY7#«OGÀÀˆÚ8o… í …›xßu½m1¹Gq¾ñH0uOƒeX4Ĩ1ôì¼=“¾oÛ¶ï»à]Ó4¾ë: ýÄ»@ØÍÖœóÞq×ų¨Y­•¡]ÇÆ“#‘cà;‰àž­v�ØKlC›jÎçÙl&*¦eض­Ÿ4ìÝúgKû,#!:Æ1Fv<4ž9寨€ Ñ!ÐÄ{ïÛ¶%$&4ª�8Br®Ù!1ƒB”h7¯9ŸÜ¦¦á;1FÓD@ïØ€è§ªÈÈJª2i|ãQ*PB€Æùå¥eÃ/’&`ˆE}ã½÷€€ Ϻ®kCˆÁü^A風‚ky¦¨�@ŽÔÎ ˜’Yœª¢êtÒ} „¨ªVÄ!„ ‚̉$ÆÐ‹ª:fP16MÓ¶-6ÞKìU¨:öF¿j–—v¯¯)Âzßz*ª  fë¦Ð÷!Ä,Ç N‘>×4™ ZëùÕ9`" DË"”6sE¬,E®�’ —¨^õƒˆ½™kìO!jJúÓúµ‰@ÆX#DƒqLsA™Ø2Ž"(( jzÿX ejÑ›EDQAM¸ªFí’ìZd.!ÀßÞ/la Û»Ñ-Ÿ÷é]ÏûIH‡<ãÃ?~Ƶm×õaþØ×|㊽íàŽyÉçw¿ä¿ª9 [ØÂ~­yÀ[~pÑw#nøF‡=ç“W>ç'vvL‘,@Q[q" ¦ÀiöZó†R@"Ia04×2 r!šš¾ÊѪœh")³×*šòœs†›8BÁ{oá5�žLšâÚÚZYø–EªÄ(*Þ/€BZ¡ö}Ž£¥Ý¢ª 9g.YÝ µß^;Á5m@Ç´äM˜Û¾÷PSÍ*ØÓ`+æª7ª½05l©ŽÖ¹ó”W: ’Ç•\ ;…­ò!FýÙ³Õ�Jqé‡f0JŒ±^i—Ëÿuƒ*f=øÌq@} „äœ QÂúºsÞä ÁòU%N¦!»�mø1"õ}oÅì!#3³jÁ(¢�ÌÌÎÙ8Nd‰ŠŒa,•ë"ƒåÌ#� Ä�ýl½æ``V¸sΛ轤P0:ÇD �ä¬îñˆêsªwŒKýÔ2ó@ÙQÛ¶-€s�»®ë�b U…Q2æŽsN½ %óPœ#öSPõÞ§DD"�¢"*âÝ$„ !@Œ±k[çýd: µWè»""Nš†Õ¾kk€ÉÄAsèœk¼7ö»d9Gl% ¢„h" D¤„V©‚ÃJ²„™b†§X……() ©™LŒ“ŸÞ<Ì sh»®ÏÉ&Ó ;gã­ a:6“ ö}/]×vè¿>›Ebj¸1ÒŠe DHŽ<�0SŒ¢˜Æ¹æ^¶‘™TSéÈLÒQ€¨š¢Š QÃÌÄÎEU¹B¤ÝBß÷–Ld#§¸æ0ÇBÊmhŸC 9Ÿˆ o ‘ÒKfÌ$'+!¦œ {k s Ð…ÒIÒIë½jÃúU6BÍ3Μ¾²��0Úž”X]` S¢BŠ’zÓPÑ$ÊŠÃ+¨P @!=Ù2TˆE@�aóœè=á¿ [ØÂ¶°…-la?K¦¹ŒtÀÊkN�pÃû¶ê²Œƒ$ 6¸…Š©¨aR°Îñ¥áX–mæY¥ïcÔ‚ü•ûªªJIÃò:UÔêxï23=²B"ÒŒÏ1öœ°ªICDlãH~¬´°tGUË„€C®Éû{3;Û›SìвûÆ6lDpLjØØø|éMÚS›ˆ˜\™Iʼn(;¶ôéù|„½´¿º^!7˜�998EJ…9O@µÜƒ–œg�HC�Hò%u‚�%+±ºªCßÇàý ‚yff§ ¢»‘™#b×õmÛµí,û”w~ ! ¢Ú(L}ûLcµ†”[n T5m˜Ù<M8Izx†y͹a!JÛ÷Ž+Içœg‡JŸÑP*‰TL/Ï9?™xÕ©i’ÃýèG�°mÛ¶¦ñ}D„ˆb¢Ò÷¡ïû¾ï%ô ¼о‘ :q^Ðüj bòÌEC)fiºD1Fãb´³�D‰}±i¼CˆÑ2€lÈz"Ö¥¢š=Ý×6r~ŠÛÃ\ЛªÈðbIÔ÷¢ñ` Á$ˆ”h2ÖŸ1FSŸ4h’'ÌDÈ„� M¼* ;&žÚUÚ®W½—‚ULj±i <‚@™x4Äs^�0{gqq$TM쟤ÞÚ8‡ˆ}ß+€w÷QÄ`LuS={.�E4«±`æÔ[w˜ÿ_pJ"¯ÄÙ-ÏÍ%s‘ [ˆV ˆ8Cõ *ÿH·kA7y/Ím¹ª0ÕÄÍU$Ó�°wK/X¾N†!²n¥­Y€ ÷Ù_»2æ 6SµL€�PÙ¤µ [ØÂ¶°…-la?WV|ÉÃKÿ¹ÚAC >(pyì*9ˆ,Æ–[¢Û<_Tsá.,!h�ˆ¢Ì.„��Î9U]][ !X¤Ôv.Úlœ²[íв-òCˆxô‰ÖŽyÅYzà*\âq_Õû]”(ªù„°‡˜Z:¬Þw8sÊþ:¼ ¬ W¨Eǹ›­l¸Á\šá U¡JÐ;û�Wc}<4/ßG5,ç û'Ã0×HKtÌ …"¡ˆÈ€Èlòc"¢jžœs"À²”1A�Y™Ù{DŒ1&YÀäà"XÛ$躮ÏiÉ9‚ ¬XÎÕQ+·“;’”fiyÞ^pż!‰‚Ѝg¢TëMJ†¶qÑپ„—09æt<“C&ÇŽˆ½[[Û=›Í1„à#‚ù—Ì�hšBìC¯É±¤¨CTÕ9V…!ÅQÌm"UDÇH€£¥ß‡˜ITEÄ9n|ˆªÒ4FD2Ñ :MK†¿¡Déb[%¹XÂ…1)|Óx“–T"lS‘N¢A�›áˆ}×:ïM¹ ïº(ѱ›L&C¤=A«éÐÇn2i˜t}/mK”Ê'†¾"ˆ`ßö}ßöo|aó "yç43ðMÕ�Hˆ• ŒrRèeÂ2!syXî !»Œ‘)@A^(WŽ0üÍePŒ ±µ¡hs'©!Œ‡¤=ݤw©E_¦”-íC(–dûXuN4X!:…<% é#kÙLs/Ò´g„³´®ê˜Î¡Õ]ÕïhȤ†á½˜;Ѳá€SæÓˆ×‘O›• Êß&$DÍÒ ºQÕr,la [ØÂ¶°Ÿ++¢qXÝ) "‰Z–iåÚ**¨hTµäORTŒ�„¢¢1/I‘PRòBæ-Œd Tsª�DPÊqËäØƒªö}‚¥ps¡¨m�ªBdݶ9¥Ø‚�µZ÷f¥ñl›¹×8Ú^“ .°EÀæâå{•B§qÔÛG$ ˜[+—•/�ä“l(P¹áºéÛ\`")ÿÛFµèw9‰}‹¨o´¹Ë•SíÉC©ÇSù€!…’îRSTÊhîX’oŒ\RˆФÚ ˆ!Áš•4Æ ¡ï»¾ë;!æÉdÒÇ�–‡€`•ÙEÕ(-Î9ÃiˆÈÆ’÷>eYW`VêϪG‡c ($‡ ,U!ÃQ³½‚qš`ÄQ·Îñ  C(ÆÀÔ�ž¹£QD‘©qÎ2¦Ó !Bß÷vù”Ö4lò6ábóØ5ëƒX5ú(�¨Æh7(QXbhÛŽ™½wŽ™€fëëåA#¢d 0{›&þo½ÈY\ x°Îœ� Hlû�TÅD7ÌÛ *Ö‘V5Ð(Ø9ïˆ:ç%Fß4Þ¹®ïíU5„hJ~Žz"t¤ LHªV p6›!y�}ŒFþ_^Y)Ã5ÏÄ\ÁD5ΩÒgyöiœ§I!‹V`ÐHŽUµ—ˆˆÈÈ]ß³½÷˜$ ¡"@Œ¦·‰S�DÂyñ?‰‘ˆˆ™cªjØEö†‰¼÷P½%D„² FU`®ß!YÿR,�ÍÌÏýRGãj¹Ò¨R5ÚQC±c²Aš5E5 ÷«Qm@T£˜2 Ù[…p €¡ .äB 6Ç£*e”ˆB.e¢©G¶°…-la [ØÂ6oI {£»«ÉÛ<YÄ7]ƒ²¸1Y©d¨PŠœAŠdç€WÒ>LfŸc”Éd¢ª«««]×yï‰Ð”ÀLý+Å—ˆ$Æ>DcÓ«Šñ´mqè{Ÿ‚É�€¥›h½­BkõÒ7øò¶óˆP0ç6oºåêX}Ôæ—°U2$ÞlYËŽ¤7inúŸ‰£Ds°MÌTÊTɸUX JÊǦ--­ÌWÈ.¢ˆý¢™ 1$†lhRòa‡�ç¾Íü”BýE Me@EÄ4-³f<D‰±È^ ô}gƒ$„h.hÓ€2—ªé”‹,¢JŒ!y‚�� g³¢1z(ű‘J¯�sº8VRR�ç=lÈcH¸XE„éU¦~�Š@Dè‘sbŒ„„¤$ "Ò…U»®‹12ÃúúÌûÌBlDL<Gˆ`fv i "6ÎÑt:1Šeù‹”»Þ½ºÖ4sKËËi†fë³¥¥¤ ¢©©e Q¬©¤Øx®pYt4™ÉvÚ¢ *±¥—˜i%Øð5ÄAM(2FïœHœÍ¡áÞðÄõõõ奥Ét×Ö×û¾'¦™8çfëë««-Z*RèûNÅ;_rþëćÐ÷É'½7ÇÛª9@.ìj ŠÌõ;¥<çcPA!&öæCG)|ÈÞ#j !• )$@•¥É$fÙP5‹ŠßU1�œs!†"`(‚ ³ù錠 1©÷ cÞêK`ɲm5D«@¾2”±Ùü½Qs6ýzîÛj¥ Î2ºˆ¤ À™QD†0½*G(¨ª�’½‚TDÔy7¾ýöü>\ØÂ¶°…-la û4¬4§Æ+«,a¸‰ËŠ� Îy«a^äíM"“Ê� /åDQ,xƒ‰•ŸWuQƒ] Ť,SŠuZ”k˜N§Î1•Ú‡©ÁÜeJFHÑ`ç}ßu} Qªi+hjP{Úƒ#7^Îk@ !WŽ_ÍüßÓªçþ¯Z±°HT…ì9WÉ{V)Ñ´éBV‡ë¢¦r5`£J�� �IDAT•æÁ#`a[ ¡Ö-ù)ìñrÆ+¦g‰ÉöYA3¡£j›fžH†§1VCͤ sKD€H‘�È È1¥Ò†ÃM|DTvä'`b¾¢²&JTècÀÐzïØsìfuf8s¡ "VPKL€âÿ˜??¢€(@Ž9[A¾L)øÛ¾iªCÒ|"à^|@”àˆ C1y´* ùH‰¢¨J j)ð@ÈtåêªùäÄ\ƒè›Ô»Þæ¹ùÌ @Ì.Ë[:&$R�QYZš4Í$„ÞØ¡ïb´è=)aŒ%¢ {ï#�¦ñQ+mâ‰U‡^µ63sö‡­àª¦Úˆ–£ˆ2ˆª%Hd� 1t³#£šÀòò²s.„žÙY“ä‚™m×õ}ïœsLjÕ+D$� $ï½slJ~Ú÷AeT´­®•¤ƒ<MÀêhˆªHŒ‚^zö¦2 $"S’õ—,$hJ6¥ˆÙpÒc"‰ˆˆbRœÍ-±c%;mBg@Eb¡Æ”š0ß‚ke&PùàeU²]P°ü-T¯©¶+¿±ÊÌÇ4BÓû!a J’¹I"J$’~¥úJöÂ/ê;¥É$]È-…­â©ÕÍ]` [ØÂ¶°…-ìçÐÊRpP �c�d—“°ä%³ÅüC ù¢ñ‡]3iLkPE‡u4¡jfÛW>°Eð,ÑW³ d‘6ïýlÖö¡gf‹Œ‰N& Åh¥ÍÀ8´d‘À E%Æ��]DÔ‡BoŽU Ž»`ºªú¥˜ŠT_&›ãAŒ©›¯,1ÿ3w²Ê}¶Ô“O•9ƈV¨/EüŠÕeÉëF!`f n4ÉÚOÙÙÞ¤ý#aاpÈË÷ßìÈW±á É·ÆÒr;œ™9¨$¹ ñ3¶³%uçàjR7”Ø4.ÆØõ½mgfÍÏ Ñ(Ǫ*}èsMã{äbqxŽIŽDB&§ŒM¬Qngî~ë@èœ(¦úöÜu=3¡HÑðgç «À'&ÍÅ("ÓÉ$J !åÔ”v1ŠŠƒ*„b"ZZššKŒ&Dƒï�|`«Ešäô ›fÒ‡Þ±sn&±æyßìܵS¢¬¬@ˆ‰·nÝ2N/¿ârïýt:õÞ‹ˆ)b"“ ô}gí´áa,€¹$—Ò?™Á¶C¡ïCÑ&¸õŒX<›˜%[ET»/{ÿLšIÛµ«««ìœc×4uþW\îœ_Y^n&È…T,Ûb%saÚv6›µÎ9çÜR3‰1˜$"J”4̈T$ê�®33GÕº’E™`Ž]R|ˆ‚–��Îû®ëUÄt"c€èœC09O±Äܶm´J°€Á‚!´'²·€Z‡Ø;ˆÄdP�¤Z†¥ó£ÁZÞ6é«ô¤2r`8V†ŠŸùM9 `“)/ oÞü×’õ5”hK\k3�¬,$Pª6šQP%Ë¡@�½”ÔÒ6T-×�ìOX !ÝuÉVX` [ØÂ¶°…-ìçÏ03Ž!…R¢½‚º.ôÎ9ÍÑf�0ê¾IÑÅö *1ö1 €¤@—( ±�1=µ† fm—SÊ®# BÛuªBˆ¶öE@ÇŽ;çú>´];i&ˆ8kgÞù••-kk-!"q^“ª†>ôÌ*BÄì¸4Ûê/ªÎ»»ˆ˜Ôk$Qr ñú_fN¤èl½â9Ï¿,…±f.TîwíFÎíV·Ó4ùçœÛbtt_a€´Ò‰˜[—О¢‚ÆÉÑ[prDHÅ‘°ÆÎßBÝ€œ1¾/¨ªzçËÎuóDÕ“(QTQ“ðDˆ„Ù!*èÙ%ÿ*‡Äíªjéb�Þäû¾G�ÔÔ!š%3 ™ª»ò¸-„üÂvQS§Ëød®Drþ=gç$…74JDѨ’B¡Vp ï™_¨ ˆˆÈÈLmh€=[;EE@5‚rc öAQÉ›w„ÑtGmÀ[û‰ F!Çä˜%9W�0ëZUÕ>†BÐ "f"ž)h{][eæH×#1/¯¬ÌÚ™ÌÖ—‰ˆÉÙëA‰,D¨jŠ eâ—aOÅÿ4?³—¨9´›´ËŒ0çMEbÑ8†Š€×ÖÖR·G :è˜0³ªì^Ý «»É<oæÙ MgÁžøtºd)*KKÓ‰kˆ–)ÆØÅ^’î u] �1ç>$uL‰{r»õ™sÌÄäXb��£3ô}`"d6ŸÞ²6Ú6˜šcªØŠÀÄ"Bï­à&b*ìJh”J%ý¤ôgB…’Ÿo‚ @åiÎÍD�Ó§ÔŠ˜SϾ,/ÌECÊÑJ#¶;FÓtî„ÕP(#D#3[yLkIú[eª¢I1À:#•&4‘ÂLH ¶d÷ÅÌ"cdb5<ÆØAHì0Æh%°Aðå*í3Ÿþä5Úa×—Ý0{þ†Ùª…Ýðm1rö3i?]û§«µ?i"‡Y”Ê6Z0Æ<üœ8P, Bˆ�±€L‰Õ¤õŸ-%5ÅiÁ2É±ÐæØοVt‹ùÆ(}èú«ª³ÙŒ÷“¦!&1ì��låmh;ƒsn2õ*‚Hª2Î8"fuö^V‡Ýér•¹èhêÙ´Z5—¹°g~.@"(EÌÇ\Î$ë³¥í48øõ‚¾^‘ç}Šÿï¹{´ «6uö|ƒóÀAv ­‹$kãaLãûcέ°ä«³PèÊñ"$ 3&o$‰žeßÉR01h‰5 "*‚¨PVË/ýSÜ¡¢¶O«9@ÓmÁÈ•‚,©u~Heœ¹å0?ÆDÇ9Òµ“•cVžæÒ4·s9Oi[�@€€�–"„„”xÞÄe¢Z ?%«tЇÐÛ7æ‚vmç½kg­Á=D„€mè¬%Î9ËBDçý�·Q*Haÿ¹ÊX ò(ÑÔ 0#>iÜVl*Q¢Í8ï|}×"šS6†ù£Ä(&ôˆ–‘‘! »µYÛÆ.[ØžÐ9gÏqiyÕÀ#‘£±9º¾×„YE"n,/MË„T«Úâ,@”Ê“wèŠÜãð5õ "dÉR@sÅMÁ^³9ì™)€Ö ,0ðf2+õ¨ÈCòNý~¨^é£QTŸÌ~òû¤žÞZMr¬–_`³÷„ýAÑ!¿ I4TrŽU¾ý4»Ó)Ê•mÌX, pÀL%Uð©¨@W2¸Û=~é*÷YØõnŸùô'o€=ÃlÕÂnø¶9 û™´Ÿ®ýÓÕÚ½Û¿|äC×ý${q�Ÿ‰ãœ£5©j·Iàe¿ŽDú¼ÌÍ„O3Ñåʲvhð¹uXZø1±)ó³çétbÁêÚÚÖ-[‰ìšLBdv]ß(1BˆÁ¾êm¯’}<5l£ž&Ÿ’~UnGuËöUÝ e1 ø6BµŒ®qó…Ç}»Ñܹ(Áºz‡ìW$w±lŸg2d3_KDá¢C“ªó#bLÅ̇z܉ܱ*ÉÝ€R¡ ÇÈkoDs tñ+ÖBRRH2fH–ÄΜià:ø$’°‚ì!¥¤æKÔU0j‡?·��bÂÈH%Eþ0çN¥˜Ã2Av³õÃÊ튮zôõ¸¢rëå!˜[X=í„€è/iDb:–`lye@`RQE`@e#x#Fp%[$é•¢å3bÐhý,#b-#U‰…Cžï.eÔ§7…ªk¼f)GKy€(–`]”Ÿ¤’‚*¶mWîȨö_×¶ÌL9Jo�Aã|;JeG˜ ˆ„³ÇU™û®gO"1@´œDLyO– QäXÑ1L'Kª�•lAé1bÊ£ˆ%vmÛö}€˜H4ª1BqSv’ÌͰžr åõ-¥z_ž~š`ÐêÀa&é°ó€ %!•³…Œ³cÕ‚pøJcÄ Æ^ý@Ú¯Çíœ!n^ö¥œÓ@Á‚…1 ªýÓª†ŽØô¢ª …A$*…$% EvOï®…-la [ØÂ¶°ŸO3>¸€‚©×¾hò¥ó–’-�eá ‰¿Z[ãÄÝt¬×aѵ%8�X„Ð¥e:Ád2Y_[ÏÛQLEúÐ"{ÏÞ×g³är�Xó’ºUM—Ð=»µõ>ö_>çü×å~`ãBSG‚Þ#Ä@7‹6o¶±,ÄG âüÅÜjlsXqÐè"$%Ü “`|hhv*k?òÆnju‹{€ TT‘“£ˆªJ€Lú/8d^€(fp ^ÁcеB± ¹œ¢*H‰…Š*&j@òTS¼:yÕ#g¬ÂDŠÇ•ÃŒ€ÅE/wŒ8¢x cA¬²Ý\Gf«º/@,ÎØ°½œ“¨æ@éŠ$ÜXÅSqƒªå0òwJ€{Q´ü%$È÷¢ ` ,€ä‘˜ ‘˜AÕõ-i¤àeæZc®†¨:D,ù+Kf-¤Ë¥GT•¦ilôÚ¼OBŒìm»ˆEós4œ9E€Œ9j=HŒš«WØã¶>!Ô(ö›HtÎ{ïgíÌØˆˆ!`*Š!"2™.Ù%UÕ4/ £`f!»£`‡ö䘙,îB? ]#Död,ÊT?óëH ª`‚„¥Ê¯eå×[D­wq”#SN2�}‰W…d;gî ªB’H㟘¥OçÇ9b`ÍeÈp|e#Ìb<Vkɰ˜ê‰dÏ2<ó¼Í,L4âù¼*� ¤bü‚„ÔŒ¡…-la [ØÂ¶°ŸO+«Gذ”rTE«æF•¤a #ñ-¼úaÙja´8$ó9ãè”ÕšS×v1FçXEÖÖVÍ9™N'""1Ò”QElÁ×wo¢Ä•uÎIŒ]×7MCDhšðU„ß"ƪ­{pk){‰0¬H�xƒ«mû¤¨~v1³‚ëó_Û¥èà-›6[|€B~”Zæ1ÍáYñPkT+âÃàŽîvÁMŘ˶Õ^e¢ŽŠ€ (Šé˜‡Vn¤ ED$6v‚œK˜‚•R/•¨ ä:t•'V7 ~âs÷[|ÈÊ#ífÿF¬ržË®XäûÓx)D€Q÷äxÌáWMtiãÅD’:|œ>ÓàÆ›Àò;™#„ËãÄÍiDUÐ B ªÁ;' Ñp(RBQ¢ÍÊ—ÓÒ#4a¹Z5š”cŒ}èU¢Ѥ™X3˜Ù;ï¼÷ÌŒ˜8 ^2îp(º‰!Dï9A Q¢%Ø{Æ9‚$H€¤J�lô "“S±áÉÌ‘˜ìí§¢@5Æ5(›Œ‹$¦F=õ /){>ÌÌ�ä©Ì‘Ò$‰µÉ„Àûä—Ï?Êcª<v2óöËneŽäYPÚÔ�Ké “ê)OD†™ŒF{æü›¿­ZeY‰ {îÅÊ—i~åK#ÚŸ£\P3QTÊyè‰GPi(¸C¹eLÕ$‡WY­ç £ª [ØÂ¶°…-la?¿¦:r1�"6Þ÷™»ÁŸÔä͈Em¨øKPû€pQI«·U-¹¬É$•ôF´ýID»¾€édˆ¡]×͸ï‹`aZG÷]ODMãɹ%ô½¼†Üô1'`£›QuKö눥Z2—çzeÎU.¿–£öè6°ï=» óg3O’˜ŠÏY–È"Â)?ϰ¨\C^í|Þ PmÜüóø6‘§#ñWФԸî„bÞ7�€©|Òw! = ˆ€\I šžlèÓwHLk’Gjß)9ºX5Û¼Ž¤Üž[nž0Rˆ*|„p\Ï=SÍ9#µg[?²z#ŒÆœ DD1ŽèîUï남*)R�FOHÁR��Õ¼(ä} � ˜")UËð`BB !€¬¾a‹ ëÜŒ3âC6�Ш@ÎQz4I? !-?F%"&vÎyï,€OD}ß[?دæiÇɯ!j¥·‚9Ù“E/"¡�HÄ@£þDDB\_[·íÎqÁ5D”Y)?KeY_Ÿ‘#Ìb‹àÙÐÄ,.PFDŠˆ&É„á"Z£õHH³¸îiÐBÅO:2ÙMÉy ”Ù%–„¬’H…„E%¤t‚y5Œ5Í”í%ޝ:H•÷ôz¨.kÓV²ÆDžÖ]šÆfž<CAÄÌ(Øü]ÊL÷�Ç/Ò½ŠÔ,la [ØÂ¶°…ý<¥Zo•¥X 8�ðÞ›D—}U¼,�P)!0G,eØù(±RÓÒ‰0rÉV@´5šqíÞh"÷ÓSK_§×ØËÆ!©‚Šhˆ=;'¢j¿*æ51sHú #Ú³féÁâ§ÕÞÚ¦kÄZJÐä 7µÞm ‡ˆ%ßuOnsÝ·¥5Œ2‡8l\`oÔŽå8N®9´¸'u ÚÃ:9³$J®�€d‰‡¹nÜËj»®P?ý"é+a "J #yxçZcr=‚ÁÙÎÇr-0‚7f�˜�RUU!‡~†ÄŽÑñÉrêýZ`'„"i9øóÅ[«žØœøâèiš““ŸËðU Ô9ðÖE<"ÒÏyés¾Yz¾ �ÈHˆÉ3·ú *¨rÉ`ðƌҟõíT™Í'@$ òì«K[W€ÍÊìX†*ô!`ˆ%ãAU@ÔÈõ‚")a^UÕO§Vͱýz‡ksc"ò쬸� cF&òìl´1ÎR6Šˆ„&¬®®…š¦ Q Õ Ìùë`•£ &Ö�ÂZ×"æ× �z+á­´$‚¨(BP%QÓѰ1–4"ª—@‰z—’"Àè.c&ç, 8ç|)9Mêƒ; >Kƒª¼086ú’Vˆa:yûH~Õ$f‡Ñ<}ƨ–&à ´á¸9Ë’œ˜æW:õÜ;M¤ü‚–Ü¡ZJ“äQWÍÄü;dô"%, O!í¶€¶°…-la [ØÂ` ™-Ûì4Õ芚tã' rb9h"?KÅÙǼD ªÎ5gJ”Ñj‰iõÒëâ[vpzí·´y_¶&öÞçµ4xïsmÛ2£H4ÿ�,¨[\ŽáÄcÊÃF°×^ù~å´suàk'³>vãÆ«iÅ¥¯o,’{ãð²æ¤¹‡,ýŒâ~›Þï^¶Ï{FÓÝ€ì¥Oq Ç”»³õ€’Z“c¬Î‘Å)ÍwµŒeƒ ’gEÄ„†2$á}ë.€BïÏ´|BDP•‰i‚RçTO­Hý)æFFQ¾Ù•••Äuq`Œo*<a'©ýÿÒŸFü®û§þ¼ƒ#ʉ溑âQú l¤er Z#Í¡5™È% AS=>DD£F„ ²€JŒ JŒHe$Y\_QI´—’*"ÓéÔÿu¾I1AHì}"ç Æ8”‘¨Œ´ëzf’Èy4!;ç cƒÙXÊU¼÷6N<5àfª8Y^Zš.÷}èC�ž9‘‰Ø»®ëº¶íc0íÁ”¸"1#)d×Ùwz€ð²>§ó$¹h­™2z¬–4oCÑŽÍèL¦?Œô_53çíN˜ÄùÐØTª©îF}Hv›mJ  ¤uW¾âèµV^˜šK~âP¿fTFÑ`°èýfãY3¾6g¢b"˜]’3,œý}I¢ éêZ52²É{2kèˆ %½–¢ºQûsa [ØÂ¶°…-ìçÊDG+Ãá+‡ˆ1g aR+ë)E‰¶tsª´R‘´Î̼Ü‚óžTÔˆñV¤U¥, m¦A'ÓIß÷ÆB'¢‚ˆ¹ í;lUUc@&ˤ-áՃϷ3ÄÏ!¯_뎨Ýìòkm1F+Ü‹º˜$~—˜ƒxå[Ì®ðüž��£å{qÅk_ºöÏ!«ñÏ;€É3ÚÈÄÙ™TÄTœ²ÊsŸˆY2¸€^Q´BŒ� B¤4’pˆ[Ó˜ ªëÅ#*ºtv~«Ha.)ÒŽAS¢L¤ø'7®œ‡mtJ QQbðÞ;ïB®í�‘ˆû>4'æB×§kÙ¡¢Jš¢¦*JDαF±˜&aÊw @i»Þ{§ªQÔ{Ìæôµm[°‹Ÿ1:`“#¤*}1Ƥzh}V!qÃËÝËÜÙèÿ«¢$‰†p=ŒÜ¯4öŒmŽ�´MO.ù}1;#Ñ¢FsÏl¨3‘¦ÌO�QD,Y�ç¬åÅ#DÝ”¦ªÃH0ÝzÇÜ÷£J…}£Ù£7Fhd@UDçT¤×@HŽ2€Hœ6µ(±µQ@º 1›ÝÎ9{ÄÌì¼¥6DºncdfQUÇÄ8M]Š,v”ŸN—'ÞÀ ^¢†¨ª‘]ÑÖÓŒ‰j%–Qüêd"  …È!1„諪éXò(¥ H’«Ê™ƒœ+R±ª†`37½Uˆ!;ÆcÎQy.\¦u~qdÞWGöH Ô‹ù¹Ø-×÷XF`4ÅJӬ쉙Âp½œ¡&™!ŒÔàã@yȭì;s¢‚µÄt4ìY'\/Š}0ðQ‰�Óh´'¥D$ÑÆÏü$[ØÂ¶°…-la ûù1EB5×#×wQBrmׂ)š«æüñŠï 9g›•´(¹›#g W[¹ÅÒ²¯âO&çÜl}ÝÖ…¡ïÍ¥tÞ/--íÞ½Ë9g$‚®ëD„™›¦ !0³¡)d”W–×ÈêW :Õáêÿ7î3çÏ_MÛ|pMϳÃJI¡B"Ê·å«áß±{3jŒfÈ#5˜æïbï-ú ø'…<×<ÍŒD,údPI¬•}˜�¢„¥å%UíÚ`B˜HcÒ>-ÈY4ö”L/“#*§.5 ªˆ@tLˆÎzE$„Aomu™Íë+À›È ¹`çHîÉr9¬¾­mîÀQíW¥~TÛª}¢%`iUÆn¤\½~XÃu5yc6ù˜m‡ê*ˆºÉP,ù©ÕýbfåÌ݈V¸^j2�(DL 'F©W2yÈ”"¡%åwPÆÅDU›Æ‹ÓT„Øbƒ„"Ä®ïIAD»Yª„Îf¶ 1‰¡�":ç<zš ��1d¶ET‰1JˆªÚöŠh”(Qò,#¢>„¹1VfD½ìù " )j©£¡™c4òTýøìÆÊ“Mg†47T°”D%µò#êÊx,Œ ‹Ë\嬇ñ+"Ÿ1ãdÕ£Où,Xq”ò…Å2AÈ„.!«9ˆ“‡Vâ ˜\Î RM9- 6ÁφÅ]ç]¶kûÞuõÊó¿+7½õöéþþãôÿý‘•Gx³Ë.ßïØ£o<Z°„+p¹ÞäÆÛ6m™øØßzàÍ®ñ"ç'da÷Å?Œ7ºÉöØX=ûú…Ã~ýi÷Üÿ†‚­õ;/úßô€-?±öÈ® ¾ô­xä/ÜâÚ^BÛ+.¾ré¦û/]¿íZØÂ~ì>;ÚKÏùâeû{ôé:¿~²¶7ÂøÕ7Ü4’ �¦)h!1AÊ ä‘"J ömZ§š”9¢T+`fÇ&2ÆŽÙñ8bˆÌì›Æ² bíl¶}ûö¥¥%»–sn2™âÚêZè!2QãýÒÒÒti‰‰R1í«øÁú'kÅ#¨Im!!™à\ùýŠ”A}®ÎVÎ9\Åru«_qÏ?ßËvsî6Ü%l¶¥Ú<ú%I q}ª$¼hÕäÇ+jgågnô˜B„‰P–g]çç‹))*qP*BæK[ÛTAÀjšÊŠh4”ŠsQ:+Æf’k�Î9PèÚÐ÷aÒL—–V˜=�6Í”ÈÛmzâ†a¦Ìh®?c"¹€*h)ÛlÜ”ÂxœƒÕ£c(‘äñóFû`7˜•óµ†ó§½Ù³ýԇذœ“õIˆì_*Þ˜åaýƒ˜=8¨·£ü[ÿØeÏM~ŠãWU[ z×é!L3ýÞ!1 šê:êänÏÄ™Äaãoy&ÙK&f‡L`e #F%pÎO§ÓfÒQ».ƶŸµýl½][]ß½º¶¶{uu}¶¶>[[ŸÍfmÛÎìgÖÎf]ÛvmÛ…Þ’!˜Ù5ÞOšmÛ¶mß±cëŽíÛ÷Ù±cÇöíÛ·ïØgŸmÛ·oß¾mëÖ­ËËËËË˓Ʉˆ5 ¢=@� ²Q­lyFö*-ÏÓ¦Û01O·¼±ý™æ»�(Ú”54Þäfj eƒzÁjãÏõ¿å«‚P LI@6ŠêáÃ…0£J êîFàBuucr± REû±‘«Š¢ {MÉZØO‡…KÏxöû?ðe€Ýý×—Ÿú—ÿw?í™oxþk¿¸~þ?ô¯<«€ðÅWÜå&¿øš¯EgÿÁÝŽ|Ö‡Ööv`¿ÏµÅBÛ†kÛèM-~íÕ÷:âéØ5ôäÿéŸûŽoºí×Öaîgíõ;Ùú?÷öǼðÝõzÒ‘éïýãžþwß½ÖínOÒaÇýÑ—ûë³Q [ØÏ†ÝÀgGÿ…פ׸u~ìÅôò÷>þf‡>ùï|íÿfé×KKŠã¿IØÒ{gq|aTþ­^{Vùy9ë¼›L'ÓéÄ9vÞ7“f2iœs*Úu];k‹ËÍDÓétiiÉ9/ª}×wm×u] qÒ4[¶lYÙ²eiyÉ7¾™Lœ÷Äl-¤\´loÝ5¤¤=kod®èWmW»F»]åIæ 6 J;çzþ*›‘ ê‘•xàœ|ú\Ÿ¥ �� �IDAT?˜ ¡¥>Baƒï™õ°—H\í* bRÚ+ç„‘_Ù‘0)ݵuKRÍ#‰ ÌŒUÅG»cUD I3ñÏÕ1VªæÅÉIÊoæÞXö¸cç7,¬ë;ËyáЂqdŠÿÙ­‚ bƒdZÕ›öÿÜQe¢ÕŸk×zÎÒ�È=†9Œ‰óªBëV¶t³¹07Ì aæZ‚¡ì)ÙʃÎTÀtw›½F Úcnð�$Î9H«šjA0…Žä4:CdÈÒú‰;ÇŽl\©jˆ¡Ü‹¨8vìEDúûÐÏú®ë»YèÚ®]m××Ö×V×Vw­îÞµºûG?þñ;wîÚµkו»××Ö×g³¾ëÚ®›­¯¯Ïf*¢ˆì:g·<]Zš.M—–—–——lÌØèjš¦ñwÞ;ïØ•K’ Ù¬39WC!t]×¶­•(: õ¿0~ÃÌmÑ1ûcïc 6üÍØ8Zʇ-müQ‰QETÄnrbB=ª–±6() ¹(ÃÆ× 8ÕÈ꩘JÕ­mK]] ~ì5O¸Çáû.M·|§þð?LçêÏÿóîwË}–Vn|Ì òé+ª+¬~ý-:tßÇ} ÍúóÏø½}ÀòdË¿ð¨WüË%×rÉ¿œúÐÛÜhyy¿[ÿêK>|Ñx‡xÁßžtÈä°ç|rã kýœwüú=ݶ´õà»>ù­_^-[ßyÊñGì»4Ý~ÈÝžøgŸÛ¹ÉÝwßyÏÓn³ã—ßv©}÷ã¿zèÒè¯ÔÒÃÞ¹sÃAñ’¾àØýîòªo‰’Õ¯ýåow‹}––ö9ü¸ßxÇ×ç¼âÍ»nõ o~êñGÝxe2Ýç°»>îÿüû~Z)ëŸz×ßw:é>CtŒov¯'>ýñ÷<ö>äÒÇo¹V—]{ßc¸ÓË¿|ýâsÍÕ+>|ÚGoô¨ïì¯ÍÉäûozà|ÓOd}ö«~Ñ\ÅÜ”KþíÕ'ÞñÀ-Óånó+/üÀùóSþ:Þ}øipžã“ûüÙ÷ägh>.ìn×åÏðψáÖÛ>äiO{ðmV®ßò:Ûü¢=Yí±ï€’<¸€Ã2}PŒK Ffn|3™L;OΓóì™3DÑ‚e}Œ}@…¥étŸí;º®SUÇ×ÖÖw_¹»ïzïÜ–-[œs„Clg­U/‹!î)Ho±Ð©€°ŠÝ^ÅO‘³µ­^>_»Ó^ëÎ…Ë–ký“‚Šù§>­ýŒ¢‘Pµ$/£ëÈo=hÌß/§s!†›*'©B„õJ“+g‡—0©ˆ ¨Å;Ó~ˆ@Ôö}c�BdFf»½Å»éÊÊVç›Ù¬[ŸµBŒ}híNC ]ßI¨:×v#†vYˆhº4L'–c Ñ%”Ÿû®oCìE£‚�*1æ"&ë%˺¶+ZØp�Åj�ìe4^¯ì(¥_›£Z¼LP "ö!«B¥DC¾€2¡G‰ú1³+{îiSòi‰˜­"@úÀFê¡Ôa”i>h¢áƒV´{h 8ñ¾1W —ØõÑnÇqc?ˆœŸi4Á‚‚žØSˆ ‚!FirÈ »†#¨¨FERB`BÇä¡�mèg}·Ö¶kílçîÝW®­îZ]ݽ¾¶{mm½­·íZ;[][[[_Ÿµ}UÉ5¾™N\ãËŸ4¾I?ËËËÓé´iší$ 6õ‰+ÚEš°•3<ô›½îÕŠ_Øv¢,ô±©{_˜#5¤|žû™kÏ Pàˆr/…@”Çq:GzG‰ÐDUcL‚8䂬%Q(šS6¶aƒ+ßÌ¿Á®žéÎ/ôËû?å­ŸüÚ—ÿñŇÿ¿ßâ|¼€ø¯{ü¯½ÿF¿÷Ñs¾ô·üážøì÷_®�ºó«÷?vçãï_/Ä>åÛoüµ'þýA/ûôE—ë}½òu{þs¸\ôÎg<æMá)§õkÿð›Í;N~ÊÛ.ÆÀÚç^~â Ïìý&-Ÿ}æe'žòïÇü¯³Îýìîþ•çŸø’O¬@øÂ+Oú­Ïûç_ºô²¯þßûüç‹O>õÓ£ìÚ·>øÊ“ïz÷gžþ½‚cÀŽÇžvñÌ®8÷/yàÍ~òñ[ëƒúïÿÛkŸ~¯;=îmÿ±{xÔÝg^vÂs>w§×Ÿ}ñ¥_ý‹ãÏyþ£ÿàßÛê˜=tÝò!w>áŧ}æ›ç}ýÃ/=ò³ÿãÙo:7ÂO£­~ü´ÂCN:nyØ„ûwÊËŸu}÷>Ê69ðš˜ö]ÿ^pëÿñ´=ðQ'ÜÁ]»ã¥kÃÂ%ØÄ®bnêe§ýæIo‚g}ø‚Ë¿÷±çn;í×~ãÊõw8@Ü}eÏÿýÍÕõõõÿÏÞwÇYUœý?ÏÌ9çö-ÀR––Þ{C%vE‚½ŒÆ[4c‹Æh^c‰-±F (j샂ˆA"R”Þ–¶}÷–Sfæ÷Çœ;wî¹wÔ$¿¼oîóáÃçî¹çœyf晹ó}j:ÝúÞOûÿ;ë±D%úÏ'kØÔ_ÿêǃ¿—úõ_@êP¤áH.U•–æÚ·ÃäÛ¥õoì�ôƒ�pΥ߬‚PbYV4u=—P‰FBá�p\—1***‘°,³ÇOgÒÉT2™JeÒ™t*-„°,Ë4e¤‚)’'ÊZSsÿë£�Ãb—j¿ÇáR5¡ŽËº)¯ó~'Æ:zU!ò‡lG„–«¬°¹BãdQ{cÀê˜S:hð ð~ù§ y%‡Ž�|ÇåN¢ÞÀ9w]@RéL2•ÎdÆD,šˆÇÊ\ו©.u>uc!?:K~( !„X,‡•Ù�!¦iÊ3 Ã4M?†" `X©Žj`FÔ¸)YÕóyvf-v&ïqÈ #‚‰5{¬ÿžÍ5›  Ê.*dO‡µú„jƒœóAy>>Å”ÿ†b²Ï9![7(1 Ӵ̰e†=/çyáy\Í2!IJ¬P($Išèå<ª†8p@Á…` �Q‚‚ä´¦Œs¤„E67ÇcLDô8s]×vÇq<΄Éd2™L¦ÓiÛ¶Çqå¾ຮçyºF`‰éÃ+ËÄúòfYV($Sp–wz²VBÇî!úRÈ›>e…C-4xùK)8Y„ ’ÍzèGÁ Èa„|û¿ê¦ÔÉ__Œ1 %•ÖD+û‚òë˜êWßÙ'ß碈l팰ëQw=ïùF :î‚K'WïX¹ªY€÷õ_Ÿùrì¿=w¯þƒ½îæÓÌמ|£^�°Ík’ãîþàùszåüɽ•K–G<ñ˜Áñžûž6ydûš5 ù.H›_yêýçß~ÅAìñí?©ýèÉ×ø“Ý>ïæi/Ž}àî£Ë YsæÿåÙÍ“®¿ýÇ£ú˜|ë/ªþ©Ó�ÉåKÖVzÒø¾‰Dßrx¿í«×æYùqûêíîû«†k ÐŒ–WTTTTT÷Þ6oß;îRM€­úãa=ûùR½�l^½±ê™Üy€¥žaëæ|¸aäi—Ù¿<QsÈÕ—OÜ6󅮨>ã´>½Žzt />t€U{qèný{TÆB°ëÀ]:ñ½w?ûèºC9g¯/8jæ“ï4w`DsÝ›×ϸô€Ï>ä™;îú¦ÎѼüžžxú �àl|bÒcÏñ��¼ºg~ôY?̽möô7Í㦎Ó#mùºûNr¿oFwünâÀÊp¸¼ö€óY¤úsºk^¼| .a3\Y{ÔË3Ÿ\38vèÃÙ‰OS6üú^jÉãçíß·<dź >ã9éhâ-»c/C‡>´™¤—?ùá£{—E+ú|ñóߨ� v¼zŸá5]¢–©yüµ·]9y¾å‘h·¡GÝü÷’oó;¿>~LÏX(Ösì36æÆVl{}úœ~'<Æ€@ëm¯ŸÓ£ËÔ[�€­¼{¿ø¸ÿYÚ?ùý”Ý«ã–ï1ê²·Û�ÀùèŠþ#SfÚ�¢å‹G.?¬G"Úmè×½±‰�[ýô9ûéU1­X¯=O¿ñÖŸ>º:‰õÜíä?|Þ&Y´W¾påaC»EBe}ö»æÝB;·h|÷†ÃÇöë±Be.~']´!¾õ½›ŽQµBå½÷¿åS�DÛ—ž6~`—H¸¢ÿA<²0ß*ïοjPlÒ£uòbêõ³»×L{/îú×o8~Ï~•ÑDÍ>g=¼H²(ÚÿùƒúW„ÕÆßñÙNά;Y›lÝ’¥éÝ'Ÿ<¦k¬ë˜S¼_»*ϳèû<ž[�¢µ©YtéÑ#‡ÃáI¾ãz,Q‰ŠQrú#oXè�ð ÷’8ø¾u¼£ÕáýãþöÐ=2õûžzǬº®kÔÎ)ó÷«w«®Œ†Â•¾dúªNV­¨Ÿ÷ûÓ÷í[Žt:éòçW¤A4<s\Y¿Ëæ¸��ößZÓýì×Ó��î¼+v=ef«þxÁîí¼÷“^ÕÓf9�™97O]Û-2C•ƒ&]vëõS÷Ð%©è?þŠW60�;^¿ò á½+"–ë1âðËžYÒ^ìtüý‰h9ÑôÓ1)•©ËåáL¥rð+e ³˜�µ³#"rÁ]ÏõuùnÒªy4¨aY–LO˜L%©m»©TF�ZV8‰E£ñX,á¸^2•æ Ó€¦ŽDbªŽ×®“buÔªŽ˜>rÓQ\᩺èyzùéèñŽ`›~CáÅ¢/ œþy±Lìú«D>lÖƒMméÚ¦õ7«ñ­ðDGØ8g<4¨Ä`zCžçÉŠ˜¨½Ö763R¿­¾µ¹-‰G#qD*ýb(P•²Y P§UÉ›ë¹^6a¾t¹×¥$©½RyÚŠ+|94 -I • %j§Ò…Y Ý’‹_Iإ˿t=à\f*Èz…BËf {[ÈÏ2B^:×HŸmÔ¹›Œzƒr¦à¾Syð_ö~x QáiÊG�®Ç™@(5¬aXÄ¤Ä „p&¤‘„,܈r^˜Ç<×õ\×±mÏõ8㜠Ïu ¥H\1—yó¤ÅœãÂc‚{œIÿ Çs\æ:žãq×ã.Æ ”…<&˜Ç=—¹.sÏv<Û ¦´ØK~$Œ1×q\Çñ\ٚǓ2é´ë8œ1”é4ü ©ºBéŽá;b å—Yg߃#àA$@¦®0R0(DP"�*‡/˜ó‰‘n<Y£¬þÀ÷ ɺnén\Y½�È„A½¡¯¨Eâ«�t9W™2(‘úbõ¬_rÁ/f¡~n ›¥Uu¯`KT ¼?ûDÍ-®“U¶k”ùjÖœ¦Ý'Ô !½|ÉšÊQ£¤2À9v[öÕ·`—#n¸ïšcç¥>2÷:ê0úò­7½¶²yËßÿøê çŸX›ŸìnÅWËaønà ��:xì¨ÐН–y��©ù·ýìÍq÷ÿîÈnE~ ùÖ¯¿nì;z¤L’5vPûÒ%$Æ}Ðö§n¼{öÆ–µ/ÿÏôäIçY®?9àŠûnš2¦’¾øÚ§ïœQþÓ_Mí‰�@Êúï¹ÿþcª#ƨ ~ǹûv×ÍË´×ða‰oÎøl[Ƴ›·´2sÛ†Í.Fjưÿµy¹û´¡�[‹õÚã'ï}×'ôèD}c }ÉSg=4ûì«OÀO~3ÿóÖîãms~õÆë;j/xöœß?¼O÷³ßf§¬÷È!éU_µp�¶vó· öš¯�ß¼åÛ¦î#w·:x×.QˬéïÄŽŸz@¸ã[°l¯Kžþä›üv÷/¯=áÊ·%Í=h¿ûÏ^¨¼nþÖÖúofýáÇýÃ{}dÕ·ÿÞ � õÉìÏËÇ:ºeæ W~4ê¾Ô·n]òÚÍ“z��:üóÛÒétë;Óz“ÔÜNøéœ!·}°z휛û¾wÑ\É@$×-Zœ8ÿ­U›7,zì•÷ݳxÏ{Þ[ºjáŸ]ÿ»Kï]è°UqÊŸìSÿ²põŠÙ8±6gßâu¯ÍøxÐÉ' ¢!Ðzâàc&¹oÏK€¨Ÿ;{Yÿ jÖ=~õM«ûëʦ–M_¼øó}c��`xÏ7Ét:Ýü܉!Ñðê¥ÇßðíÁ~¶våëÑgÎùÙó[ˆæo|Ýÿ—Ÿ¯«[ûñCæýî‘­Ç<:wÅ·ß9âó®úóZà,üÍIç½Ú势-Y³ä›'ö(\¢uÅGóèi/³iãÒY7*Ö÷Ž—Ý—>û½ ÍMk?~êÜ‘ˆÆ7.›|õ¢±w}ðÍ·óþpÀ²_wÉ+õÚþ`îqÄÄÊ…Ÿ× �à|ñþGâàÃ÷§KîžrÆŒøe¯¯X¿ðÁ}ÿñ‹3ïXà´½í ?ÿlÄmï¯X½pÆûî,•ÃNÖ¦1âˆ#ª?¼ëšç–4Öö?ülÌy§ç9t|ŸÇõõÈë·Õ³•/ß~ëÿ<õβ&M/´Ëë±D%Úuê`uðm_ºiÏß~±jÝòï?²þOøÝWÿÞ”¡±=ûÑŠº­+_;ߘ~ñ/_î(Іo|òÜîÚrÔcŸ­ZñîM>¸hòõsR•MØ­þÓOV3�oÅGó·5-øt™ÀÖΟ¿cÏ j^xû§¾ƒy›ÿñyæ˜gVlÚ´ìÅ©íOüæ½Þ7¼ºxÕ’™§ÙO\rÛûi�‘\óÅ¢ØÙ[¶võ¢™?¯ž}ñ‘¿ZÿÃ1YÞ²&ÆÂÓT.Ò;{tÊdB|›¤:Q©ŒV2TAÂXRW”Ñó¼L&“J§"‘c¬µ­5™J"b,‹„#¦iʪÒÊçy^:ö</‰”—•SJ]×M&“MMM©tJ¦FüÎ=×�sàÿïÏÔ·þ]ù-]¹NEQ÷÷x¿Î[GÞ ˜o—.ì‹È–ÓÁ­è˜v‘%(f½×¿UB¾ÙŸsîyžëºŽãxž‡ˆÒE%;¶×Ö–Ž„ã½«ûZVxË–-MMMáp�cÊx«þ,Ê­Î�çÜu]×u9ç”RiVåuþå2É" Åãq©Î ”Êú¦i†B¡P(T8ã”®h |H¯òÒŸRC­tŠsÌÏ9¢sx¡|в,•…AŽ¡U¹Òu~Ô#Òê(Ƙœ»€ü“l…¢b–Hù1ëñ”ÝŸ "ó„m»Éd2Ùš´Ì°i… 3Dˆ!J .T„ ¥ú˜È6² Q$¹ôbÉf8 ¦A Ã4MÙc2"EøÞ%Ô²L™–¥£N)þsA7šœk7ø=9ŽKýrƒ¾>ûLóœ¤Ô¤•.mÜr©+õ °«D›�‹$ï$# dyð"Ɇ`¶³YÝj”O<[ÁA“«œú’äïH\y’Vdä!·~_?°Br×þõÂSÿ\uëc—£ Rmí<Ë_}Œ'bÐÞÚ‘ª»M¾õæýW=úÓICùˆwâ¥Çö%¢þ‰£c†aFdßß®hmK†1`,uÚÚ�oك׼4ú¶_OÈwþÕƒC†afåY¯5¶%!–Èc¢­]�©=û·Wõÿìž³0ü´W»ŸsÑø®»: ö'>°äGW]2VbFì~Ô¯L¿fÿX·'޹ë/—Åÿzü€²xÑÇÝ>»Áˆ„)$¼nÆË·žKŸ7tòÅ=§½ÛܸvÞ,üéq7}’é˜#¬¨èÓ'-K 2|ÛT·µx¼»¨[ùáç±I×ì½Û€x·ÃÎøÙÀä›K—:‰QûWnþ|S«M 7guI-ÜÔÀ¡máÆÍƒú첋ƒR´½¦w¦¿WqâÔ};S5C'œ0nX¿Ú=N¹ëæãS/?÷A*ÿA/‹&×-Y¾Ív<¤: ¡N9©ûÜ™oíà,š=N<zÿ°™H„šV-^ÙÈ㽆êîO6R+kÎÌyú¹íGÞø›ã‡TõØíŒëÎøå;³¥»�FºV÷¬ê5üØ NÄbývZÓ{ø±ç×oÃW_· ¶æ•¿~>à⻯4¬¦ÏˆñGíÛ3{Üâ›þ6ýÓáSNB Z/?üԣᙶ$çÏþ¢×G4‰¸±cÅ—k[IEŸáýýˆ $†Ï"o=ù7zÚ-7L¬íÖ{ÿK®ýqlÎÛóÓ��@âU½{Tõ{úyGtu+ì5¨wŸ±§Ÿ=)¾ìË\pMŸ¾jÿkï½hÜ Þýv;âð±åÀ¾¹k¿ˆa†;êñí�H¼×€šîÝû ìo*ÖÆÊâÞæe_mJ[]j‡öM hzëñ—à´ßþö¤Ýúö5ù¶{ξúøkú<|àñGD>|ýÃ6�÷Ë7ÞmûÑñ? ùÜ3_ï}Õ]gŽéQ5dò/ºÏú÷Þû–eæ>7³õ˜_ßsÆ^µ½ì}ü¡Ãv¥±³µ;øº»&·þõêcGõwë†C.;e¸ùƒ×Ö#é~ä \zP¼é³‡ÎÙkÔä‡Wøàl××c‰J´ËÔÙê0ºôЧwÿ±ÇÝôÄ{ýÄ3 þ­zì:hôÀ•}Æýô¬ýœoWl,î¦Æ×½øÄìê‹ï½áˆá5ýö<ý÷·ŸÐüܳ3}4dùì9Ûß<gnûÈ-sçlà¢~îìå£ÿ}2÷Ëþ )¶{kl„*{ö¬ê1pÂONÙà UÙ·zЄ ¦Œi]òÕ&ɉõЯwŸ¡]øÐ}gÑWþôÚ?MOPIÊV­ÂÈÚN©A¥‚Àu=Ji8¢Yc¬çy!Ó.k€SBcÌs\î1Á8÷˜TÈ;=ÏSˆÅ¶mJi(épË<é¹@ *o–HÌ4MÎy}CC{²]‚+Îy$q]·±©É²¬�:*ìXG0#pØÕQŠ~E!(ßí=û9Њ~¿ú³°õ@»Pà [û‹6¡Ÿ¡õ† o.Úµ�ÔÜ/„s¤ó/¿•®æ…¾…oVòl‘6ÈGAúg„¤œèðFÁˤґP�d’6!“ɆÒ@% Û¶3™ c¬K—.={öê^Õ³{UÏx4‘lK¶µ¶¦S©ÖÖVÛ¶£Ñh<Áó(%–_b„àˆ²(p΄à”¹H@�gÜ3LjZ†�4ðš†Á</•LÊ?Û¶L“3æ:…bѨA©çº®í„­I Ïq !ñx\V÷ &ÕÔmj`‰A}‹9—ÕA0ÁAz¼£_úA �AbP95<›DUÅ“9>ÔEéÓ.„ã,G˜f+íÉ­)ç+(Ë¥úLš‘Œêù(ƒ3p¡<5@5ü/“z“€BÝ ³ñÜ=‘˜P‚DèÎ î¸.%Ä4Íê^5®ãÕmÞ’±p$R¯=&¨a�B(–ÙQç„R11ùÁõ¼p8⺌s`œKssƶ Ó2Le‰ AÐÜ#Gƒ%ò¢ü‡¡–!ù9BÊθœ…"€„�™Á EÛöÁ­îqÏãtû¼|V€ ”(¯'¤�O:!0Oyjæ‘×Q@ •9 äԠHsa¦r™88gRU!UE”R™Ñ“åG:¶•*RͬR4èd}«Q›­ºAʉ. jÛñýˆÊÊé{Àé2#rjn¢šìdرW×®‘»úÙ³&þ|ý™3_¸td�0šˆ“d{»äD´·%!^ÖQõ$ïëߟñëô/æ¯^·yý¼ßöiÊ©¯•'>´`ñâÅ‹ñüý˱L{Òi‘lKYñ¸%^ýÍCxÑ“õ‚tæØ«ß\´xñâÅ‹çßñ£ÊD ’mI‰D!5ïÆÓíuÿ—k×Õ­z÷ ãþÉ?yqÇ®õ<=÷//¶yÖäª]U+ž“n~mɶ¤“i^ùàá1cððÁAœ:õh¸²ö€ ï»î 5Ññ‘ïøàã{¦>1퀇~râ¼®àt„ïho‚²îÕþ9̬.¯´ÛÛ°z\mץ뗶¤—~Ò6ê'{ؼáëÆÌ²·õׯê¸V‹†7§ÿý;dø ÷í×ÃÞ¶¥Eä=hŽ»õo²ê—ô<ñ²§¿j`ísÖéýç<3s£ýÕÛ³R'ƒ²ÉøÛÍC?ºh·šÇ^ÿò*;øjÑVW×ÒòÒéÝÃáp8Ûëö¥¬¹±1ïˆAÊ*Ê!•J�`¢<étø¶ºmPÓ¯¦ÀÃ×½<cá˜)?DŠ´ž˜tÎÃo<óVcjÞ[s*Žž¼‡IzŸ÷ø‹—”͘2¬ÏîS¹àÄÍwlªËlzôÈòp8—þhÓÔØ–7“¤¬<‘N¦åjM”ÅíT€oÛ²=Ú§o>¦ýÏþ‹Å‹/^¼àáú¯â ‘‘W>ÿ—)ÉûP{Ày|Ò øö›YŸµ¾ŠÇè7°¯¨Û”§€Š2õØÐ¬—>hs¿|åõ¦‰S¯ä[6mÉ|xùÀh8Gú_>×mnhdm[¶¦ªúõÙåo;Y›|Ó\²ôôY߮ݴiÑŸúü‚É¿ùÒýç=Xµç‰çL»üW÷ÎøäÓ{F̽åî÷•F`×Öc‰JÔ! ³]Z¤ÇÁe[7Öý;b¸ë^½þ¸=j«Ê]G]û‘#“!!V·©ú ìçÿ¨…kôJoÞÜL†udß/Þݰcö¬ uÝÖÎúû¶¦ÞùbÐQG èªÿ²Wîd÷öIîÐÉ”��L”% #wkBƒ‡ÕòÀ6õCˆ ‘!Ò ™Jeáy¢Ð$?\Vâ J…€Ÿ å~gA�� �IDAT.ˆ‚„H[+ÉÆl+ ­¾ ¢X–‡Ãá°D¤ 2—(TzÂTÈW „€e!ÿWS'°_¿¨ó©PÑÈ"k¾–ãÜy‹ “ëc¸§(ŸëêŠnÖt*ÐÈ÷­PÑ-œºUYä“4ì+e“ºÒÞÞîdìx<F=Ïs‡s.ýM„’ùo¾ùæ‹ÏÔºðÂiçœs^ïššöövÏåUU=ˆi†d»2@@¾_~(JºRC׳È#KRÎ)¥©TJFËÊÊâñ8¥Ôqœd2)sÑË`~ }ÕŠÐÕ.ª!²®4ejüÕmúÜ)t-ó"«1²qC áù~ó„ÐïH>°—’`Â\g(¤I\jñ|ý¡a(½CÀO} (æu„Ù‘ê- ÛÊ%x�.P8@bVY¢¢½5Iа3öÀÚ}{÷íYÕ³wu­ë²mÛ¶»A(5mÛN¥RµE[–eY–뺄�ÒÔÜ,XV(Žd2×uãÑx&“ñçËcrœ% .ó:ê¯ô°Ð•#rÓt˜'}RlÛfŒQJ¥TV“úS—µe«;•ðÔ‹Jt!ñŽgÎ…Af"D¤HT°L!©ÅÛŽî—DHÀxªp¹R9…ƒ6Å~ ô=§#t BÝÍüâ„+×þòë7Pá?>z@Ó×_o‘aïK/§#F)>®ìÛW_øÇˆ“O³j¿‹¯›ÚíÓÙŸ§hE¿£F5jäÀn±ac†Ã²¯–{��lå?¾¶‡n8Ÿ¾þö†¯f bèð?mYwÿøg¿A{9jÔ¨Q#‡×”÷5ªË†%K%àJ~½xu|äè¾Ô]8sæÆýN=©¯¡šC¯¿ê(wîßïÒ¹ßýâÍ÷’ãûQbç·É[óôÝÓ·ïvÒ‰Có~ÜŠ ] �åáS„’«§ÿz9=yò½]üØË蔓ªx¥hÝ– ävëš›B±ÊÐa÷êV·ðýU‹V÷»¿ÝFÕ/~Õ_&öšðC°Åö7f|Øû¤]Ïð—\½rk¤ºwä?ˆe»ý?o.];ÿÖ¾³~zÊoy`Œºà’?öès3ßh?îŒI � Uüì‘Ùß®~ïgæsgž÷ðN(Çv²?ßñîÝÝÎx©1#ÉvÒŸ_?"ÿˆ!£’”Ê @A{Õôk¿]Œ f«_š±x÷)Ù¸˜‚Ö!rðÅÖÎzôñç^šU9åô}-�0j»î/Ÿ¬Yþ‰õ÷N¹tzƒ †!r,’n=»‡^ña›Ï¢ã&ß¾ àÙ®/ÍìGÚ«¦gjͪ-ù¿V·#G5jÔˆ~Á¸™ŽŠ 9ñ¶™‹Ö.~d¿¯o8éúYN÷šjºqõ:?7…·nõzìU“Ò9øÌ)eïNý½é¯$=ëðr Ý{vñئtv˜íÕ÷d%ª«ãÛV­nÝÅ#éNÖ¦ØñO>uï’òÑgÜxþˆ•³?Òºÿ׉ö3ªkÛ¶m©|Îw²KT¢âdÄbfÓŽ†|£Â.­ѲiS{·žÝ¿³ïø÷§ÖW¯;ïicÚ+Ú꿾{|Ça´º¦—X·j½¿IfÖ­Þ©®®@c쉓{Í{í¥—ÞXºÿ‘“;bï¯Þzyæ«óúü0ªÿ²‡ŠìŸE)‹Ëü?@}ÎÛ¼¡ªzþív‘V¡È%êøH)5¡ˆ~9iãK©<à�*³‡g-6®ë¢€°¢”¦Óé¶¶¶L&#MŽÒ¹€�šÔ0©a*í“À…I Š„¹ž`Ü0 éÝ­3W”QýÛÀÙ®#äü¯ Å@G-= ^×/v~NÅ|ÝQYçJÝ8%wD×wztæZZuù ÏæPêê­¨~é7 b$¥ÔS©L{K; Ä bÚiIDDƒ˜Üɶ€7nÜ´i?™<yò5×ü┩§pÀ!Ë2u]7N§R)Çq\ÇɤәTÚµ;LQU¨&ðW„A,Ë  ƒ¡eFù@ºd\×fÌhP~4‚q'cÛ¶­ÆAæ™÷³ÍkC­#då´˜b‘EõÊK ‡Ã27¾„ñ™BÈâ Ìõ\Ûñ½{¸¯°“D&¥êŸRîè¤$DIQ8ª]á"Uo<ùxP!KÞKÿý[Û¶·oßžJ¥bmß~eñDßš~U]»Ç#ñT*ÓÚÚŽHdá@Ïó¢Ñ(f=öåkmÛ6 ƒ1FöèÚÃ͸ÉÖdy¼Â0LÇñ b (¢_¢EEïUŽ üì1ÿ—D–H0Ã!"ã:Ò` 8�Æ‘sä ¦ra@ÁúUe,uôÑ.á€Ë”A<… %D¦(š*µZªz7;¢Â Jñ©?¢”kr‘ 2K£ÿ¬&u…;œß.P‚@AÈü(ŽÚ¿NXè€Ø×\õDŵþÅ!;“Édl—£N=k÷Å÷ýòéEëV}p÷-uŽ;÷ØnÅûGª‡-~ññ9ÚRË^yê-G Ë‹h'½O8gâ¶?ßôÀÇkÖ~öÈM®;èœ)ièè§³So¿{a¯ÚË>Üöô1ºAÞ:àÌÓ«gÝù«W–nXþúÍw¾ÕåÔsÆG€Ö‚óŸ{rÁÖdrÛ¢gþ2×6zЮàY¶fþ'ÛG¸oNK ¶¿}ÃOûýgÉŸávë¶UógÜòãI?_0ê×¼l8hŸÿ»SNºiVƒ(>t|óœ¿Îœ·lýæ ËçþéÊßÍéuìä=;Æÿò·Çµ=O•ÏA#âÛVÔ§˜av4¬ÛÆ zðø}’ï߳૵íõË¿yöÁ5Ñ£FŽ Ъý&žzàÓÕcŽŒ…ÇÚóÛ‡çÕsð>~@4ŠØòÚôjO:yŒö£±ˆ»jñWÍAÄÍ[êÖÖmß²rÎ/ºþ®gžh$ÿA¾iáœ%››Ó¤jÔÈÞf[K«� ½O½ê”¶û/ýc椳�{Íg-ßÚš±zï6¼»hmifíÀšºÙ/Í^U·áë/Wµ†ÇŸ9%þò¯®ú˧«¶lߺnÉ‚ »r@"ýO>ÿЭ]rÝ‹‹ÖoÛ¶aÃiÊgß¾8cÙ>SOè#® [ Ã/¸zÒ×·\ñBÓÏkˆÖó?[]ßîÅú\é´´d°kÿþñoÍül}Ýš/ÿ±ÞîzÔ™G¶=yíM//Z·u{ÝêÅ‹V·í ‹Ægœ»Û¢;§ÝñÖÒMÛ·®ßÔàìä),ÚßñÕ¼Eë“¢bðè~±dK‹Wyôù'Šç¯»þ¥¯6n\úÚM×>™>î¼c«CÑ(Ô-ùrsF�„ö;÷Ìš7¯™öN9oBÀÜóÔÓνã²G>\±yû¶Ë>ûz ?ï¬^ïÜxу|[·­nm]±Ú£_›|ÝôŸpþŸ—yX1txÕ¯=ùöª–tËšYO¼²¢ëÈ‘UD-¨ø8°5¾üÁWk6o^óåkwÞ6½~ ”ãw[%*Q12w·wêµ?Ü÷á·›·lÞ¸CjŸ:Y¢}Éûï,Z³iígO]}ׇ5SNÙçß)sœs@àv&ípõ³¢¶ñÜ>€µ'7aËÃWþæÝ›Ö/zþªÿV~Úy¢�ÆØ©'õz÷†_~À ʺ~â>óo¾áݾ'Ÿ4< ì(¶~'b>~ó£å6.{û7¿|ºéÐÓŽîŽÚùc»FˆH²Eå!J^'ÊH¨Ò¶çN]�†iâçBSÕ×A@8BH3#�d2Çqd®iP• MþßÖÖ&šŒ1y§<¾Û¶íÚŽî# °eàH*?ëhSu¬¨åêßF ßy@Bhá÷º»µB/;m¥ðÏÀPˆ4¸_ñYä€.Ÿ-þ “œ¤<4¢Îñ*X¶ˆšü6ˆÑ4MˆÚueÂv Ç!€ñx<‹‡L+lY„;•’÷3ÆLj˜¦Ù¥¢rܸqG~ô¸q hYxØaGBêë›››¥0+Á¶,Ë’’ Å‚§ œ>0 ‰!+` ¿©,†Rƒæ8ŽLO��–e„B!åb#W Œ—œ€·B1ÀWô¯d½²åý~¿,K)’”µ_¹…B!ιTU(÷à‚{ ²H2÷ˆë‚†TR©Î Œ˜ê 9À¿.x…\ æi2L �pœs&cÜuYeEWäØÔмnÍ:æ²Až{ÆYWýüÊÑ£GSjmÞ´eË–-­­íáP4ŽÊÜ“²Â¥ê¥”»¼¡¡iý†u·666 !# $D�$ZBþBÝÞUÂÐu]ù•JˆÐÖÖ&•{ •ަX—ÐÌø;Õ·¡n}�sNþ\0×÷jA.gMîYÝóf³¨" è#E÷ÃÂ?y„ÙMóöÈÛGüÎc°ØÇ.hÕøðŽœÖ;y¶þ“yËZ>ºfT<"©ü¸'w C/{öÉÉ[oŸ0lÌÔº\3ãþ:J�€ÇßûÂÕÝ_;kt·ŠšCnZ=þ¡ç0C“ê3ýëEøØä‘#޼?}Ös_Ðo—~æÂûýú…ûöYxù~Cö¾hÞ¨»_üÍ!1�Òçü?={&{tò ÊÊG?œúôŸ§õß•×y+—­Š¢¥Yâ­kÌ›·xcª£QsÞ»tÄϺgAõÅ/þÎu{E@$7|ùñ¼kZDñ¡ã;¾zñŽÓÇ «´Ï)4õ§×ïšÐQîˆ 8éÚþ-O̼ôÀ‡Î?bîÚ^]ºFh×Oñö‹_òÐþ£ï[÷ô­ËZ0qÈíÇWµî±ÓŸºjÚ§Û÷žpõ5a��Ò÷˜á5iyXß@â ÁØÛï¸aÕ?àÁ7ýmÆüÁ'Ÿœ5Ù£N»üØäC§Þ4OóÙ •C÷¾úŽ}k«ûí~Ê#í'>ýæ‡Dò­ÿxæŠÃ†v/«è7éOä‚û¯>È�ˆ¿ôÂábÀigík�ß6ÿÁièZÖeð”7j~qÏE£©±ç%w^qÒˆÚ‘G^ùò*–˜x÷ë<xÍ“wë×»ÿÇ]5så.å '}Ï}æ[‡~ÝÄ¡½{?í%Øc·>_ú +÷Ÿz|o9>EZ�¬:þ²3z‘½Î:u�¾á½ßœ¾wME¢û˜KíuǧW“ðÄkï:¾õþIƒúïqÒm³6óS{í–!óq؈>½ï7å–w7í’¾ÎuõK¯\ZñÚE ¬î3úÒºí3²g§FG,Ö·ò•_?¦Wyy¯ýoÛzÌn:&]Ž}àõ{öXtí!ƒxù¼áw¼öЫ ~ØE—þø²Ÿ<µU�Ðç^´[Àó/Ü7�`íyÃ+ÏžœzôŒ}ôî;ê°‹ŸøG;�DÇÝþƳSí?Ÿ¾gmïÚ=oY1dï¡ø©_›¼~Åüæ}½ÕÐA·¼ð»1Ÿ_¶O²{^üÑÐÛ^øÍ„HnAýÀÇEÛê÷ÿxÉá»õ¯yèåoU\üâ_/BỬÇ•¨8‘š³xøÄö‡ݯÏà£þÜ2flm ;[|ûßo™<vàð#n_è£/ß²'©`ÿéTvÜ-8zÛ]‡ô­H”×^¹¨ß°šêÛxnÀ¾ç=õÊu½Þº`ŸÃ&ݼæG¼öÛñq��cô™gJ‘ S&•v;zÊA^jìÙ§ ìNÅ÷ÏïB"³üéi ¸÷yëúóÿtVoÿ' ”BõSŸ;èˆ ˆè2OúTK¯W‘uˆÅbÌóRé´i˜ˆàyÌ0(c< 1Éã¯�A)•?ûéd �$ÀÁR• ÿ—š0<Î1\Ï“ðFž°Uõ/(8øràLý©Ÿuä\dd;=8îʱ²èmÊUxv|¿~§¢Î•:l褧E9 äˆ|„/ôb›-æ§” 6¹è_ÿÙß|¥¦³"ïADéøí¹®@)!YåêûZS’Ig� V÷\×´,Î9s\+² 3 W”UÖïh9ú˜Ï;÷ÂXIgăÞÿøãÚ¼a̓MËG,{ŽãHéâL0Ƹ`’ƒ�K4ëɈ2¢œÏs¤@ªa”7žç)«¾J@ДïYÏ�ˆD"@P. Ìzì€ (” Ws“B «‹ „As±�� Ã|ÇQI1›WB¾5b…äJ”!2 Â0 Û¶Ýj-ï'ùÙ•ÄêZ%Eˆ¨ƒ,}Írƈ‚®ªÓê[’RͨðËàq„¤†ÂcÂN;5½jì´SÝ£×î£w?öè£{õ¨"<3_zëYï˜mikŽFCMõ¶ŽÇ£sÛ¶ ÃDD‹Z۷ׇ­HkkkeY¥íÙ[6n®èVQSÓ·=ÕfYr=¦²¿æû|`°aRO꺮¨d2I©A©ÙÖÖ‹&<ÏcÜÇãÙê’²ËQæ�΀snhã_T¥¢HÊEZô[)Ÿúˆ�ˆ‚P@’5Å !2!hiV8øòLY~Ô<êï,Fyî`¢˜ÿWÞÝ…›dÁðªðs‘+§˜{'á��\p©¹ãœ3OúŒÈ%‰¾f› �©BýǹpõO.9áÄ;w¬}ÿý÷ËËË÷;ðàNî)Ñ¿ˆ>ýxîàÈç¸âkïûÑnÓùbÞ5C¾Û™o§ŠTK‹`ÛÞýÕÔŸ×_÷ùôSþýç½E7Ž=tñU+^?·ƒ¶½ö–:k_¸âä;+ZøÇ‰ß#>å¿þ3å¹D%úôýÛyï'ýÎÁgÖ=:é›ùNô¿pòu÷ýòIK?¸¬o>^üû»oõ00í,égŒ ±léÒ‰'¿C|åå—¯¾þ% €£:l ACB‰g$ÒÁI:–wU;RÊO§Ó‰D"µ´´�@,3 £½­Í4L•ÚPý¯ì¥Ê é[ö˜'òƒo;·S¨ˆ‡ "ýWS�œÙÔ·:o>E±$ó`òB ñ@ѶT‹– -ºúÝ�€;sÍ(j*ä\Pâ3á3£ú%(qÊ*Ñu].c¹©oÍfŒqÆ˲ҩT:™t]7‰@!D:• …ÃÔ0 “ÎØ©t:é|þÉ‚ ·57µxð¸%Kþ±}{}×®]©žÇ…g—¹ŽíÎ…i23Q³xÕØêŽKì!® //Dnj@TíFд3”RÛuTºÁÜ"rÏ­–aî! _0QdÓˆ*)Q‚s/›ÔP½ågÆX(ŠÅb„T*•Éd䃱XŒsîr?Á!Ä¢†a×Ñc¶e9çRùb,„ù†Ü·Å¢ UT…rÿQ�RÃ4M.P€+˦Öö­ªŒ•÷ë׿ªk7“B*ãÙËÊÊ Ü–lÊ8i9_••åmí­�€Ù¸ Ó4ë=Ç0xäÀ‡ ²qãÆW_}¥¡¾Þq”€¬ž‡Tz^Ô¥Bç_r(=¤LÓĬÔ¶mË‚D¢¼ºººkeUÝ–ºÕ«W3ÖÇ Ã‚Psª¾ ä ´€»‡zOVM€ Ⱥ†‘UÜéþÓ©vu©èèæ¢_.“W»®>ðõAŸÎ^vì¼€T‡p–û-Q‰¾+ñ5/¿ðånSžô]íB;}oüËÔ1—α†}ÍsMýÿQ•Î]4㥺Cn8¶{Gm{Kÿ0ñÀ[—•ï~ò¯_ø]IGP¢•¨Dÿ[Éó"Êc" � „ú¡_ÏT/ÏŽãD"‰”8ç†á[ÇáŒJMÔg³L:ÝÚÖfRÃ2­,¾"œ .8gÌ4 @4 Ã4 éçé1Ï4MÎóP«jWµ ‹„!XGgå5éÊ‚Nxš¹µsõÇNù—�UÇTr¾ô¬‡…J“NÔ(zŠßa×`€„²9<Èž»3Ï"ªÎôžëJ°‚*/cv wÔoß¾Ý ‡LÓlnmIgÒ]ºt” îy{ªÝ@DB"å‘ÁC‡„ÂÛ·ïxÿÝY™LzCÝúîÝ»g2í€5²þŸ†A ƒ€SÚ¡ćXù*ž­è!+ù©Û¤A^"v®åo'„HK¦ì‘4ïë¶ýB’à_ª ‚ *«)ÈChÌWÈT ò[+’@z‚Lõ6Ã0@ˆd{{*•!(!žëF¢QÎ9x@¨ï7!µ òD"0qª³¹qÁ;Õÿ¥…)\Dºl5#ˆH *‡|ƒD4-kÛ¶meÍ--r€pØ ™LfÍÚµ›6m@ ±x8N2fd=í¹TQY–ÕÔØØ¥²[Ï=N8îø~µýÒé4�<ûì³nÆ ÇÂ�!@ºž‡(ñ÷%že•ä§çT#£¶S�ˆD"C2dXEE—ÆÆzÆØŠåËËËË¥úQ™ÅsC¡²®÷ŽVeáÈCþ¥V""$�r¹‚YF‚ü%”U?°Åq­�g!"È� Á¬o¿ÖBn{Ø%ò= 4œ YïP€TÈÙ¦ 6:©‰+âWT¢}7"ƒ®ú¸õªѤï´wš§}_¾þdîs×òÆÎn0v»qAûÿ.vJT¢ý$ë°Ç¶Ôýÿfâ?ŸHíåsÛ.ÿ6 ŸÉ•ÍNa "a™–Ðòè·JC¢‰@ˆ„.ÑH4“±ý¸‚vƦ””—•µ'“HÑ ”1åÀ5 ;•5¨e‚ë¥ÒiÁ¹aš‚�jPDšÊ꬚u‘ � ‚ Ñ©𿊶¨¸U×ê¶Â Y ; :Paç Ù; ¨' Ù+ÊFÀNØ PÃ.²N)òTNi0%–|ó8fù‘Oz‘d½N¤û´Ì&ÐÞÚÞÐÐ0l؈iÓ¦ÅÊzì±O|nYVyye&“² “4‹¥ÚÚ›*ºTöèÞ—#oh¨7B†iR$$QVƸíy2ÿ#e(ÄöÒÉ¿°w~Ç}#T”A E¤„Á€ �@Ð�âb�p� KÎ Ó4 Ûµ]× ‡Ã5@¦T0ý`]ÝàSÖR­s"‡H×BÃõü\‰‚ Ïó¸à0lX� \Æ ‚ à‚#G"”ÒT2ÕØÔ(„(/+'„´'Ûe�"Ôðq žëT(e\˃ ü‰d¸ Ÿ'„0Æu)B®K‘µÄ#�p™#. òl¿þz÷€¼ì¹�"P�ãL�"%‚ë8@Á³ù·ß~ Æ£å{í¾7A˰àùŸ¾?{ÎÂ…‹ZZ{×ô"T8¶M)Z–eg2‚sbœ1!Dƶ»ví:qâÄj]ÇbQÛ¶W¯^Ý¥²’R꺎@�¹%¢Ï0çó‹’¨Ù”1Rž€RjFYYÙÈá#kûö ƒ lilY¿n](Êd2†AD6{ ž`œCSs3"ÊòÔ0(¥”„p%0y Y5€?ìšS�ÍÏJAæå�Ž„d‹_ Ì ¹Th˜]n¿˜mEp¨f„v°ß B É{›&Ûþd+±Ùij�åDj¡ÿ.ÈõVt5AgïUZ•’3A‰JT¢•¨D%úï&bP’³Y ΜxÙÔôÜ7ÑÈ Yb"S¨lƘi™BÎ! "RbX&¡4Ì5 J à̶]‡¹]â:´(7QpYÞLp\€ ä‘gŒ3B€CžU ³‡^Á,´{dܯR&²©ÂîRCÀí ™ þÕ]µ©ãA³ÝéÀ:Ç|ÿʼnD…ð5çŒ>BS׋Ns¡v@ñã›»Sqæˆ(íä$?"ZF¼ë|æú"ò¯ _XI×,(Þ‘PdŒ�™�sÎC�jij·!@SÏ8ó¬sÎkoO%â•§Ÿqj:™éÒ ƒ8ž …˜ð€Op4PP·¡y[$fž!ˆ×ØÐ¶SH¡D†Ç$ÌãB�¡(€#R}Ô0&üä\d2¶a19ãˆÄõ˜iXˆ�)¥œ ð@ Aƒ3Áý˜† {!˜`2S�"Šl¨¶ÇüñÔËæ1„PÆ92d€ "œ1†$¨¯„ m—ÉÂŒŒqÏv)¥–aðƒ†9 @ê>­-m¦™AÀ°!„ ÁÁ &�ʾ£»_z0žˆ3îå "ˆ@)¡”zl—qΉ)r @�9¢x¹�$H�%2QŸì)1d¼†„¦)‚Ô7!�ÊH‘p.³ë!1¨A¨\xœ Æ“„œó Y]½rm·Ê*Žô…—^9lD[ª}Åê5ñ²ò~µÖ­UÝ»S¸ÈPJ9g„¢Ì±j…CÉ–Öh4ÚÚÒÖÚÚ.#aZZÛ,XÀ¬hÈe!„ h!“ QFÊ VšT:b¸žkX¦\Yáp8N3‡hXF¨[e·d:c§ÝMê"VDx‚‚@Á) +dµ¶¶‡¢q;ãòD·X¤býúõ}jú´µµ444ôîÝ˶m�\Äãñ¶ÖVÎ95 Á¹iÉt’F ®ö–l˜‰ DeÕ[@‚”A ‚�JdmZW÷�� ˆA �À—Øs$A壠¾’íf%6þ»’uò·&}¦¾R$›�_YƳµx¹Iˆ,’S È™¢ D$BêA¤f!›Š$o–Ïä‚¡JT¢•¨D%*Q‰þ[I€@@Op(²Yê ”úaÀ…O"›wZ>!§Dð ˆ(w]=O30Á�)1À��BF„!Àq\ i¨it~JÃbÅ ¾ �� �IDAT©„ÊhUTG�ºñJ3},ä:J \Tý…ܱ2ø¬~ׯ´�yÝ)¦,PWT†ùNØÓ/™Î ó£Öõ.@#±–@.H$?¥™/E„° ‹42êJaG$Æ–þ_|ñÅÆ›»té򯯬üö¥Ò Æ80á1&Rv²¹¥‰š … ðí çŒP�@!x¶ÞBñyWÓ*òó€ÿﻄø4û$ràŸ Ó$�¦JdPWùbžØHãš)SsB²Æ~À<÷ Õ ÈÉ$÷˜jR!Wo –¹�\pÁzÈ÷˜^pL$z²36"RJMÓBxBpÆ9ãRs”cŽH²ŽÛJ{ÅQ“Yž“¼´Ë×ê«�´%ãz~úRDœsä‚ a† \pàŽç¡@J„aH?Ï4ÍDY9²fí†Mw !]{TVVt­®®njÚag2ÔDêª\†ˆv&#sCÆâñtÚYº|ùœ9szôè•loÿxþ¼åß|[Ý»'5ŒÖÖ+A!<9Y �€�jÝ •ašB©E•©lÇ^¿fÃÂφh8‹/Zôåûï¿Fb‰h("'ãdlƒÐd{ÚÎ8Ñp³½.ݪÜÚÚÚPßdVSc3¢GC�ØÞÞÎ¥–ðKZFcQi^A ‡�åjij“uYø.Pê{²‚Žˆ˜7_R“ÜÉT #àˆy®RJlò�øn�,[ì@²–ÍÈŒq¡e±UÌçe ɺèÚ€A¶%N%*Q‰JT¢•¨Dÿ„€êè@m~®5 JÿgiE�€jFrE9ë%¢�Á8gç@ëj€RJ ƒ3æz®çy–eQB;2‰ƒ¦#Àücœ:ü¢ôô!êÀSêÀt”Be‡lœ½üƒäÇëBx¼¼h»êŠZ „«ty'ÝŽ_²S*dLWIp­´„ú3 Ä)Š· !±:¬w¤ è|ˆ$Pœ9s&íÓ§Ïܹs2™Ì€µžç!ˆ‚sO1à8N:“2 “sFáÜ#-Ëâœ{ž£¿_ùê€T„¿^.óÕ‡Ca¿þº<”ø)å…_†0O|äY³y¾Î+§¡@àœ« ’7©ž@D‚Àó´]ú€KC®à‚1ff­²iòam¶ôT¨yQÈ› ’Œ¡„Z–…H„òN×ò~–52ƒH¸ôöÁ¬,�1üT|œsÁ¢ŒÛ ʯFô„ ”†¯G�&€��@ R¤Lø:Jˆ´B›¦ކ “4ÔÞš.«(onnÈ,Ë2 £µ­9 &8Ž#7MSpN)Íd2¦auïѽ±±õãçoÙ²£¶¶¯ãd† ÒÜ\ßÖÚO$lÏà'æ�YĬYqÎ cŒ"‘Û‚a‘p8™Ê¬]¿žy@ˆ±ukaˆØÖÖOD8眱P(Ü–jCŽ]*»0`ÂÄIeå‰eË–e2™M›7x®M†B!Y>63ÆV(Ìóü”þ"0곜#!¤$ ÌsÊM­Ü ³bB€ø8ú¼Ë¦c†‘|¡ HÎk�²!¢@‡¨¼¢r/ω,€VYCúq.%¤/¾õI±Sû§ …ËCv eõ!y;^Ñ–¨D%*Q‰JT¢ý7"ˆÌóEBÊ%~` ˆðCZE´è1 ³ÔáO}ë±\ v™^å5(αv]øŸuŒ( w>ÍÝßñŸÔ ºhG…ý4E€Þ»�ÖRHR糨JBÿ3 ÚÐ `uýÛ¢ãÖF)0‰zxð¦ÛQò Ax€¥�óÅ0L°_ˆèzvïÞ½’I»±±1“ÉÔmÙÜ¥KBˆëe¬ ïw !¦e !”PJ€Ké2¨ îòm.8nþLª„…"Ÿ¢³*òH˜/òاª>E+º«÷P@ž•"¥)P/Ñg‚S黇 A"Ñ/døÖc¿Â<É•µóSîÉÐâ§Š <Ïãœ0Ƹà !Äu]©¿ù¸Y ò\eMŸCM´|Å„ÿŒ)¯¿¤‚ÐDò†4=ב™ )58—1��œ1$FYYÌuÝuëÖôìQÃ9w][a»6�ĉTÊ‹D#„zŽkËá•E#팓vR–2xpý–úÕ«WVUu-/O$ñdª•šÄq‚@îG_èÖóÂÁ9'AjPJ©¬Á™(+3MAÇqúöík{vScƒa„97Óé4¢°ÌiZÔ0�0 rðøÁC†Ø¶=~üøL&ýæ›o:nÊóY¯Ás]˲d¡LÓ4Ó®«f¹ˆ„Hé „ï®A“~ž¦@Ö›$�@> Bz‹ä6Øl¥¢­°YùZ’½'xEq«oŒå4ú®B²º3DD$”jÍÓA[Pyº 55E¯ïÒÂOа“Ú.%*Q‰JT¢•¨Dÿ‡ µC›Øê£¡­ÂÞ¾/4“ö™œ3§þ" õ€U‘y¡¥å“'i›Ù†ùò‚ÉÛWüj^¯ywj×:‡âEA{�‡ëßv¢hÐÍÑEÛ ð ×ùÚÕ´ž ?pO!'ßCM�ù)$+TtBPÈ™U¦T¿ �ÁÀô 4¡ýÍò«H$Â< E7c7¥B¡iš®k�\Æ£”e‰rÁ¨ß;䈄sîº.j"îO4å`‰Â=?7„ìÂÑÐG ²)d@„BÂúx !ˆ†@”>Kɼl'ˆ¾nB¦¨Bp=ÁwL¯õ¤þBÖ>Ôß_ ê çÜDð}…€” ÿ¢œR�`Dª”c¾„ûT&ÿ\+¾³ƒ�Èf§D$=v„„R`BðÃÞ©Ldˆ€À€Î÷<N1Lƒˆœ3Æ!`€¬š ŒsH ¡”D"‘~Õµ–I¶9E,& ϵxey›t&£a®G ôÅmÛiØ^_^Ñ5Ú¶Ý–jcÀäà›¦™JÙDÆùƒ¯_!²^!A%ù¿e˜ !LÓtlÛÎd¢á¨iYÛ¶n­¨¨4 «¹­5™l·tÄ ¯ñ!®ëPÓ´{Ë–-áH$‹„Bf(¢”664�òòŠ2‘Ned­ð÷ì´ãج¬¼,ã¤QÛF4ÞxÖÊÿßÇËš\Éd—þçì‚Øû¨^Œämv„žÃê"o¥dkí" ü^�è[=>¨Å¥·£)˜üe%sÚ�ä9å¤]ä)õ@�ª<‹B�ä=B"dud%*Q‰JT¢•¨D% P^Bþàñ‹ ‰êTܵü,M‚ê$Ç“A¹zŽÀ€ À²,½š|[GéýA÷†ÍwyÍ3‚AÎv¤ßYŠ+Ò¤úÁTõNï©"uOá AÓ’šV¯*¼?ðAÝ& Hç°£áÚ)éÞã‘Á¬£{€½�Kê À¬ËŒÐ| ƒ˜ÊŽzêyn2ÕfYF(d €pØ’¨@%óSÙ(å¤0îzŒyŒq!¸\p.x€½À€øŒƒlË5³QÐ…Ò"ózš¦‰D"‘ˆÄu²2E_áôu¢ÒÇ­¨�(§}éqU” ËÛä’Qe AjPjPù§@ ê§T´ÇϤ¯®Ë|‹Ä‰ÿÚì«Ô „B‰a†iJ�Á´Lyƒ™(ÁcždÀo=§¸$¡pH²!]å)!¦i†Ãa×u·oß^_¿ÝõœPˆ p8©tk{KS*Õî8¶Üp\×Íd2Ô0i8‡¢2Ê=‘H*‹¼p@¬««kmméVÕ…F[[[4 QKÑÒ×N`¨µïº."†Âa+’aeååmmm”ÒTª=÷Pk$Êz–œsÇq,Ëòkll\³f ç‹FæÍ›7wîÜæ–Ûq"‘c"•Jõîݧ{÷îžç¥Ói@´B–l:ü „0+Šjs–$…V*er!ˆ„¨lJä:Ù ¯#™ |‹ÅH®#ž-8ª4,›…Ch¿2¹…Ôëi­SCä=[¢•¨D%*Q‰JôßJf]r‰¦QmD’ó]ýõÛdp:Aªäm¢À_]=«›"ÕAp§|NœXÀzyGÏêÀX2˜g‚ žûT£ù…Ðô&ot¹£î®èбèÙZ§ïq´UÝm¢y¶¬ƒÒàf%Ð{€¬^¨×êó@§ôQê¨G–ef2™ææFÏã®ërÎ+*ãÉtRó†Œë&„#r9üÔwŒ1!˜a‘ï#“c„(p!ÑÇÇó<©#�»°/"ë0YA’¤4’c‚ÙûåE•†@5ªd/ëÛŸ§”Ñ9çü/SBN%èTà¢Ðø3N5½†'QkY&)äœq.dAG��‰\0ù¬.6BCz:p™È‘k=ò{ª%½÷GCBÆ™ðd3†•K`Éc†ÂÌq·1Æjªûµ··€0]Æ‘ Of²´,S�aÌFb;iš!!¥f&c‡ÃصkWB¥ ›››°h4ì2ÇqÙ}@!„@áû»#Aœ(ÎîHÑs“R×u¹çQD¤Ô4 H –e˜¦)››ZZšÊ+âRùå2O� žË+*ºlܰáÝwß …B³f½ûÍ7ßtíÚ5±Òét{{;"Vv)O§ìö¶�†!¦RífH¯ûàgÜ Ro(y ÂçY^$è×{ô5š>çœ@P´ ÅRICQ­AáÍŸ¥ðgIh¥=�@2"|õHï!-œÇ_ ‘ùnH$@ÈU¯7!„`êýßEõúéÇs¿ÃÝ%úçÑæÈÿgrU¢ÿ|*IN‰þOÒÿ.ÁþßÅí¿šXÇ^ê~ì�æA³‡x ³×AƒF@¨QUõÄÀÒ¶mßB+cÈ CZÒLÓìˆéÂã/äk Á ±�è…üó®îà >`¾+DÑ31ä#Þ�Ú7O‰�Åb@S[öú{hŠ’bC±§ü&–æZ·€v@=Eò+H"ÙJ!P O^�×µãñ²öö$c,ð†††öv½�¥Tºè#剬¦’s.D.ú]ïµßn~ Cý+Ðb(¤ï½ôŽQ‘üú¼„B!×u]×U‰ z„Àëߊl5% zViãå<Wû@Íšðóü¥²§WÑÆ­PØäEé$²ÊÏ9™¡¬ Wȯä`úõ5G†l/² -Û;Ù™HRiCr²ç1®M»úJ;j*HÿOàŒ1"„à Íõ‚1–±“åååé”Lµyž“Jµ4ŽÄAáñL&%“¶š¦É9·m›R#“ɸ®‰D2;N—%BÍÑh¼©©¡¦¦&‰d2)˲$ç~åÌðgPL¥ñDD×uËÊÊ�À¶mÙkƒBˆöööŠò.­­-¶m˱ªªªr½Œm;ÑhØó�Òé´mÛ=÷L¦Ò«V®ljnܱc!$± ÃhiiJ$Œ¹›7m€nݺµ··{F£ŽçJR@>嬑l¶B©‚AD¢éeÔê ˆB€ÒT!EDÆçR‘ùÛ]¡Dé­ý¶è­‚úV_&¹}XY.ÎWEFuÙ¦F^ÝD©õ*²ÉÈ‹ŽmKu~—Üûxð®ß\¢}úñÜÿÇÞwÇIQ¤ý?UÕÝwÙ]Ø]vÉ9 FDQ”ÃŒ¢‚ŠYÌ'zætžóÏtg<æNE#Š•(™e3Ó¡êùýQÝ55=³ †÷~÷Þ»Ïg?ÐÓÓUõtÕS5õ}Rýöü&WmôŸOm’ÓFÿ•ô¿K°ÿwqÛ:}ø¯w~÷:FuÒ§åFÊ–Ï3•ƒB&à‘OÊ”sxh¿¨rHL"wØ–eÀœ­¡†7TèµÐ÷Á¹^ÜŠa…õÍ""Œe™Ñ°.9!È®ïƒJ•ZÄ?¸‘1i(Vo­w…išáPªÎ†DéHœ»gÍ‚–vÌ:X…�~(ø*Êe_­î.ßBéäû² g Pè°G6­«Zô×ןDD!Ð0LÏó¸àÊ÷^p€ÌÈ·b»×h4jÛv4Eà©TS&“‰DMDyŒ½MËtz2׺‚ITãû† e—ñÕBËÈ ÷•êjÙ-eÒ@œ «‘Éót}Šze©#ÚùúØ…†C½¸¼©æˆ˜Ôu=Q(òEçYä*näŒ „p!ô„jîqðÓ @¢v©Ì -©¼VyÕ»(è®îHu†ìd^B¡�è9®x$Ú‰ª„d3£È©*PÈÅ(àYößÐK#„p�Ô0 ¨PRRÂ97M“1cËÖÚh$IxÜ¡Äðx£àŒ1ÛÎX–Å9—ë˜išÒS��šS±XÄåN²¨(•NEÃ4\×5ˆëÙ†A…ðˆ ˆ(@’€È ? Ž#‘<§Óél÷"¦3Ã2¸ãf¬ˆÁ "lA¦4¢îQ0cœðH$K&e�`Yæ–-[¤û†çy¦i2Æ„ð¶n©D"�ÐÜœ¦Ô0Mt‡0 ³nK rpfj•Š‚1ÆUã‹D¯øjßK…¡†!DVÞôÇ? 4W;¦F<vç¯lòa]£§Fé*i�@™UQs‚Èýið³âëh C ðP„�]Aä€àÜ´,9|úBÚFmÔFmÔFmÔFmôô­›Ü@Ê]–ÚÕ…`ð¶ëÍ ðnýùüšCwZùH ­E¶å¢¯6¸!­ Žº©ÛÉå^“s.= Ô¶X}«v¨étÚ²¬X,Fijj’¶eÓ4õMshë¬øÑ»ƒ�Åp~)6—å\èýê4€¬i:Ô{jŸ­Š„Ihr\ß¾çW¥÷‰z@9χîëo¤!Äu=)†aP‰E8@hT²Œ ÿPÉP'ë9ÏèÍ95ÖŠò‡LÉK¥1(Ÿ•¡¥·¹ž)Jï rOÖ *£ÉÆS€¦é( £“"¹DiÖ{(ÄpHbóE7÷í„Þ”R ÙÔ‰JG)•pW¢t•¡Qõ­J¨j¦„ €ËÝ€yy2)‘‰S©!„Ã4)ÁÑvšC&zžã대sî;7I>TŸ{Bˆ@îz¶ãøÈC "  ¼V�´¤x9ÓM "wœŒm§-ËÌ †0\Çu=[>Ï=…+<‰D(¥¶c;^ª±±Ñó\+bH-ŒëÛNKÕžª_1À9—ñ :Wùò,{9øôAg�<86Ð׋œÕ&¿¸È‹Qj‰„–à 4ë!wâ„„6¸ðÝ8ˆA så¹.¥ÊUB6'DjQž§ÈU‚�×uâÈÚ¨Ú¨Ú¨Ú¨þO‘²öL� }³¥¾]1›úQÇ3ÙV󶆅™kYw o%UÌBˆu­€fìÍo]h)rêw\Š`Jf BÞ‘¬øGÚSJ)u5›³ÜúûîÊZòÂÖuú3:ÞÓ?ê�R>ö«gB=/´²ê6ö|>õ;ŠB7W  ½êO©ò „pžƒB` 4X‡¾$$}_“à!� ”RÎ]B!ŒRFBpß›š%¿ÃµæB]¤3QË•ØÒ’À[W`!•Nú¸âgæôÏÝDB�—ùã¤úŠÂ„—ª+¬ñ��B'¨Kˆçœq¤D RÈ>;� ¡‚¡¾Ò_�3_ð<A"ò�?™ÙT¦] „˜Ì £”"šåˆJg‚@8—Ÿ�?ç"þ¤J)zœRj0ÓŸ}ç\�Ú)Y˜ îq.@žì'eÒ J ¡‘ÈÃV¹Âq2Þ�‚s böAåÁÌÉá¯Og5²Ž!#‘€HÛ)!„çyˆ‚1Ä”óÑCáº%ÑdqÂ2£B ëØõõõøqJÁ0¨ãfµ€ øA�A!�ý¸¥xÒV6Гÿûÿ"Ê2Ée + ¢oüBĆäCþôÔ—� À�ÃëRpÎG¶¢ 9pô•D¯\¯Ci r*ÞN¬H?Pº_|²SÄŸS’s¢Í› Ú¨Ú¨Ú¨Ú@Aãà£y›/?Å÷¶ü²Ïç‚FIùÊüûòsÁ"»ÛSûõV(ø1´UM``'×1¥”Hosé®6—ºB®)¥±dÂu]Û¶UÎ¥ËP5“<k[Hà7” ÁrŰΪzõ<h&»�V×:8Weu„C«µŠ¡ÐIy„vóˆ(a¡Î ÊÌ|Á„$ˆËÈ—7Å¢ c!Â�"¡�jCO³ï¥Ç¿MW•í(­§Õé a`ýc¨[T%$7äD•Õ5MêNHØd—(ÓºªY)STm"7Å@HJ !Œj-ojJ�½'U…!ù—€*$' ­ªôÚB⚃ ƒNð“8 _ä ’~òˆŒ ÂäAŽa±ÔÕyYéT$’�0­B"^„ˆ�D�u9R„”ÈÔ’CŽÀÏ*€‚bŸ J‘G4PÃùžç)FIöÒ¢îTɶ2Î9<‰j!N"¢âz¨�? $è%�¶“6-éG`3ƒ0ƒÏO“¡$SŠ’‚0meËU×JÖ³òxÓÊàfx5Î_ZCë>ˆò2¿TÁ²ên+O¶DùèË—>ÖB¼&PL."s@J™Ü}xµQµQµQµÑ-Îÿ¨¬FFG·C?ÐÒ6N!«íå©ÕÚ@Ã3­×œÿUh#«_ ¢ "ПÑ!®Â]Ê×7T\oHZMBwZ?A‡Ùz˜«OB¨œp]!– v‘¾/רŠ×y“×4/U¡Ú…‡:r­©ª¬‰¡�¨(Ȥ‚ùØ%Bi镵B²=,7ýzàƒÆ;" Ä:½Ã v…ŽüAóX V~‡‡+ËP®Œåw ^\=H—Í9'"œ^1ÄLèy®«¿†Æø¾ü8�RéiD‘§êM}ê“ïŠÏ òh¦aY–tÚ§”Z–µ"x{žGƒŒòU!„ð¸²Àû£@2S=÷“�è „Q†èñ„£‹À(J)P(¿â2#ç\‚È7!OpB (Q-Iµƒ)¨4«�$„P�àK�uM iG 2Ó¾@ä"“ÉÐ8µŒˆiFlÚvš».®a®ërΙiDL¥R‘HÄqmÛ¶ ƒJhš¥Y£7" :­Q¥zQk©š>%S•s €"äg¿DD‹UÚZ‘½…€½®2ÐW6}‚`®rJ–F DÉ Z‘c@("p.s^Jå¸úi�™Â1Fc±hA†Û¨Ú¨Ú¨Ú¨þOÕlªÄWÈËo‚ü-^+ȼ µS B\õÀ6÷—ùø¼•F²ÊG88@�ÅÕ}é ¬¬¾2‚À4MS ž×áhcc£ó)ßþÐNw›ö1…Þ„ÃVM¸¡ÚZé af½*Ë7_çSK=¬£P/pΗz T¸7¡×z5õQkè1Æ$°'„  Ò +S˜Éû”2=ëã4¡b+r»±€<¨·¹r ¤ÙžÑõ úW*óŸ„ÇŠÅ\‰"éB’Ï’â\ “ü–R*õ%Òl­÷ /ðz?ç)8éý#ß]ñC Å~·4Rê†Î<çÜ?EPÆùK>ÑO=*î(>4‰Ê*_8 B “¡þ~W{2)e”"T "eˆˆBxzX‚1U'áœSb !èy\&SPÙì„"d(Q¾W %ŸC¯lÒW)Õ»M€í £Ì0P€ÔPJ3™”aPBæ’Gú* �3 ”˜:?ºîIqè³$×}DH(ªçC‚s2w@nÍ:…æ‘~­w{Ž”´·’»Î¨‡s…¹íëkšºFDyt§ÔØRJåÁˆÚkJE'HéJ$“žçqϳmÚ¨Ú¨Ú¨Ú¨þ¯¥Õ‘˜õUç&PäcÍYt (ÿ Ážük}GˆÚÑù[[ÈÍ ÀX¨i}#˜ß"!„QNÚˆÈ|ŒG‘¶félŒ@râ³ÊÈaøŠ é*,€R*H¶U=˜>Þ:6 m—ó³'è{î _· ìLýõe)y¢AþéÏK݇äÖu]…+tçÅ€ 4PÕ†LýŠä1R]¢Ÿ‹© äº3ä×Î$¾*‹‚DžŒŽ*1_¶‚àZ*5õ/É*˜üÇ<iöy[;õVtþÕWù‚G[Îe¨„Z--B¨O¤Ö 5uC~¸  N–ÏÊO.'ˆùg#20(ž\ ¯¡n\9”øVÂ`ù°ô)° S¦Ó�yd†A(PbÛ6 ¼! @™ròŠ@(•c)�E!ÊvH@�D¦€ >�GA@z4 "rÁý´£„R€@ �äžç7BÑœ1Ê‚4‚TúDäEÇ "ri w½"Yû¶/á²J©eE(¥r–yÂED „Çj %Œ2d4™(f”yžç8Žçy†ayžFTv ]�(ÐâŒrøT:—|ã?æ~ ‹¢ÿµ\åBk5äJl „äV˜ÃU¨9ÄŽkúä õ3Èd„4GG î ¦Ê@yú©ÔPB)Ëú¹®Ç=(+àO×FmÔFmÔFmÔFmô„òR}¢˜hМ�� �IDATD¢Šùß!i-ú@¯/´U í!Óê×!¾ "a]¿´Zd1(+5P"%!¤”Jßj‰d¾.È�aÄ´¨1̈@’q¼¦T&•¶SétÆñlÏõ¥…B$ yкž›@ÁÔ|X%¡œü&_ƒ {"\çaýB•ëÏc!æÚy+¥†‘GêL>Õ®,Å“_é€3¥{y,³, ‚@ ÈÝå‡PGAüI©A©€jTç¦û¸‹0 Bd‘³J‹ ì«…'_~ áäô0äy²d¸ûõ†ô¤úªÃÕM)<¡YôžÛKj~QÌž(¡šP öFHrÊ*PèB‘/îyz=!„@sî Wþ‹À9pý_ H¶¬ô©±,KzßÈlêeå�„R+�„`ŽoŽÏ:Ë:†B|ã¹ úûJ2̈ÌRà îrÏõl!8¢p]W9çÜó8ç «òO dŠK.ÝÂ4™NHE‘#fº¨¤| Ñ¥‘Rj4fEcÂã™f[*€a �Äb1ƘëÙ†I A®’,žM¾›ƒ®Q"š:CŸã!Mœ’ÏV×JM³é'7$„ÐÐK|S]€[’È]ý•Ð4J) ¨àBA‚Ó¥ðèSOø¹#RjÈjò~Ydý”Ë#%ˆã¶¥0üßO¼aù†5›�°¹qÅ¢úÌ6¶ÿsäýøÊm÷þkÙ†…_,Øž…^ãú -q& ®.0sÿ‘×´aC½>;š¿}zêßgÕþëÛ|rë×Õ4ýOò#~þöÛå¿¡ ´·¬¯Mÿ޵Qý×Ðøì°k‚eü7¯ÿ³ô;ýjhÐ }·b¹ƒå®mBAP�‚ ¯aH˜‰¿�GTž™ùÀ„È›:ª„`ó­B÷õbÁí#äùÍê›`ÈÃTú&2Dò+Ox9ˆÁ(cò_‡{€„a ¥†%"‰d¢¤}E§îí+««ºt&‹ê›š;wëÞ¾²c¢] RÆj$ÛÍØn*m{#àqt=P*7ïò0B <áqî áqîqîrî Áäa]ži�èyn$b¥R)J) B™™JÛ@˜‰¹'À¢F4Õ²˜ÁQØv¬HÄžaˆÂó\Ïs…àš€ã8¾W31,3ê:œ{H‰‘J¥¢Ñ(cÔžã9žð p!€ D zœ;®ë¸®|ÍæTJ !®çÙŽƒ�1ÉH2)gEj› /J)aŒf1³DÙfÒ«CjSGO�G"!(@†u ïÐÎ#œs@¨A Óµ*ºP…�³jT‰“¼6 Câ.%yŒŸJ™‚ˆžàG!]Ç͈…Òv†Œ0 ”pÌ`ιk;#TF³B<À‚Cøe£‘HDj2›sÁ˜Á˜LðÏ¡B ¥4¬(/c*hB²$ %P�F©iP3«¤ A~A9K¥ò�zwlÏv¸ÃE¤ˆDxè àz=W¸žp=ô8ðŒ›!‰Ä#Ìbˆòp>£ŠþÛyÈî8žãxÜá˜A ÇñlÛ%„QÃôRÃÊÄCp¸p]Î9§„Q ˆèyž+\‡;w\Á=¶Ç3®ãq.„ǹëpÛãŽA‰çÚž“! LÊ�È(¥(�9Ü2YÌ2ù9‚�ƒ1F(Î]Oxœ”É@ A ©<’ˆì-iª'„BU�!€ ‚ï®ÃžàÜuṉÅL8�œR¤E‰d&ÝÌ… lÛN$�‹Å8ˆ¾‡¥¾ PÊÔÒêyéå{A(ˆ.U™ èÿ¡ZÒÝ(™šÏ9i%¤)Ð…ôUçÿ ô¤RÉwÙ.ГO¢Ç)"“­ Á��zžºœçD±LÁ¹Î ƒšŒ®íQ ¥^‘b:•²3vÄŠø ÈÞó™†@‰Æ#‰¢íøýl£ÿhòjÞ˜òêkß �hš>ã†ë~Üüÿk?gÏúÛÅw—^yûacoší€÷Ý»uqë|Þ·×îÑ÷œwR­tK ¶‹<Ûö~-ӉϿy¯>§¿¦©5êÞ½ýÂ'—íZ6µJèfìßW â~|á¡—Ít~×Js·¼tö¨ÓŸ[õ«ù¶_9©û¨Û¾wO¦Ú¨þ;è?|v¸snõ—ñß¼´B¸ù¥ã»t;åõºß ‹Üò»p"œr7 +"EJ¸fLó½à’Z²&µÆs!ól¨Hëµµ^¿NùÆáü! $²BDDf>Øãýäg„ `†•² 5›V¬Z»~Cë `ÆÖ†FÇs…�`Ô0#.Fx3K”––VVVvìØ±¤¤Ä²,ι„—�àyžmÛÒÈI)eLÚœ¥y)ÍÆÍ¦R) ùÒét‡¢‘¸<> »®+3œÉ‹ªª*Û¶Ñ0LÓ4¥¿´ëºÍÍ͈hYV4¥”¦Óét:M)..–Ïc»Q¶‹Å2™ŒëºÈeh=1 Ã0™´KOiÓ4£ñX<™Ô¥KƘmÛÑh´¬¬,™LÆb±:H0ã8Ž4#KmQA«¦>îùX2³‚¡"ÒƒÌQBéKh„š£»Ê. ÚÉ…JÈ• ¶â£ê Ùá•G‰œ#Bå ,0°+!Ì6 Y¯ ·˜oûÌZÝeTˆºªòfz!Þƒ¨J©bUNgýÝÛê½P)òˆ@D”çB o$(�A(´œœs©ó q¥8ÏT×s]—Ô_Yvú>ùúQ#9çw(5‡¿–nŒƒJÄbcÌ”n òD@.(¥L®z\!(1…:A"p!ç.çœ W×ø¨žlI†UßÊg|O Îb0f0`Z™/À÷¾q]×ó]´D6¦)œ¢sÏã(È€ºVEà#¦ƒ|¨OÏ@qRê©[¢‚ó]·üç>Yxñ'š‡”î`%9‰ÇãÑhÔqœ†¦FÛv 52¶M3MSÖ˜ˆÅ#+J…&{VMf°d2.3Î/ÏMà­ýàÖöìU‹¶ë¼ËÑ·Ì ì«îÊW/Ú¿wi,Q9ôè;>Û¢½Yó‚GŽìV6é5Õ–»ò+T$«w:òÆó Ï�bã‡×6°<oßÿ «ß[—û�ÿùÙ‰]#ÝÏÿ$‡•^øä{v+ŽuÞý”¿߬î>}Þè>e±h»®{œxÿWõ~`/NXrÀ?jäwuOËר¸§ëó ñÓ/Ö~·©‹”Õ¹yþgêY‹•öuö“ B¨¸p×5Ïyø´Ñ*‘hi÷Ý'ÝõùÖÿT«Í¶(ýéó¯;OÜ7¦î°.{xúñ#«i,ÔjÁÑÉ_Õlêåc+v¹áûßWOb·¼7mzù‘v5MebÍCc+Æ>ô?²ÏþϦmïe·17ÅÆnž°su2¯xàe¯­ OùßXÜyor æxdßûW‹ÿ¢ùØFÿá´ÝHï¿—HÑàC'O>d`âWª_ÿ-D#ñâTÚ%Ì„À¾ÈR‚F‚¨þäu?tñ)„¶‡Ôr{ö¬js,ÿU†\Bˆçy™L&egRét:N¥RõMˆašuõõu[¶644¤R©¦t*•I§2é´±]ǶÓÍÍõõõMMM©TÊqd³3MS:`C°§§”RjH;�%„Étýr+ •Œ®ë9޳víÚT*E3Í�å-+‹%lÛ^·n£¦Ü@†)›K&“ÉdRTÆX$1 ÃqœúúzÆX,óCt§¹¹Ùó<î¸2ã:r!<ä®çº®ë9Ñh4Z’ Ód†e–e˜¦‰ZÆ>…ÀÕIºš •¡l Ûä?¦_¬ç0UlÈB�8&é"!oêõVB¥ p W Õ¨z^v‚:ÞBó’ ÔÝo…±Ó2àEâ@U9 |ÈQWèÚº‚ò¬^?Ÿíü®Ðõ¡¯PöÊaA ¨¹tçBxÜOI€‚ê� @ü<ü¨©c”ˆ)‘)?Ìh$‹É� ìöÊÛBÝ'"›:A‘Ÿ Q®Á¾RDJA€"%À¨¼ Ä`„RB©œ{ †ŒQj0j0ÿŽÌ A†� ¾J’¢Ž¹Z %Ÿèk \9Çý Ü@!ιëÚ®ëèÊßÔJ´t34tiÉ/«V§‚R-4 ‰\A ­º¡©ú6t‡h‰ZòG\Þ4L³fSí–­õ†I–E‹%É¢"Çõ2ŽmX&aF*cÛŽK) üYéû1€è¥ÓͶkSƒÈ ©_DXÿýôï;œú÷OæÿöU½¾ùˉ×~l�ÿñÞãO~µüŠé ç>;¾ö– S^ÝŒ�X?ï¹+Çí:úŠ›²þÛbÙ'Ÿøz§ë?[·yéËÇ6Þ;éâ7B\¬{úÌcòN}eÞü·Îµž<îÔüœíÔW7L¸l–kˆÌ×O8ïó¡wÎ^üå߆ÿpñ„«g6€7禉üj؃sk6Í{tߟ®:îºÏrL°©¥oÞtÜîÃÏzeuVgRrì´ [%mYüØøê.‡7:ÇñÂ]óÑݧïµË¤ü¨ùb:_\ôù_írß·jæ=6záÅG]û¹®†i¡ëâ]w=úªi_,Y¾à½kú~y唇ÿïŒiþxÚ›pèÄQñì-ÒaÔy7œ³gYë{À ¡ë¸ÿÃn¬}{ÚŒê#ÞñW¦òŽíµA‚´¹‰›¦;ñ!8罟7¯þàÂâi'ŸýäZñûàMîÈ¿.iN§ÓétÃûgw¡ÿ=ó±Úè?Ÿ¬þÿòç#ûü*õëïJ­lí(‚' 3ò$„0 �ÙóÕ BnI¡¨¾û,øp+·‡Ý–Jåo— ²¡[@‡g®ë:n&•j²´ü*“Éd2‰ÝÜ(M¾œót:ÝÔÔ”N§eþ6Ó4ÇAD­mY–ˆˆ ‚£�é’È�e¬¹¹Ùvœ’’fÑh´cu•¢©©É4Íh4ÚÐÐày^yyyYY™±½TsFz+477gÒv:ŽF£„é\À‹ÇãÉd2‘Hd2™À\é)èHcósH’æYBˆÄüMMMR ÒÜÜ,µ'ÅÅÅÑhTa†išA–µ,ô•½-ï·4ÄÛI­K…BeJDà‡¯HC!I&¹Vn½ ]„ ²¤z 4Ð(û{8¨&(!$@­fAÚ™!Ð2Èçõ¼’ùMç‹wHÔõz=º¸àLÔK…úAo:ǃÂsƒì ¹§f*RN"AŠF£ñx\é­ˆ–Ü^7§(…'8hþó"pñ\ù±ÅL(êZ\ŧühY–ÁЋ‡TE¡‹|-¥TÏü"ߎs. õ„0åô!%!´¥¿fhÄC¡¥O—ö‚…æEHôÇT?‹ÔåKä *héTë*aGè}%Ec±h"nÅcÇÙRW×,£ <GP`Q“˜VƶS™ŒiYñ¢5 ’Û™~?{œQæf7c;™ üB"íºõÙ»N=¸W¿‘“ÏW½iÉÒ:oþsO}·ã·œ²k>û]~íqæ¿U‹�|íòæ‘·ôìÉUY-º·dޢ؞ãéS’ì¸ûqã5-_¾9gó.Ö¾úÄ•§ÝxÁ^½z?çÆ3ºúø‹ËýÅ´iÖµg¾¸ã}·\œÏš3ûŸO¯sÅGî6pÜõWTûì§šÍ[Q½ßQût-*êºï1c»Õ,[‘cå'5Ëjú_ñî[ Ð@ oWRRRRRB¿¾ë†Y»O½kB5¾ôoèØí„—kHݲÕå§¿ôÑÍ#²Š¾ræÇ«wî=Úuuñùûo|é…¯]¬yþ¸.U=¼\î: å»°ßЕ¥‰Ò¾W¯²V,î—Ÿ^¾ßC'ïzÿäƒ^zü½ºV� Ö­{ûŠçÏqÿI£žšzëëlÀºEwŒxìÉo�€³ú±1<8Ó�ðÖ=}ðÃ÷Mÿmþë3¦½m6qdL»'VÞ³wѨ{}3ºûõmû÷*FÛuqêCß*‡þlAwù‹çî]5£¥ÝºoQæóKú$ö{P";ÜôÄ!Å®øÚKÍ{ôÔá]ÛE¬D‡>Ç?#M¼…Sw5 !‘ýX+�Ò‹ž=ìŠã%=ö>çÙm�ÀM¯_0r@粸eÆÊ~é ŽÛ¹k»X¼C¿ƒ®ýp“dÅ[ûÞ_Ò1ItÜqòó«³}‹ßœ6³ÛQG1 Ôzã›'W–M|±��ø’Û÷HŽüër^÷ùvªNZV²rð”w›��Àùô‚Œ›ð’ €õß<4yŸþ•Eñý¸ü­5�ø²'OÞ½oUIÌ´U»Lºúú³ÇîP]KtzôÝ_5Jí%/\ø‡~b‘â.{\ò¯|;7nù×UcwìV³"Å=Ïy/]°!±áýkX·"í: ¿î �°ñ»œ¹O¯²X´¤Ç^“š“k•wg_Ô;1æáuòfêÍ“*:Ÿù~ÜŸß¼êð]º•Æ‹:;ñÁo%‹Ø8÷§ïÕ£$-í¹ÏÔ/·áR½¹ÉWÎ[ÞiÜÑCÚ'Ú9öÈabÅÒÏ¢_S<;°ak–UVÆ£Ñh41À/œmÔF…¨yÚ‘Eƒ®šã�ˆU÷Ž*Úûž•¢¥Ùá}ï;÷¬HFÌhI÷Ý:}ÝïëµmÊ|xñÐêÒx$ZÚsïs§-meÖbí¬;'íÞµ8ëÐoÌùÏ.Nn~ê°ânSfº��ö‡gw®8éÍ4�€;ëÂ^íy©A/ž·z;ïŸQU}æt 3óÚÑ;t‘ÒÞc¦\ÅÄá=Ëâ±’û\ðê*�¸éÍ ÷Щ$fY‰Êc§<5ï÷L—PaI¢Ž+˜A¡ÜP à(8ΈŸoŒ P êOšþBÁ)âAw¶�E îD[ßçWz½Ö·­¡ú•²@G/¦iÆãñX$B<îAæ&Ô<Ã-ƒ2’==Î4MÆcÄ7º†B:Þ;Žãº.jàD噽%п�B 3¢± „ ۭݲÕøýók7o­Ù´¹vÓ–tÊB¤ÓéúúÆT*#!±h¢]»ÒH$ÆL‹&f$eÌp§±±Qê/ôÜ~Ê{¶mÇçžçÿq.@�*äùãžÇ=Oâ…hjjBDÃ0dqÙu™L&NKO Ðöô­ˆ]Aä —ÂoÊÏ‚¾²mêÀ¦%ô¢*¤Zæ‚–$MxJTt_}8Éë–d½,!Ç?cÄ`À¨�t¹'µB486‚ �_½p]Wê•h­C2 Å¶dÑŽbOµˆZ¬¸Þ«"7=‘ùC£W¥ ‚�‚Ä †A NR 5 Æ$è%ˆ)-¤sB2m¾ìI—;Žg;žíyŽç9Âs‘{r•‘y 8zHPô-ùÔ¯SZƒå€Pà=.\9§J DØBæ¸ õ¡rÖP$=qBÖø †l¸AV%P¨¹ì t©RÐäèR•bEwî� ·Ér²Q¨W©ò§~/!!±ÏŸùñ MðV¦[ˆBl·ô|èf>ç[ëë0+á(b‰d‡ŠŽÇ%K—óíÜïçÍ[³n0#Q”´…WWßd;~çK ”-Èÿ¸çrÁ¹ç°Ö;nƒ2?LŸ¹u§1{u ^4oyéàÁR` Úq _øÃO²®ºç’Cú$õ.6w=èì•ë¯ycIÝúï|tÙèÓÆwÏÙˆ{‹X†ö7��XŸGÿ°Ð�H;áo¼÷¶;ع‹ óçoéºÃ b�¼cï¦óVq(yð^5O\}ûŒÕõ+^ùë´æ£N;°^.6â‚{®™0¤”xE±âÉ›ŸowöŸ'v$�@‹{ì2|øêcðä;§ž²{…n^fUú-~ûù/7f<»n}77®Zë’Xç!#†ïܽXï­ë��pÃÃc‰ªÏøl·[o>¢²;Ño‡sŸ8ñ']|ùü¦Ù_5´ðœhœùç·ÞÜÔ}òÓ'ßùà°Š¯gÜyÏZ§¸Ó ¾é¥?Ô �¾bíO›íå?lá�bíúŸ¶V Úé{–èT?}Ú{‰Ã'Žˆ¶ü)ÞõÜ'?ÿqñG·ìôÝ¥G\ø®Ä¢Ù‚ö7þñ…ÒËgoh¨ýqúÝGöˆî|ðåsÞýp3@êó_µÛg¿ê_ºêÂOßó}mÆyo\;¦#�`.›Ý˜N§Þ;³M}rÕgÏì{ÃGËV̼¶ëûgMþÛؼòÛ¹E§½³tíªoµäž;æîrÇû –Îùû~?ßvÞ]s<�¾ôãù»}ì?ç,[<ãîñݳö-±îç?ë}ôÑnµ^´÷!£é'ïÎj�¬ýdÆÂ£Gw^ùèÅ×,ýÃsK¶Ö¯ùæÅ?íž���sÏ;~lN§ÓuÏŒàæ×Ï;üªŸö¾ÿËKÞ<‹=uòŸÝ€€u?}=¿Ç•_­\·â³©}gÝöІCþdñOŸÝ<ð««.úÇ à̹é¨S_/»àµyËç½uíþ•ù“�:‹÷ÊkV/˜~Õž‘B yßÜ5åžôIﯪۺâ³'Nd�nykʸ‹¿ÝñÖ~üiÖÝ#^yع¯ê¹ÍØ¿t·³��œo>ø÷;œÍ»}ÂñÏ'§¼¹øç9÷ïþýe'LýÚhüàÒ#þôåÀ>X¼lÎóì¾­TÛ˜›ÆÀ¨þøÖKž™·¥öË¿þíË!§NÊqèø5Åõù(j7Öò%¯Üxý_ŸxoáVM/´Ýó±Úhû©…Ù!6ÎÿbÍ.·|³tå¢ï=°ö¾ñGÜöÿ7eAddzžþtñº KÞ8͘vΕ¯´h#V?~Ê·®?è‘/—.þ×5=?:kÜ3S¥{ZûÅçË8€·øÓÙ·~ýÅB€¯˜={Ó.£÷Ô¼ðòÖO}óÖ~ÿU槯Y³ðʼnMÝô~§«^Ÿ»tÞKÇÙ{Ãi�l^þÍ·‰“^[¸bÙ·/ý©zÆ9žóúï˜R¶5AYy‡úúz¤ I-‚Hº°7Ah“§oŽó·€-}Üž‡[!̵s¶²yÕ_AÙ™ÕîŸ1–H$ÊÊÊŠ‹‹‰„aТx¬¤¤Xç=×B�Ò??*`6|!D A`j[/ïX–EdÈ´Á(•gÏQy"\Sª9•J•¶/+//·"±=çÜsO:é¤}öÙgòäÉcÇŽ-++K&‹ ÃØºukCCæM›š›Ó555«V­ª­­mhhhjjjJ¥9òX5D”qÑh4‰˜¦Iƒ@nÂÑ0|/ ±nß¶,K&Õ“¥¤-®®®®¦¦¦¡¡A:477Kw†t:-ÏA„ÀgXAè‚—c”Ö®óVP!ÿÉ‚PåHéàD©0”¹2k²Îk%tGç$”2@U…ˆH³T±§+50È‘á'Dêz¤ðH=‹?$èPõFAöY%E.Këàƒy@T“;õd)8Æ#" ,tú0½iÔ‚PÏz„�xž'gÈLR¿¦w²î[yk‹Rès_>™UUˆìqzà†È%’«yQ¤)”½4ÅjJ„6Ap¨+H6Ç„*žÕ|) £î¡Spt Þ‡F ZX· )X¹>47+ÁöTª rGJ}¥ßÔ¸zCŠ™ˆu]7“ɸ‚sĺººšÚMV$Ò¡²¢}E%�ÔÔÖÖÕ7JŸ©H4^XRJ)£3¢&3¥¿^Mà®xîôcÿQ~ý#çõg€©Æ&‘H&ýC"’E hjhIÕO:Œ»þÚáK>{Lß^>ä?ïЮk;8a†aÄv¿eqCcs´(áã’(Š;M€·ðþK^ÞᆿŒÎ‚wöÅ}"†afé‰olil†DQM´ûI·\ÔãË;NÚ«ç€ã^¯8ù¬}Úoï¾ßþüþûæí{ѹ;JÌH*ºùÕi— O´ðxÑ!·þsJò¹Ã{'+w8ìÆ›X”AÑž—?ÿÊc;d™Ö»NVÜñÌÕmY1ë¾sÎ>ìšÏ[qñ %%]ºÄãÅEý& èín]·¡p¼;®[òñW‰1—ì6´g²ÃÀþÇÿ±WóÛ 8Eƒ‡—®ýjMâÖ9k3½ËRsÖlÐ8gõÚÞÝ•mg§loë{ÓÞ/?q÷ÖT F¿ÑGŒìß­ûÎÇÜzíá©Wžù(•[&‹ãÍ+ç-ÚèÆ+úô­ŽBdÄ1GU|òÒ;›œogÌbû<<jE¶.»d‹HV è]K¬Àœ™ùä35^}Óá}Ë+‡ùI½¾{o†t ±öÕË«:ùˆÞ<Ñm§~; 8ô”ú­úa~òå¯>÷UÏsn¿tLÿÎ]îsÐo±æµi_ ˜pT_$¯õvc=Þ{éã&€æÙ3¾©:à AF´(ilZüÝŠZÒe@?â‚PÃg‘â–wwÝUûwïÐiø¹—™˜ùîl™ñœ&Ë;U–wÚqÒ©´wKzîÚ»S—'4&¹ðû….¸ßN›¶tø¥w5²w§nC»c;À¼u˜a†‘8èÑ�š¬êÙ¹¢¢K¯NÉ­…"‰â¤·vákÒVY÷~]‹n}çÑ—á¸[n9jh×®ƒÇÝpÇ)ñ× ‡Ä�� �IDAT}C߀G÷<ü€ØÇo~Üà~÷Ö¿÷=|ßèwÏ<5·‹n=aHeyßqWž=ìç÷ßÿ‰g>y楆CþrÇñ»vïÔs·Ã÷ë¿(mÍÍÄÞ—ß:®á¹‹Ücäõ«FM9f€ù›‹kó‘VxÕ}çí•Üúå'ï:x܃‹ýÜöÏÇ6j£í¦Öf‡QÖ¥g—N=v<ìšÇ®ÞmþcO}ýoÕö½wèUYRÚeäÙ'îáü´xua75±òÅÇfTŸs×U èÜm—IwÞxDÝ3ÍÈtÝoLßE3fÖ X;ó“¦Aë?™¹J`í'3 »/{<ûËþ#-´zklDJ;v,¯ì5úŒcv6#Õýu­î=zò„! ó~X#9¢‰Ž=»uêÒo¯Ó¸çDöêßßøw=C3Í©öí˳` � ŒÏÒG@ î> ®C²á_µCϨëÐ>2ŸrÕa;•º–ØLbéX,1LË4ã±XÄ2Dn”DÂl?é�ec&!L¡BH$I$2°_í°)¥ŒùIé3HGâ ©üc‰"fFO46§·64RÓ²bñAC†öíÛÒ¤î¸ã¯<òÈ<pÿý<ô÷GêÉñGuêé“Ç?j×]‡õï?°{÷že¥L#RWWWS³iÍêu«~^³fõº ëk6Õl®Ý´¥©1U_טNÛ¶í65¥lÛe̤Ô€@ e¦Á,Ã0T�‚Òƒ(+C0B¹ |ÒòØéx¾8-áò!kKeÕ ’\e %Ï a>“!̬ †$ÈN!ЮH7‹ ÿœJX(´ã AØkM¨Tå(“üZÈ›#¡ži¥··ï>" ù/jêà‚",ƒZÌ0)3¥&c&5)ÓÝéø°dÔ�G䂸oäëÈ‚@é 2>ª!ð÷O7ä‚$¹žJzòo$"Î9÷ü˜%Kˆ„sÔ³KÊä2U¼'rø'¹P¢’’<Ðô2DS£Í÷4zù²žëGg„0?"rîqîqáÊǃ>‰Šj™ Î#Å¡’%ZE]-õ Nî MÆm2£>†*ØtVÆrýY ®”ÒH$'‹űX"‘(6lØ•W^ùÌ?Ÿ~öégÎ8ã¬vÅÅ®m—–”·+¶s ´¦…çÚˆÛ5J!r—=}âþúù„—^8oP�H¼(I››š¤\aSc3$‹“-ÔíÍ¿óø¿¤/›½låÚŸgÝÒãå Ç>¸KÇ?ðõܹsçÎýæÙÉ=Š‹™¦fر¹1e%“n~ý¦ÈYWë ÕkîxñÛßÎ;wîÜÙS÷--J@sc³ÎDQ’@jÖÕ“®º÷»+×-ý×ƽãÎxqÓöí-ÒŸüóÅúOW¾½½D;޹öy›LÝ’ûÇ&Œ>ú„qR¸ëTÑhi÷§ßsù^ËŸ{¾å-¢ØôÑgwL|ìÌœ1~ÖbE /"65m…âŠjfV·+µ›¶4’ê‘ÝÛ/øyA}zÁçƒÏØuàÚUó·d~¶±ãÈnå¿Áµ7¿=íÃ_á/Úµ[¥½q}=æ4G^ÿÚ}£–^9¢kŸý§<ùC#‚5ìÄI=f>õÒjû‡w§§ö7*Åãî~íÚ~Ÿž5´óÀC¯xei^ Nl\·®¾þåIÑh4Mìzã^·eKÎ-.i©T�€µ+‚t:bãºÐ¹[ç<ݾXùÊós†L8²7(Ðzј“Œ¾õÔ;[R³Þ™Yrð¸MÚéÔG_<·øù ý»ì4qêôµyË—Ø´f]fÍö‹F£Ñhñ؇×9[·4æŒ$-nW”nNKíFQqÒNe�ÄÆõ5ñ.]s‘0ëqÚ³ßÌ;wîܯÒnˆºðÙNh¾glÏî#N½ïóÍ(jV¯å]zu÷U<F·^]qÝšT|ÔÄC#Ó_þ¨ÑýîÕ7·î?ql©X¿f}æãó{Å£Ñh4ÖãüOܺÍ[xãú ©òn]b°´¹)V=>ùÜ“¦ÿ´bÍšoÿ±×W“ÇÝôûûR¾Ëø“Ï<ÿÏw=ÿùw üäºÛ?PkæöÍÇ6j£)Ï[p»f­ìÛ§xÃêuÿ΄îÊׯ8lçîåíŠÚ¾ôS‡ó’­òukÖA·^Ýüµh÷žUéµkë考ìúÍ¿flÞ4cúªýÿ|ù¾+¦¸qëGï}Óû z¶×ÙK·±zû$Wèæ�)*.‚L*ïàÈHŸþÝEh™úMÔòÏhnªg»ÜŽA¶*$°-¢A àgÒn°×€该V€hëE¤Ù ëÓ.Cñ=Çe@ÒŽ“jn”»sË2b‘ˆÌÖ&K©ün º4 A ˦‰–“Åó<B©¡ ©Lñ.Ÿ‘7ëê¤åñâÅ3fÌ(//šÑ—_~9bZñx<UUUVUUE"‘!C† 2ÄvÎ;·+.u<·®®î·_o×®]»¢b™qùòå6lhjjò<‘Éd$‡µµµ„X,–¶3–eµ¬¨i2F(eÀ¨IÈcùf@—1–Îd”œs®¼è#‘ˆ‚F†a(‹}+£ °7ŠBˆàBÇ]‹¦@?¨Y•Cƒ‹A @5R äéÔ¥y6Š!„UT»:©â9_ùg úl©50üT�ÒÐjp=—bY":ŽO‡žû@hi…ð;V‡^ÙŽÊëŠB ½”úHr èßftPGB*Ü‹H0œkóŒø:#J)‘m¿„QÇqä��¿]éFá¿@ô÷</ã8„…-êòE<Ïóup@üÔýB¢Jt=O¾ #D R�`ä²Ïç8 C0 @‰.¨¿n ¹ ¬ý\˜†bF±¤÷ªZ%ta<«!„imø^‚RŠÆUI¯Bºó„FPoT]íC%R6„­¤™UÏ ! ð,Ó•Sºµ´ðb ¡FúM}"ë¬ê9.�A¿QÏq™Á¸ë5¥šÛ—•[qkýÚu|ðÁæšMu ‹æ/`„TUU»¶ÓÐXW\\ìy€@$�òäá!‚à"³8G×ùñèX÷ÑeG\¸rÒkï]="°ëÇìÐsëGó׋ÑÝ(8 æ.b'ô-lIä?½þÂ÷'¿4 $ºÇ9—O¼ÌŒ¯RçÓm`‰ÿ„°† €Xä½³|É÷óíþÇ0œ/Þ|wÕgÏö7. jÚ§²îõO2¨£ÿÙ<¸lÕ¼8¤”@óü¹Ë’ƒvèÊÜo^ziõwÕÕ�è¼ßtÿÎu'ŒÙ¶‹½ûÍÛï7ïs뾿âÐHoù“·O«zÉø~9°³P×åñ8š—MûË"6刻ŽhkZtçØ9-=HË“¥¸zã:ý(�¸ëê¶F¥EÀÊzíÚaþœ–òeU{ ï–üéW,5¿+Úõì߀5o=ÿq§£®Ùî ÍË–lˆUw*šô‚¤xèI}û„k>wÁ‘§S4ø‡›v<ùÜ=ï½ÿágV½ÕtØícŠ�€–øãC3ξ雇N©•;~|&càØN°Ì&+*Š:ÿøŠÇÑS"Š•ÚB€ÈÙæÏtDdU«ðƒŸVx°SÎ[ðe/??w§ãþéÇÅäµ~AϽÏ9½ûȇ}¦çôÒ /ïn€Ñù—ÿó®ûøÖŽžp^·¥/îc˜e‘vèXéuÁô·í¡«U¼59,f׌à’Uuî˜úbézÝ´Á²:ôÔA½fN2Ðè;þ†—Æ_±â­«'wÔý–ÞÒ¹š­^¶Ò^Q�ðV.û™TuÎ iˆí}„âñÓÞ|¿ã«Í‡Þ=¶ЊŽ‰®ZþæÉºÚ.ÓXܸtYÛFÚJIîœVç&nú×óUŒ»y·"Ba‡ã¯>íÁßþtýŸwêJ—â:±NC·o\²1…Õ8߯|l£6*LF"anÝ´™dWRT½³ë׬iêл¢E_äߟ^¿üÔ'©ï/9mpqÓ‡w½£¥Yuç*ü`éÏ”�Y¹l}¬ºº„ãÇUôÆË/{ †Ÿ0æM»]üò+/Egu=üÚþŒ±ì/;�„×Ïs 6ä/ÁVÔu–øÚUë ¼ãoÑno/ij£ñ82 ‚À(3$èå®HÆãʉ]톅*'Ÿ²ˆJä`F ^b`aS ÿ%|"@|3€&,ú6T7 åž,t ÁÈ5 »[�È=ªND”ImÛ¦”2ÓðHÛ)!ˆ@uÆ!A0M&„PÏœóH$Ç3™”¬\&ÿ—qË”Rƒ™sŒD" M�$±\×å9‰D¤©)%‘ mhnèÞ­§m۞ˉ¨Ë½›j„纮ëØi™Ñð±ÇC EEE‰D¢¼²c§Nâñxss=ef"Q4h‡!#÷å8ÎæÍ›KJJ¾øü«]vÙ…RºbÅŠ•+WÖÔÔd2yêá¢ÖÔÔ–””¬ZµÊóœòòÊ-[j)¥ñxÜNgdDÆX$jZ–Å(ÒÀPÞ„�"•]*ƒêl/ð/]¤ChNéª{%¼IBþpg €@!º”ß2Ãc*•Pý ‚¶íÄŒ˜ªME…È e”×J®”Á_~´,KöžŠ.q]— ç|FdŒ„ªÙ¡Ü.TCÃàÁy:ê#Z é£!‹(ž…�(ù.œsîq�8<_‘‘€„È ®÷°š­šÿ��E`@eœ ‚‚Ã`†çy¥”R£a€†ã8žç�¢ôÔ&” ÁQx‚3bøÊ�$ày4ø¹„PúõH�ˆ”PBˆÔEJ&Ïq9:2™( Â)± ¹°]˜iP`®ãàèy˜A)3 (<Á‘ËJ¥ã ¥µ,b0‚RŠ�ŒBÛv!¡.òÎ*hZZ`„j(-U“F0)Tþ (�_¢ P4H¤Ý r#ÿ•”ªyçs¾öM„ê_QJ J)E^Qäª?t …’\))R èÿbnÀ‹¿J $„ ròˆòšfûR[¼)‚kÛ.ØQ˲3©¦Tó¦µóçÏóÕ× µ:”— 1Ë,ŠÇ=Û&P ô‹“MÈw0"¦Ç9A0Ì_þ#ËçßwÑc%—~vÙÎ;“ ÌŠ˜ÆàcOÜé®{®|räM{Õ?}ÝsÎaÚ¡ðVˆVè—¸ûÅGgtþ°äê·žxo}¯#ûçD´ÓNGœ¼ÿµ—]sß÷n¼{ÍÃ+÷ºzB/éóÄñ„|ÂyÿŒîgƦýtÏÞ:ü±Fœ0©ú7ÿùÕ!—X~÷Íï”ûÆ>1`Ýô%¯<óø×{ž4üøì??qú_Ô{{ð,_>ûóšÁ“vÏj °æÝ«ÏþgÙ¥¿h÷â„ݰiõüŸ¹õÊ[¿ü—÷¦ `�M³o›üצÓ¾nLI¡®ƒµ3Ÿÿœ Ý­G2µì½›o›YuèE»´l”—J_×öL vĈDįŵ©Q•fÔ€M›WnäÕÕ}ööÍ´;¾î{å êÌÚ×î_?èÐAQ�(ßcLâ†û¾ˆî9ú¬DÔݯãÓ·Ïævßó7,pýÓ>í~ÔÔ!>Ø'bîÒ¹?Ôº[nžIQ¿nźšöÍ‹^¹æŠ·ÚŸðö~±õÏêÅš9Ÿní8´g»òÁƒ:™Óë€v:ö¢cn>ü¼¿UõÞ¾ �°—ù•Ýmh×D§¡*ðíú&4»÷ê¼î_/ÏXÚm@f£ÓuÇ}N˜<òÏîpÑþ=“öÆÕ™ê]û·ßæKÐGŸ¶ß-—œ{ùûÏÛ»“XµIšòùO/>¿pØiGt‘%¿u�6`òÅcî>õ‚oýùËM�lXüù"³ï ªD!}J%õÒ¾Gäâw^úrâÉ·lb†tÂWžwé5{Ü}æˆÎVóº Ðk§^ÛVF;ÊÐn>sj¯;OÞµ}fÍf#­ í 5”¨ýaöêvûu(é³C·Äcõõ^éÁ§¿òÄ˯ØïÑ ‡±ïþvéãéÃ9´œ¦xÖÍûnmf@×hdSNè¼ç%gF‹Žyut�v9ö¸^ûNòP÷kX*jWÖ—î6¸*ºÏ©'VpõY÷w¼ñÈɆu…ÎÕ¨ðÜ+§MùÓôozxr¯~*—½ñø»Ç^³_ù晽º¸ýNƒÊ©šPûý¶âcJV|üúÏe;öm5sž¾aZíΗŒhGÄ/šmÔF…ÈÜiän©kî¾ç¸î“úÅVoJ!�@+³›æ}ðÞ·½v.Ý4ýÚ[?î<áêaÿN™“¶agÒŽP?+Ùe<¢ÖîG:úæ+.¼iÄßNëŸúdêÕ¯µ;îÑq�ØqâQU£®úKÑ?^\fvÑ…W¹=.¼f@HÙQhýüEÄW}öö§cî‰ ½òÉ­ûÝ~p!ÍÛ.¶$äY\ˆ„R™cP"uC‘iª7 +žŒE£EœóL&ã8!ÌŒ.÷D%«°ŠD¿”æl°”aµ“ÀäWj¯©²ÛÇl ù`¾Ðýœ «„Ôµn› ÝQ7ò­µj»/a¡@€Òšˆ�H$²Üs!R—o-φú²²²D"& ¹ùÒ<Ïã(d„B<O¥R%%%ÑhtË–ºòòò7VTTÄb±eË–¡ ‰D²¢¢bñâÅñH,N#J!‹%’1‚ņaÔ××—––Z‘!¤±9U·dÉO?.I§Ó±¸Å9onnFÄÊÊÊâââˆi™Ë2"?¯^Ó¯Oßv¥%½zõé7``Iq»h<VTTdYÆÂ…‹=ÏI&“ŒÂ´ç^HÅ×®]»uëf„s·¶¶6N×××—µ/‘ÉÔ°B�¡C=Üú ¶4Ðú!£¨<‰I< p’ª%9.ÉX–E)UH,ªt[ y1ÈJ\Ϫ½äIìˆz)�p<W>,r³ß§R)f~ �±¾wƒ<ÒBu`.Àƒ\h'´€vPÉIV€)ñV¶!„ˆŒºªª–†@MpÒïÝ�—û)'ÔŒ”Èßǽ�9ðUº÷Ë&ιãfÓê§„0Ê@¦T”Ú*„ð§@y š!š»„Bje³à+Î9 BÔà'„Ä"Q%eœ€àhs›Â³ S¦DM½¨Ôšzç+O ‘›2 ¥žT¸b­6 À3EnúÏó„ðô‘Ro Ú\ }Tž2ª 4®-Úú€‚æß‘-µ-‘¹É hO4_´{JŒU÷†ÄÏWÆaá š|é ú“˜ÔÜó2¶Çª**«+;2J]á!˜I Ü .�yàš‘Iš^uÅ/"¬ý|ÖÂúO/œô­ú‘þ±úÓÊûMyúñµ§]2ºÿz«ïA—<ï-%� %‡ßõÂ’ó/9q‡6¸Å=GL|àÙËBfhZ}üÃÏ­˜|Þ¸AWŠ®ûœýÌ£“»m—:#ºÇ_^¸§vòù{ôÝZ<ä¨Û_¼iT�ºœö÷§WwÕ¸ÞÔBûþûœôä?Îì±=ÕyK.÷î«¥Y Ë¿ž5«òØî^øœgçýóN|=Úw·1ç¼òÕ9ôŒ�6¯úî³YuûÖ#z…ºî¤M?¼8õŽs­k2Êûí}ìßß¼atK¹ Ñó¨KWýý¡—λÉH¤¸ªËN1¬ýž“z|uÏ»/;ñÄá;Œí:ýÉëz`‡Q7’ºíÓG&}Ýhõ;xôÅtŽ�Ю‡ èüÄœÊ?tD÷êÓÿ–Ÿ›ë_ýl2bÍkÏÏîsô]ý-¡1ø¸ó}ùŠc¯ÙwÑÝ݃‡hi¿]<;u÷î—ºVy¿}O|òíëFÅÖ< Ć`ò£ j2FûÞ#'ß{ñ^&�@bŸóNðøs¸»�bãìûÏüÓËK¶¸‘Êû_vçY;0Ͻyò§ç5ð^¨ÜóϯO¿tÿÛßüÛš:nèi[D²ón>÷áÕí·m›£]Oyê­ôÅ—\¾¿Ÿ QÑ}çS»XbÁ /,~îádÿh�HùáSŽ¿ê¹'Û—€XõþM“®ýpe=&: =xê}“ªi´âÒ[?æ²1½¯3»zÇûÏOžøÈ[.¾ø²?ܳ²ž”ôØÿº×_9gÀ¶{Ú|ñ˯Š?]qÖˆÖ¥hQÇÞÃÏïØê‹‘Ê õZòêeÇüõ›uͤ¸ënGÞýÐ!E$vè}oÞqñù—Žê³«v=jêwYN�’8ë¼>§L9ã‰ÑïžUÅžrÖðÛ§Ô_púî��k—«^}Ú»àšã‡]°ÑŽV>íýµª8>òÆ·ž6ÿô—I»\Tc›%ÕýÆÝŠŸ -87Å7‹g:ËØàÀÀ½®{á¶ /˜2¬ru*Öe—q7¼pÓèÖjLÏßTÙ²þvþ£_-­õ’‡Ž9çÅG§ôeàý‚ùØFmT˜hç“î{pîä«ßáÊ&’(ï:äèî -Ì�¢æÃëÆ=8ë¼Çq¿rÝðVRÁþîT|ØuwO?ýºQ]Ïm„h»Šn£N}¿G[N}âÕú .š<lj­Ùmäñ½qË>I��c‡N|û­½&Œ)‚OØkÊû[N:®_hu*´~nøE¬bfÑ“gîuáòtÉÀÿôâßNìD’¿K'(-7d9~¿CÇpî5442y  !RH)R¹«Ï>­'¥áTY;å3†‘ ÓÍâ™�žé»Æ`#˜“¥ òvŸê"ØÊçõZÈ·6×5—hü±¾c-¥TÝÁ‘+À ×/‚÷Ҕ̓ó…€>}ú¤Óé7VVVº®»fͪòòrÕ?ŽãxÜ�@â @€iFÖ¯__UUU\\¼dɲ~ýúÉÓétmmm§ê.sçÎF£UUU¦i555yŽM) ô( '(55<Ïs¹HÄ“V,ι›J5Û¶mš¦ëº‘H¤¶¶Vx^ÆqѸ‡=îp/jZ‚�RÞ±²¢¢¢²²Â²"¥¥%eeeÕ+/þiذ]‹‹‹êª*«ÉØÖ­[7oÞüÌ?Ÿþâ‹Ù•ËõVò>í÷§ö°‚Ù„a¸%‰¦ ›.©zØ?@Qž }Bˆçy¦iÊÐÛ¶å`)AÕ9ñ[áBGÈR°]וŠ%`W—gÕ@ÿ½w ¶-»ÊÃÆsÎõدó¸}ïíçUëÑ-µŒ%@, ª`l … —'®�.RÁ$&Øq…TâØ ÁN%¦ÊNÙ$U Žl,†Bâ@L0/aá–°¤~©[}çž{Ï9û¹ÖšsŽ‘c­¹×ÞçÜ«·iµöPWkŸµ×žk>Æ\=¿ñøuõ˜¹òs®)Ð$ƒÕ” ôÖU D„4¤\û¬j“lpi+AØR1ESО§h ÑZ{íÐÒoasÛoIoCªÏYÉÁ "J›¡£fÄXð¦›U�$`昙ËË™ÃjµÒÒ)¨UQŒ1 5¼‡A1‚Ä-SQç™�×®Ä"Æ8uÂ;ç�¸®kQŠDDdMþ×ÈÚÆWiž•ž€42HDˆ@DBlÔ¬à½_Û•6qû½¤­‘ ª ã^Ok À1Æ®ˆ) DÙ”­€=Aÿ׿gë5+"ÉKà  éb¬Ú–Y‚�D(c Ûh’­çžo¼±{KSR`î8\ÏOÔ–™ ?¥6èþïXÈ!ŠÌ!0�X›9ç‚÷±S?‰„DÖxïEÈè4 ³¾ëQ@@þüwß·ü±?vÿÀÚ_üÅ_ÜÛÛ{çÓè>÷ìäs$ÿìWåU8óë^ñ ?òµoÿ{ßø[ÿôžüÔâd?áeyv&ñÖ/üçßñÿ§¿ñ÷þíõŒóáý?øÅÿú?ÿþù³úÏó³%6/üÄôm?´ÿ?þöß|ϧ‘Ÿò…&¯N}ÞÉN>Cùô»ù¿¾ûuÿþÿö'‘ ÷Ù’ÏÃmÈ/þÈ×üÁŸü·>øOþìVÚÐ/ýÂû^ÿ†7–ƒû‘>pŒ,ò¡~ð=ïyÏöwˆ?õ“?ù?øÚ3RëËDЈlËÞ;k}ôïýrU5Mà @–ئ.ZzH¯Ÿ+ÛG2ÐÏ3ïèîû�¯ï˜BÄ Ë›ËfŒÀ¦!@…Ÿð¤®³Æœÿ6¥Ÿ¯ *ÆèûURvz.9çDðÆEQL&f.ËòòåËUUåy®®T5 "‰ˆÍ\ Á97³,+Šâðð°,K‰1ÎçóK—.=ýôÓûûûÏ<óÌ;w†ÃÑéé —e"ˆ’g€ÓHõ&ÔÈ„übÑ4M^f†ÙZ€Ì2O†Ã!UU3›Í.]º„ˆ‹ùª侉¯\¿~óÖmüà3†Üb9kªúðð`<žüøÿøþþ~ÍÞxríÚµk×®=úØÃˆ˜ç%)Œé/lBˆMõ»àºøÜGú°!]‰½‚êi}£¬¶ú£Z�HkªÑþý’šå.ÛR Fˆ]ùÃ~¯ˆˆ„4Ð ™É°ãhÀ.©›}å`¥ù4ˆÀ"t‰$©¥tAø t­‡,ë¶¥œißɹ{îe¡ü¤ÇaL›"!"Ô%zÄ^=HaŽ‘-‹à†™ ‡÷3Š Y£‚FP¼Ì,ë$+je²i~R#‚zæ“GÇ´�@h úà½1FÍ…ñMc¬µ_‡ŠH�� �IDAT­µ)p”ÈQ¼EÓ‘ê+ ¾qyž×uq�=�¬f£óóvŸ‰UÓOÛiÔ7¡Iš&m¤Lz¡IäÐ×C¸(az/Æ´jÉFpf·;r—uÙ¦½žwöã´+;ÂÆN ûZÔÜ–Ž%‹ÆÖ+´ÿˆþÔiRÉ'/"¢ÅY‰(³ŽAB㫪Ê\†(Z›GX"Kä(‘pw&ëFZæ¹~îd'Ÿªðó?ù¿óöoÿ_Þô©æÒ~ÂòËÿÛw¼í{9{êþÀÿþ£ßñûQ•οÿïÿÃëïþϾéʽž>ø×ßóôõ¡½/ù¶ÿâ'þêÎF°“ìd'Ÿ¯‚éÀ‰ˆ�éÌdç«U–egó™sŽÈˆˆQ$@ë9Dh£‡Ñ±,VKu´2säH»÷žLw®QŽsìC ��9cÿ€ Aštƒö|Ë©µu,¾Wk"šv±Fzýö@©Ê"Kìm”NSW�”/=F/÷òòåËu]ô£õÞïí‡ÃaÓ4 ¶©GQó•Þ¯YUŽÌ£Û'ÇwV«ÕOO.)1ÃÑÇ^xîñk>p¸ÿ‘|$/‡Ãá�8 ÊlzºZ-¼¯cŒY–1¶Ès².Ž€£Ñh:›‘œçeÓ4ÃQYUÕjµºråA@sûøî`0¸ôÀåãã£étþæ7?µ¨VMS!šƒ.AäºYùÀ«º9{ùããñèøøîGžýèr¹€"Ë»ölB”¾£6 ½À&&¹?¬Ú\ëõÛåðC¿è]ªP‘לs̬qÞ{Å{MÓèý©'ë —ØºôõŠ–´ "e¬è¹ÿ[èå)¬Cñ™YÁLdѸsN¦.�@ÄvOö‡ŸÒfé.}Öo50A1nËû�kåO†ƒ­ÆÎì/Gté+�ŒÌ€" ZÊ‘„Ôæ„as3¦:´.È"xÝ&5Z²¥éë¬~Á{ÅèÐYC¤c<ŽüBc PY?tÕ¬3Æ´Ü ÖfÑ6uÓ�™"7„…I� Y2h�XûÐÄÀQHˆ…¥3*9+Y–‘€0 µú~#ñ6¾¿>ë¨ÛEÇÎÆDĘ#‹ò€êzÜÈCI뵕\>÷­I{SÇΛ¢´¹óg~‘ þËÞ#¶‰-S— ghKKK¦¶3¸GÚBêd¿‡ç{{^\f}ð!d´Æ¡!ãPín¨©tdØÇƒ1D€@€AH™HÃVä3ãÐÝÉNèMßÿ«Óïÿ\ü®ý™Ÿ?ý3Ÿn¿>â¾ü‡ïîýn°oÿÁßœÿ࿪îìd';y Jöoüèë¿ßxõ =þ}¿2û¾ÏUëéÀ–Ì�íÝ›io�DÄ 1™5ä�`U-`ó0ª¿Mp+|Q>STÂ3p8Qêémoÿ–s¬6€hÛ⼯ëfÏ?H¤€ý“h‡;Ô¡·¶2¤ÏíÕü‘‚œE”Ë™Ù9šÍfÏ=÷œsN}õ«ÕbµZ%¨i­%cbŒ" "'''"‚`4‚€™xàù|B8<<œN§7oÞüÀ>çùx<.Ër0^yï-Èó±ÅŠ—×Í*¬–uí]^>°¿¿vvV×þòå«Þû¦©Æã½/ú¢·=û쳓ÉÄûøàƒ>õÔS/½ôÒ /¼Ð>9ƒ^=,Ëò¥—^²Yêæozr6›9Kóù¼ÈÜþþ~ÓT!„¦^9g¥›´>`ØB },zJ‰È½ÁRí/__CÖÎöMÍIkGD)¼RŠx\“vQ­@UµÎEï9u¼ÚáFTB[ÿODkøµ†ƒ®ñØ‘>bвÆí‘ö[Ký‘^ CnõOCFÑÝzò7çŸ7ƒö{“¿±§ú_iÁÖÊêFP^�çœEå™cƒ(›²7"UÔ–â!„ ‚‚€¶Û¿ˆÈ",F@cK¤+EÒ’©@ Mêf $ËŠVÉ\¡Å8ÑZ+›¦LÚí�B‘˜ F£ t-h`G�€.(†š¦²Öj5 •ŸÐ:Ð×™ô¹maŒÓK"Ò²¶/´ knƤýÕÙza¦gI¯ô¦jl‡á76×ú盆׶ó=Mè*=kktC¥wþV?ï53Ÿ¼¨ñË™G–hÑBË5ކ YcÈ"ð&ɾ.Ÿ�‹˜6žà“]Çìd';ÙÉNv²“צາáÖEKÆÏû̼oš†…9ø(!ptÎfMûTöÃ!8§< D‚E€£µ¶ "ØD5[xRå^§í­#oÿ·"Û•ö¶RÇÏ?bíDí=*Q|¥­B²Å¢”5&$j½ÄoB@4ÖZ�!ŒÇ£ù|zéÒ¥<ÏŽŽ¬µyžßºuk2™h­{ï=R‹$9г™s®®ká€�¾i–‹ES×Áû7¾áqßT§'w}ä!cÌb±¨ªåÉôÄ9g�‘Ä ‰´UÓV«UY ”׿(‹Á�¢�"^¹r…È2ÃááaÓ4ùÈGÕ~×»¿ö½ï}ïp8ú#ßøM'''Ï|è÷žxó[b WzÐsxxXUÕ‹{9ËË«W:9¹c2çÿè£×îܹ}t|@ö'“zU‰ŠP/@y“Ïl”n•šßtn_|:ﳤué“GÀf±´­–´j}Mè냞€sPG)Ö’L‘ ÆO-·pŽP›R­ &�€1­¶`ˆ°‹Òo{ÒF:¯ãŸ¹Û,[€ê|ÆN ¢Ø>øX£Á­Ÿô¸…Êx³–dš¨îâº}BƒÈÌ©p-+$3c<{6ë^zä "‚@‚™…)2£n¤‹‰ÐèÈŒØòjÒAzÀk…Ó^…ƃˆ36³Î qˆÊh !6‘5„!2@«öBæ–yÅËÖ&O*rŽ1"çœsÎÅèÅ � ‡èc0™Ûzk¥õºPŸ!èø @…EHÐ$ èJt¤\ûê+íS}ÎÕ¾?é6 àèkBÙrî¡jÉ^ŒÀZ“Ï™Ÿú v¡e¡ßt±ÿ~8ßîÒú#‹ !‘¡è}䨪2,KÄ,Ä|EPpŒFãwXcM@ƒy‘Yxg%ØÉNv²“ìd'_ØÒV�@h±Ö9g,Þ==ÛŸŒÕX°\.›¦!²ÆÙ(bBHP'j•=^Ä] „†Dk IAcIDZÈz<ã ,IîåkÚ2$/±†—ßëþö HLáPª/"ÌÁZ‹ÔFDëEEƒMç%ÖDw5Ôu½Z­öööbôÇÇgEQA]×?þ¸ÞÙ:´™Ä ÙœªU圑ªªŠ¢pÎxïË2'²/¿ü±ÙlÑ4Õp8¬ªežçÌœ[W¥÷ž£7™ïYPÊb`­].«xoÏX—‡ª999βÌ9ÃAnܸqp°7æóù³Ï>{ûöí'Ÿ|òøøø½ïýwîÜ].O<ñäééÉ•¯N§§«Õêï|gžç?û³?ûè£×}ìuÓééï¼ÿý=6œì…“““ºYE ­©.(¢$ˆBØb¹�#¨)a=Ÿ¤ó7+½¥Ÿk±�ê Â%˜dܺÐfºžâí‰ÖÙõ©â�ô H³¾ñésªq¨\ j'“vÓp—¢AEQ8—‹x�`eÈoî&cB;¢MP§ Z~@°«T×è%Œ1M ë‹}'ü&Å]Úª¸i¤KŸ™Ù�B?[¤í h €´i颦²b,uÅ ™ÉQô€‰9tÓŽ þinQÁmÓJŽb‘�AZSœs÷ú3¥iU†NÓÒ¸4›½®kcLî².tòÉšlt—ÅÅ9·\.ÕvЭ>"Š#Ebô†¬É &#�pˆˆXd¹sÎ’d`%½ÛÁ ÷ÐgQ~F-‚ Z¯¼ªª–³a3ùi»(Ìþï?‹6)c¶l=Û–­û�l€"¶Ì±íÏEÈ"C[ØEm>Ú–­MY0i\úè­M=Ê̾á|þK’{™ îõº°Îùૺ$ãlK)2[,œuÔq,¢A ú”Ö<�ÝŒ €&ÞíRv²“ìd';Ùɸˆ‹`Ë%†­gÁŸ_½z•ªêt:›/WƒÁÀkÚ<Ë›ØdYÆÌÞûÉdâœ;99IÑ•]×uUUY–c‹•1˜eIô>²�ÛÔ+c†((bm†€‘™ŒñuS…ÚÔ1«ŽÜ½ƒ}ïýÙÙYQÖÚªª¬µu]»Ä7�Ðfþ[ksUU)ñ˜Â¼cÓ4MíS�¹úö¡cUdæ,3gggÃá°®Éóœ«Ue�ƒ¬ªJ!G”èCc­5–bŒ,YbQ”US"Öõ P,™à›¦i²,Ëœ%d±yFuðnœ…”´L$0²\-çñ`‚ÂÖ`ô �äÖC€LÑcLfóÈ~Pïܹspp��7oÞ|Ýëß`Mî= ÷ü‹/>ùÆ'–uõüs]­V<üºk¿.ÆxõêÕGûÏ=÷ÜÞÁÞWg‹Y9,Žn_á…Ì­[7Vu8>pùÊ7nݺU{ëÆ‡“ÃK|æw—U5žŒ«ù!¢%Z—èc`$DÑð€Â-,iìú0SÉôM ¤_!z:ªuƒÆÚöôß±èwŸ5¯aj!ýI½ú©ýyxÓš@Bðãñ8„P×µËÌÆA!K@`­YLçù DÆÀŒDÎ8©š<XG„V "ˬDf檩RǸ«Y¨£®*)FF– ¬í¨¥#0€&z�Mm�8F@T•V„ ¬S®YôÊ÷ ­Y'¡G£ö/ �8êF&D$PTŽ7ÄÈ�ÀE ²Â!bí«*VÒr×G"@¬TÎåúNh£0D}ém•4±µV®¹ Eê°Ñg‰ï(Q“4ê¡g<‘¶D‚³ÖY�Jlùì,c Jd`!cPH"I^ßúBcŽÖÚVk ™À¬ôº^•£¡j|°hmnc뺶dtuD8Š Q”¶’ÅZ™»Ì ãÂ( ±ºè‚ wÅSZ:GÍçïÚAõ*lÑ¤Ô é4º8ºÌvÔê€5¥…ˆbŒBØFŒˆ0 ˆl†ì›®¶¢z½ê62"À: ¨¥*5ƒIÛ[ƒÂ¬ Ym—Ð÷¨ z Bå½€r€ BÒEºeV —¦Æ1´–á¶Ü& FÀˆÇÖ*´“ìd';ÙÉNvò…)(bPˆq@WÇÍæy¾X,&“Éjµªªêl6+²L1*D/"!„ù|> ôàh­õÞ7M£>ÕÉd2N|ðA{x8›ÍBÖæ!¬êºqƒQÓ4]²v{8Ö³u–eÞûªª� ,Kõ ŠÈ|>/ËRø«ªÒ'Šˆ%Í@n%yÌ�@ó‡™Y±½Zö÷÷58BÓ²,kš¦išáp¨·½õ­o],7nÜ0Æ(¶‡"R/¾u‚UU„Y–!JÓ æyY–eÊÝEÔRº·X,2c‹¢0Î4MS‡�†DÀ9W…&l7M£î墰€.…=f1>Š!�Az0íÍ›7ƒÁc=&"§§§'wŽ1—/_FÀáæ­WªªŽW¯^Ù?˜8KÆ ÷õ¨,¾ôK¿øààÒ›7oݺõÀåÓ;ÇoyË“ƒÁè7~ý·{쑯ÿ#ßð¿þ›wïžQÉËÂ<¹s÷‘GÙ~ë7ý¡«—1(¸W8ª.vØL$àžÛ|31ႜù4‡²é¤ý„’ZØjªÏ¯WR$BÿÑéW)|@_ªü!ørTŠ:´ÖalåìèÌÄÞhI $i<·Ѐá΢÷ÐFMt^VcÀj4%(å{É€©BIÏRoÓp’ç:2ƒ™þ¢ –�íüä)ÆØ£�K„�BйdÓì!bìŠ5cÀ@9ÔDƒ™µÈ¼æ˜ì÷jci´2E#0r\çþ¬ïÀ^JQ»ŽÁ¢e™C$´±*ÑæiSj:ä®Út¯?qäD"­þˆ ÖZö¡‰Þ{Ž1Bk0$Z‰“54 ‚�‚&+­øA�¤²¯Ð3á&fc—C½š¬Øã&Ä^�"ÆsÉØE‹ÈE>ù£IF�AaCÀ Ðãô`jms›± î_�`Žé3mA”ŽIdsÏ^¼ÓÏG´¿ê  EûOé2{ú#� "°�!BB÷©ZØÉNv²“ìd';ym‰DFÒ_l�Ìì}c‡Ãáé驞Jµf{Y+Qs ¢ˆäy®¡( cÌr¹œN§yž—eyóæÍ¢(4¿,ËÅlÖÖ�#ÒÖ G€½²ˆÚ3ßÛÛÓc}Ó4êwZ,ZIaµZ…´øœ÷L{ôTCòK7M“РÖ<×ÛNOO1¼X,DÄ97NOO³,FO=õÔóÏ?ttdŒiš¦i�!ŠW\ä¬uHdPS²Ep0cÔ£»Z.Di‡ÖRºº®—uÕÄ@¢°ŠÉÙ²TU¥clÂÌ1F5”h# ·¬:{Òuéèëñ±Ç;::úð‡?<.]º4 nß¾}ýúu—Á×M½²ÖM&£ÃýýÓÓ;ÇÇGãñ ]¾|Ù‘|èCÏxï3çn^¿ñÄö…ç_w­ü£ßø Ï?÷¢¯ê/yÛÛ/_¾üüóÏ¿ï}ïûíßü­§žz³!`–̃—�f  à}"6?/ ¨§)Ú<ÖCZ¸>°9ÿábÍÞÂ!ôJö¦-ü|¾o &uVxk|Š÷ZÉÂD£ÝVKÖ|¶´ÖZ›©Ç1Bh11z¶Žjµˆ¥ºÜq|ˆˆAƒ$؇‘­9 G¬¦¢‡ûÓÒ·Âôg;¡²îÖD��¼÷ÖZë´ ƒm-w`½÷ q‹!"%/ôÁªr6B·­µ)È(­5ô"ê?ÁÚEÖéÚBš¨´¾Ø Ú!'[aJ'I-lõY6E…“ý(i‚ꮬ¾ÇÔŠ”çyjk-"G5]öÆÒAØ­1ªY ý©/½þ¸¶´4©eK¿p€Üà “žˆ½Ië«z»(Ø}†–’™ÉX!@î4MÃÌçKr¯úæö:n;¤‹ý½y^.B°=…$ÁsFŠ  �¨»5À8 BdA¤MƒÂNv²“ìd';ÙÉž ¸Ìé¹¢cÈ EÄÃÃÃÙl–çyQêÛŸN§ÖfÆ"hšF’Z6<„0›Í”I�š¦ÙÛÛËó|ÿèè¨^­ô`]×u!Ïs�¨ë:¯bn-é˲T냌1z%ñÒ+òošF3œ¹c§‡î|ïœÓ,ì³3s–eŠº1yžkÞA]×ãñØã½öÙgoݺcÔG—… Kboš”:.0SW2Íi ˆªªªÖ³Ù¥33¶%ðh8†V«UðAIÑšVU¥&]:ЯñOçêÔPjfé‘ôƒÅb¡tîóù\- mˆDãƒ33×!kíéé)"*ó|¶˜?ÿü W®\¹sûÖr~rxxø¡gþŵét±¬šº.Êá Ï?7 Þüä/¿ôâ­›×|ðʰ,NÏîÆ­Yw8iWo¡úþŸý?²ïû~ûHø^� /[ýIŸû°PÿLv¥ts2O`¯¢6â\FD€À¼÷D´¿¿ODÓ³¹æ¹(6V®JæcD¤�ŒZìCˆ%£€r5Da9·Ú9€È̉Z³/HêC;3ÒŽ…;ÀŸ4¡?º¾z´ƒ× ¹7ŸÕª`1Æ�PÇ-‚ âE§ö3ëŃÎX ƒïÏoìm-zšÿ~·z )íŠôîÙ`Ò¢ Ê ÿ1êjjoC!´E1Ût†®® è>Ò!8ç¶èúÚ� �5’R$c  !4Á¥Mv€þèQ˹ XƒçL…W·p~>¡W>7ãt`3÷……å<âß O­ÙÿöÜfßêØ–ôƒ ú† sÕÚ:€tO„¿q[>·äç§ú>‚�H€íÛYÄÇH„,ÅRv²“ìd';ÙÉN¾°„Œ‰!HÏ7™²ZmŒ±,ˇzHá´žkW«•1f0”e®Ÿçóùl6“^¿<Ï›¦yøá‡ŽŽNNNƒÁr¹µ•5¨¾ÎNÀ^}û8Pä¹&(†·ÖÚ̉{ß4MQeYêA¶ö>/ËÑ H°DÔã~ÁåYaŒÂ6‚b’G}T“ˉÈp8tÎM§SµJܺuk0Q–e EÐX�€(Þû,Ë‹r "šmÁ,ÖZQ £5ˆQc}y: ‡Ã,˲,Æ"bdŽ,(1Ïó<ω¨ªªår™&D:ŠNW!ƹB×Eg2™?ZÓ, ¨^.—yž[kªªï›Õr.FÃñÙÉ“¹[G7˜y<œÔ«ÕG?üá,Ë‚_®–ó»wï ‡7½ñÉ»wO^|îÙKW.¿òñŸzê©ÜòÙÙébv g§g>ÔÖd)6}›[öý}'sBwç‘"’Ù>ýo¡Ž eë‰Ø1M¶ÚßqÛòçíÉ‚RlByžaW(Nm:UU!˜Tþ@óK´MÅ Ò²õ:0‘Q˜™HÖÑ'Ä�2€ ÖoÛ—ÎL "mÍ‘DsЃå[øP:.·dÉjÛVÕ€&x#¬Éük̽ÜöA@xž¾á!ioÚæ©‡}¯2¬³Ç/Š(lD„YL›Ñ~+�"Ì�È´H,¬YÐf¸o[.¶ìýIÓ÷5PoÖŸ'ÍÑ-Ÿt�» QDÉ ºœs~~i+J´NzÑ„îÎTŒ3¨â‘Ùùë©ë&*5Þ¿ž6‹~ sX]md×¼‰i}±c|h×�ù\iƾ^õ[ÞÒ‡­NžÇð›«p±cã"¬w7wÝîµÆéž-!M�VB2€F€M^°pŒ¡9ÿÃ{É?ûÕ_ùäoÞÉgQ^3ÿêìÕN^ý²Óœ¼&åóK±?¿zû¹B¬ko­Q¶ofömðh‡ÃájµòÞ/ õÿ+¢PÞçœsÊ ð�F£ÑO<ÁÌ/½ôÒ“O>ùË¿üËŠZCÓTU¥ž7f®ëZ¹ “¬O#§ _DÔ¡§Ðˈո�f ‰z0Æ8WË9tPPuÂØÅ��š¼PU•÷þîÝ»‡‡‡×®]FMÓÌçsíºÜ­µ·nÝ2Æ+Æ^Õ ¨h„÷~4%§¥l·yІà&­—’ h˜"Öu-�JŽ † éxõtuU%ø¡h48¹0ÜÏ6ïâºëº‡yž+„Æk8ç¼÷zî²2/G�X.Eî"sf¬æÃ¢¨ª Ø…ËðîÝ»—öæg§¿ùë¿BŒ&·^yÙZûñŸ!Xƒ‹³ÓºYÑþd¯iªtô¾O6ÑK_tõ ÚBDÒå*s¯\ßy˜±)ÛÁ Øc"H²õ¸ó"™@á¥NiQäÔÕ ,Šb>_ê÷J'jÔ=‘ñ1èŽ�D0Æ%$¸e±o¥ô"B‰¹±oª$‚ˆÐ9±_‘±Ç¶Ð>ôp"® 3›vUKÕ( “À¢÷>‚`/=^ÛÔ8Ó˧H@ZA5vQ<ÐÛžiÝ®ëÕ÷•ð|ŽRWT"­3k!K.Ýœ$ókú­©‰­Íý!� çI)inSDI2Ø%ii£hS­Pzö-”ÒŽº–ˆôCÜ»¾éÕ"UŽ4i®i—A$]Ùš“-éOo»4½â—jRjã)îk¸hç\Ä)°Ñ+Ù¾¸¥I¸—s‘±ièÚºÏpQ„„… ™˜@�„|Ý¡!Ì(¿÷ï·åOÿ¡Oþæ|¶äŸý꯼ gþÕÙ«¼úe§9;yMÊç—b~õöþòK¿ð¾Ï¼qÎBdÖ:}êý²ÎYkmY–Jò‡ˆY–•e9½ÞûÓ»w‰(z�£Á i_×\¹s7nÜxùcˬµDòœÍfÓÓS´×¶"‡„"Ò 5™ ôàn­Ç�°ª+Vã‚–?�"`�²–DŒ1E1H£êŸ¤C1rŒÞZkŒÍ2b üúsÏ½àœ›L&£ÑHÙ«ªÉóœÈ;—Ïçs"ûØc¯ ÖåêM1ÖuÝ4H€ãÝ»w•�.„Ö׺\.Gƒ!ô¼Ž±ãiÏ󼪪Y"MKȲŒ¬;;;[,ZÎ-Ë!ër""@f6€†L$1ä"{HE FVRc])ëfٓȾp3—¹ !�ðªZh~užçÞ{g(úf2±Èñí[yžïMÆóù<(2×T+cÜd\N§söÁ×¾^ÎOÇã1K�–Ñ dæÙì,Ïóóx»aúWÒ*Kv®qr'I1úw&¤t!ŠX?z³É™Ùw™öAWºÞG_zƒ"C%˜Ô’ˆ(1fÖ®ššCÈ­“À÷Jß߯c‹XkPÙá#�`¢à¨±-‰¼@[!`�D»äõÍÙ»pfjU¨Ÿ‚ä·`sg&hŒ1¶%*[Øß¨âig”Y°uÂ!h:·ˆÖMh;†…‘!…pÇÑ+-èùUv×½ÔíuŸPT `c*‘Ìš¯]Ú(ЃìJ¥�€¾Ð¤K èO캰f‚LÖLèíè¤ú¡ÕŸÈÜ…Á�á�� �IDATu�3ëûTK9’�A€E�#skºê›KQ¤ÇÚ€ŒØYzýì³~líD„žn§{¶Ô µ,�"¢ç((m„ƒ 3�xŽúþYÛû £\w©oeè?1ý»o´’M‹ÆV—Ú{Xm%:Åð"ÌÚ Hµ å\;ç/ö…ÛYF´ (`„K“7M3_ÎëÕâÞ ìd';ÙÉNv²“¼Æ…C4Ž8FïC­5™sÖ9«d~ÊXUÕl6[­VeYfY‘e™LV«•fÂO&“ãl6³Ö>ÿüó!„+W®Ü¸qƒˆ4A`8ª@D‹EŒQsòèRb õÁfyVUÕ|>WÈQ–¥v›¦™ÍfÜDX­VMÓ@¿°\w¸'"ï½sN}õ«Õ �Š¢ØÛÛ[ÌæŠlcŒÓét>ŸkÆDY–Zìàðð°išñx<™L>~ý•r8ÖâŽZý±ª*çì< qòԊËåÒdæét …tgeeCLáÊÎ9 ^X­V •fyäynÚª ˆ EX „€,Ær�Râ†âC½-ÏóÂr¹TÓƒV‹Ð‰ ‘ÕHÁ«•žæ‹,çÐ:~«å‚¬‡ˆ¸Z-˜ƒ�±Äý½½ªªV«Åh4ð¾”™3Ùl1µóÕJasð~4†»¾Êf€MÀ  '…ý·êسÑv… °ÑGwJÿ‹¾ã1%`O ÇDз ¨z¨ëØZ[�(õ@a0­Võr¹t.FÓé,Qrc ­u‡{ODBh@„€£  -´¬Ôþ"ê˜aH£O“yž›@¿ÖUV¢Ýý‰ê£¸´ãÔìÅÌ$-R%"V9½™AŒ!!L6/f-Á�Š“[;B[CÑ(Ñ#öÖày3Sæ‚l˜3R'õç„Ô'±d$î,„hŒ1h‘C McŒÀëÂ{ÒEÜlõ*è J¯xïÑ(/©(ªN]?úI¨ËB@Âô•=R4þ,‚‚‚ë‘ö5M¯è ¦RˆˆhhÍT’f2ý™eYÿzRiîYLú @çœð„1zc2-¹¢›§‹bXºÕ×¥„x.và|¨NúܧZÄB×±ûÑ ÞGXÚ‚ÆÝôÁ‹âÖb 2€AChÕèEÐ×óÕôx>=ýT;³“ìd';ÙÉNvòšf®fK $¢,Ïu@ŒÁ–e™ŽÂ ¹ÓAPOÛeYVU¥Ø{ÿððp>ŸsG"¨UñªµV« ¦s­’¨[{}´eVˆc¤¢¨šº”V H¸( #, kmâœL&Šj «`§h0˲³³³Éd¢íKççôÞ&ãp‡yFDÇÇÇ¡©ƒASyAXV«¢(.]~à+Ÿþª[·nݸqãmo{›qù­ÛGEQ¼ÿýï¿{|ç[¾å[œ3?ó3?£ŒG!<üðÊd†Ãát: †‰×PÓ4ʲÔôÑh¤¼ :{EQ„ŒËŒF;¼ÏŠ<ãl6›•e©K•Ù\c Ôi<ž Ä*‘¤Žz¹\ªí@‘ƒ÷>![�Þ7Ö¶ ××+cчI3–4\ÚY"@$\­–�9+¬AŽÞûXä8”Ec�€,smð¦{0ù]·t.¡2è%¨'Yü-¬}XÂmBAy»®WŸ�?"¦Ì‘~(;Ü£ÞNl]×yž3óééiÝJ–®ª*¥·ôM²Þ4•syŒ1FO´†ÇZ’s6›Ù<G_W.wÆ`Œà,y "ÑC„uðιÈ,€ˆ¤=D$„à}c­5d@¢oSQ9²5fP–Y–ݹs'˲‡zèÖ­[YžëÀ5-H‡OÖê!"C¤4 ÆcŽ(JWGiÉb ZåQ'-±9ÖËg[Û„ +(nÄs÷­‘Èš¾0víc—Ñ`ÉYÞ¾�2¥/ѬqdbŒ,¬ï™<Ï«eí½ÜZ+tÒ’‘%½Çlάaæ&Í`Ò@ ï½Ð-ƒ2[3JˆHD¦ÕIU¤#Æ5 fŒQ„D:*A„´é>Ç.ó¿3Ô±-t’ ÷>„�Z+@š™~8CÒÛdJª{ñŸ›:Ï]»†¬rTj½’Tÿ˜ƒ-[­h„%?ÿvFƒš/ûeÓ`!"ú=v)*D„°NKéïÍ4¥ý§oÌ!´¥RcŒÐk6õÛ­™Ðh¥Mð@d� $~<öoyòõ_öŽ?xþ…°“ìd';ÙÉNvò"Ëå², cÚd[ÈÆ2ÎjÙe Ôz‡Zé Ï˲,AZ·*�ÌçóÅb1™LÆã±÷^£ ’@“u5¥{Å)ºd›çlŒµV+,ìííM§Óº®Sj13çy>V«Õ;wÞ ƒÐ4ιÕjµZ­ƒÁåË—5Lࡇªëz>Ÿ§ ó”c\×õÞÞÞp8¼té’‚íår¹\.÷ööÎÎÎ~îç~îw÷wßñŽwìííýî3Ï £¬Èñ+¾â+Žn?÷Üse™Eqûöí¢(æÓé`0FgggÓéôò¥”4AËChµv4)ê "-¡•¼÷hlžçhLÓ4ÕriŒ)ób0TU¥a"â»ùQÔŠ]hw[© ƨVîQ¶³ Ê>¶‰ä Çß®õ€cÔÊh©„ˆu¨Ñ£zDe."K¿¿\ˆð·l ý¾]èlì—»ƒ^IvÁöÌ [X"Áȭ窨ñLÝ㲡a21zcœÞã½G$¥êPÜÑ÷œQS×ÃÁ@Ç‚÷EYVÕªɹqd*öu]E™çyã½Æ,¨EI“DˆˆŒÓݤÞoõÛ{ïYžjÝ›Ïçu]+{¨’S(�fæåriBà.„Þ9gŒATóÓRoƒ´ÓEèaÚ4?09Üt÷W6}茤U�€",m%ˆîç}HLDÒELÔM“9© B-n¡‰óù|<œ "·U Ûuï—IÚ~2MêŸÒ"IQ¯¦8‚ô«sihO³„�½l `’¶¸#‘š±ÔäÊëh޹kh<lækôuõ^ÛŸÕ E6£~tòóÌqˆ>Aii)¸—£Ó.±´ëµùÒh×këqÝÄ^\ü²Ý•,iÃöÙê*Þ;â@+#l¨Üæ mëç,�¢•”Å"1 ‡ƒ£·çg0~ Ü;ÙÉNv²“ìd'¯19ØÛgˆé䝸 „�Zé�5ô]Ý¡)nßZ}TÎ? ŸÍf €5õ@‘<"*N®ëZ½â PǸ¦ñØj†hˆÐç/Pr„ÉdBÖΗK 888˜L&šn0_.ŽŽo£À`0˜ìïkúÃ˯¼âœ‡êHÊŠ¢( ìˆî˲¬W«‚Ö5P/âp8ÜÛÛ;999::*Šâð𰪪—^z©(ŠétzóæMÑA)éúb6˲l2E!¾1ÆÙ½ñH;À D”eEY–Y¦”ïÈ YQŠŽ&{ƘÁr™»Ì9w6›*äStÇÌ>†(Ü«nÐÚ ›À$&ÕG\}ÄÛÿLDÂ1ý?*'¼Y›ôà8в°´åMf!¶å4ý¡öMŒÁ|Šf‚-Aêí …‹lý_©"oá^MÏÝBS÷‚ÐY"TÛÕ ƒˆÆØ4óˆŠÛFÐIJ�™EpUUÎ8uz‡¢ãñÄæn±X4�04(FD¦®kªk¿¬«ƒƒƒñ <==Mîî.Îz›éÔ µy¥@} %Ð- a8YžëÆÔx„ΆÒÍÚOD"€!9Ç“�QkÈz“+ø¼á�:fG8‡c[Õ%D"d‰1Z2®Ë©A�BÔ #Ð9çC€ÈÊ7èŒEDƒ–ˆBÝ€ˆi“ 3CdaAgû&¡¤ M:|è<ϪTQÖÔªÉLÐÚæzm¤Ê¬�­í NÖ9ôšÉ�Êô �m †@‘5e7c©‘ا<¿/.”ûl­ß¦;sŒ¡ MBBC(… RWl åBˆ$nˆ }ánÅ^LPºØŒH36µÈ†ùà~CÖÿ32¦¦úOïÚéªúˆÑÈ¡²˜7ÖŒîÞ©~âÇß÷=ßûå÷ŸÆìd';ÙÉNv²“ת !×å¶÷½nÌl@¬¹WuO/†ŠÌª“8‡C™Ïçgggb zým–eÃáPQVë©ëމzªÖ³ 2hã2 "UUUU¥Ä "{{{Ê ¨ôÉdR ÊétÊ!.—K é?<<F�eÙb±Ðþ+q Dij¿¿?ƒ‚óº®W«Õµk×4u¢išº®™ù…^8881§§§‡ûW¯^]ÌfÌA˜÷÷÷÷÷÷¯_¿nˆ&ãñÑÑÑ`0H¨ó« óîé‰|µîCUU1FCY–:?JX cGDg¬ö„»„s…‹j^I¨þüéÎðôAz§ùˆÊi¾™[”‹ˆ ŒB&uÎ’mË.�¶ÕÑ>!,¹Ÿ ~"–þÒHÓû¹âýëÐ=|µ•ݰ53¢ÖôÃÔ½„ -CÐÏ Ò„Ôkš<Ïú«#åy1 nß¾íœ;888>>Õ²ªëñx¼·7a檪PI)4£Á{||ìGú®'“ tÕ4:ü@¹-`UUgggu]ÇãT.Ôt¥@™9òÌóÜ:§žùþ´�€fžoÍFTB».¼6Uek&ᅳ[_%Ì&¨SZ‡~ÝÁ´Ä1FèòÜùt6 Ü–œTåêâ L›ðÒ.¥1†{V°­UîëFÒ"†–$%©YŸ2°¯iÐYX™¹-s€�‘™á~Úä‰è}µÁ"�H¢á?ÉàÒWéûÀæÎ*ñI->QËІ®Î+Bb5©DRuè"5¶¦¢¿¦ÐÓ–´}%¹Ï{/ QÁ.úãÂûï5LD„Ψ!"¨å'×ÍJˆÀ…DüjQdyU…—>öñ{õm';ÙÉNv²“ìä5/gggéÔ¬)�ÔRt‹Õ“±b…Ízˆ‘«ª’˜¡@ˆ<õg­]ŒË-@SÕ‰} Ì‹AQžy¿…fõÌ—çy]×ËåR“TN§Z†Ðzöd¾\BGç�Ê_p6› Â`0888ˆžC‹ÅâΓáp¸¿¿�ËåòÊ•5¢Á{Ÿçn0µG^–ÃÙéìôdªñäyž—Åp>[ŽFD<;;C0ÎæEQ¼ím_Bc3WWËýP7·n¼‚ˆÓétP”€2ö÷&7®¿²˜NyþÐCÍæKDÔ4 =éŽdÍx<VäSå"ˆX5M3_E‘™Yx¥áeY®|Ó4f€kµ÷¡iê²ÈDÖnÛ~2E7½íºv8 ‡d]¦¾½eË׊@ÒóCˆèÈQA½{MŒ‚zV‘Ì…õÉᾈñbò=ü“÷ú: ÷€¬÷êO¼ç;“Dƒ\T'S ΢Èc�Ì‚Ì "y– ‡hŒAƒL� ©³³ªý˯<g­=88¸yt „û—"H³ªNN§1Æ:´©"’åÙžxSY–/½ôÒ|>€Æ˜årId ¨}¤ƒLD´œ/ƒfè´hÜjˆݰ‹,˜N§jÞÒqé¶êH(5ˆ…»¥î!ÒLªéª ¤‡O–….M~2Á„Ž:Q¤åô×÷Oæ˜ÖâÃ>(‰€–‚èÅç¯WVÉ$r„@3.ÕŠz’u ïmÆ.›@Dд úRRüŸ”a«£s=ÎHlÓûQR �€2(¹!@Œ‰ú íìi¾ORiÝæi^·`ö–>Cïíz¯É¿ð†Àlæt� ah<"rï—íüP ¥Áþ6<o?êY ¶u£g ¡þEA4j;⎅STûç¦Å 6 =­i@_P¸eÃBÖvZ^� ¶,›MþÈ럼×îd';ÙÉNv²“¼æÅ#°›,CKIn@1¼f¨» ºrb‚¨£;Ñ®V+uÙ©“�š¦©ªJz•ÚõÙç›&ð§°d5LÑh4RD�˜N§Z¹À8‘zìÑÄ‚BƒîÞ½«åy®IСšÇØ¤sK—ä?ŸÏÕfaŒùøÇ?ÎÌW¯^­CÍ ÃA¡)ãñØ9�‹ùl86U}ûöíÂe“ɤ®kç°¸"+ŠBÁ‰28ªÝDD4Þ›¬5Æh‰ÇÑhcŒOÉÒÔUq Ê3§TˆÎ9çL ž§5WÂýY…O䵋ÊýÞû^DÄ8›>£�0Kˆ:QhÕ•-˜ÖXç¤ Ÿ’æÝË%x/œyáý}ÀÓ‡ýعü‚ŠØp{n=7ý©È�TÃÓuk-¢„ÐËWÒò±!n#$‘ÛwO~ôÚ³ùŸ|r<ÿÚ¯ýšˆ¼rãè«¿ú]Ï<óÌéÑæùd�øæ£ÙlöE_ôE‡‡‡ÃáðúõëÞû<ω¬Å�€5¤ß{ßÔõ€¨(ŠårY×µî#UEéB¯â*±Eà©’bòÀ·ïf^ãU¸‡Kû¦™žíï£i®]Z/ìÈJD•Z/·^7€Ù ÅW«Uã½nçœ%ÃÌ(¤"B€Î9m¦€ZL’i ßy¢vï'î½ÇÐF¨¿’D¨áOzDzØU‹4¦Õ1cL”uòB5ÄNó �r.Ð@ûÕmaFDµY¨)dÛFvá|ž»nßÖ}Û+\7duˆh DLñ (ëJ/ó¿ma³þÂùŽÁ=B Ú•Íý›^k}»pD½ñrW22Ý&ЕBè7ˆ€"­ù¡5@� Hûß-‰!™óÛÉç™Äéó·§{W½„²˜½ø?ô–½âÓ|ûL$|ø'ÿû_~ë·?vûÎ¥/ÿW7RôÂìÆyðêäžéÿøøõ}Êì?Ÿ# ó›Çñòƒ{iƒ,Þÿwä·ÿîïüê~æö¼ø³ë'æ¡+£ÏYxú±þl|òKÞðé>Bê»7gåC”ŸÝ~íd'¯y•ïŽúèC¿sûÒ—ÿ«ô¿>·r/û§!ãÑØÇFsoAÏl�dhMå­‘ÉŠ�@ õ¥s›ˆ¨9@s¶W«•ÖSL¢gn}R"cÓ ­)÷¦*`ÇX¦gýt:Wª6}âh4"¢Åj¹¬Vرèé‡Õj¥±Ù?üðáá¡VX¨ëZƒ´cZ\@«'¤àí”%Á̃Á ÆxrrrçÎcÌc=võêUíÆb>en]¿qrr·®«>ó/>ú‘OOÏfgÓ“;·_yùc‹ùto2*Šâìì¬,KKFÙ5³ÀS–å`0ȲL}Œ:™ÚÃÓÓÓ£££»wïÎçsåŸÓÒ†jPP©ªJA ÆŠ't—f@gi H(ìDÅ.é-¸§V`î㈘AYÐs¬|Ãê— �4äS>Á¤~¦“}ß9¹Õÿ>Bs×7I¸n·“àèÖ£ïÕÏTpA­W©Â%u¹:êWA½¿Ã6m=fétþÈc×ÞôÄ›—«ºññ‹¿äïþš¯ûÒ/ûŠå‘k¯ûÒ§¿ú=_ÿ‡¿ò«¾úKßñeïüʧßý5ïùš¯ûZc³¦„–ÐVUu÷ì4‹DÅ”ªú„†Ã¡Ú¡4H³iTôbÓ4Z^t2™dY6ƒ¢­Ö:êP<Pa>WPN=vû²™”Ñÿ°Ï/\rã«I.Å50³sN³ô¥¡Q6Ƙ¬È3ë ¶vIÐb-U5›ÍÎÎÎÎÎÎÒëED±MÒ!" 6˜¢jêÚ7:-µošàõ½Tû¦öíû �štY›<Gýì961´LŠÁ71h‡5(©ÓZ-¥%nØÒm"ÓEfHÊdZîÝŒ�Qý<£ó*©û3¾œ¿–öÅË1‚è?A¸ž"´ðZ€Ѭ·ØÖ[(µ|¯¸eÈè›ðú÷Üë'ýQô[æNδ1}e� ‰ˆ�0€D↓画pŽ‹q'ŸwŽ~æÏþÔO€`þÿï¿ü—>|ç“ vúHýOÿæò×gõâ_ûæ¯ÿ¯ÿ?�áwþÊ—=øU?üL„ðþÿòOþï[Þï‡þàÓÕÆPןšÿI|æ‡ÞõÄwýôt=“§ÿç_ûs?öQ»÷éf_ÕŸãl'þÿùso{û_øåæ³Úè†ÈÝ÷~Ï»¿ëÇ_ú´û]ÿä¿ûø»ÿêüg³S;ÙÉkC^å»Ãÿö·¯ñÏø=p‘;ïý“½îOÿ£ÓOÿ¿Yr÷³Ò“¹{¯)̬~f&õC*5Àp8Çê“‘(mįª¦!ØÌ…Ðsެñ1TMícHüúrΛ'ïwµZ)¢–Î¥V…‚öÓÓÓ»wï.‹t¾%HµÜˆHÓûµ…ýý}årå|>¿qãÆ;w”Ñ@[`P×ê˜ÕˆH‰|ðÁGy$„p||Ü4M9È/ì=òàCZý!ϲ£[·œs{{{MÓ\¿~=Ƹ··7[ÌOÎN ‚œžÝÕ*Ü•$X.—³ÙìôôT¹ƒÁx<V6ûªªH`—.]:88`æù|®¿mšf±XÌf3%ÌóÜ9gÔ°bŒFlO$[îÊ ´¨ç Ngl�Ôä‚Ú|lç\Y–å`4Œ�q›�Ñáï5dúBXr¡œwJ‡á·,çáÍ…Gÿ> ‘¾É MNòöŸ· তÛR·ÄaŒa–år©Öí‰zô2>P�ˆÕKøÔSOì_z×»ÞõôÓO¿ýíoýë__×õý#<òÔ›ßòú׿^ù'“É¥ƒÃ"Ë‹¢È3;ªz¹X,t/ˆH]×"’ç¹æ¨Ö€&G�€FôGð³ÚžšÐåäY[JÃû~ÉÀþ$«qJ¡µ0c7c ‰Á¹H ]±Ö@]¾"ýƒ:ÕM» o' QMVöhK¢¨sXs.l–ƒÑpoo¯ˆhY­N§gZ$­É%Yƒ(QØÇ¦iïkCà¶ÙÀ1plmv1B ¡Y5uêcŒ¾‰aÕ¬DD$61Ô¡!„Ðá±òM|ਤúzLzÕ©¢N,u(:é0õGú–¯¾’ÃE¸ú^í^²µûR-\Ý;ªEÔQ¤‡RgÚ*¥òüVºp»¥>`a¤?"QÚªœªTúUÜø7s– °ý¶Ñ?¹28÷ì&YížÌ„9ÆÕªfcm ù$¼ò‹?üï<ýÆÃ²Ø{ôßößþòqûHÿâO}ÿ{ÞtP¯¾ýÛþ»_½ÛëÈâƒ?ú­¯;ü?]wü‹?ó¿ñ\䣇¿ä[ÿÊ/Ý:ß ¾õKé›ßzy0¸ô–?òƒ?}ó†ø±ÿã;®å߯œ?a­>ôcßýôë&åøÑ¯øÓÿÓéêßýÞ¯{â°,ö®½óOýß8»à…ݼð¾ó­ûø>ÒïNÿÎ7—K[þ›÷ìÜâ­üç¿üÒ—ý7¿— Î,žù_¿çÝo8(˃7¾û{~ìƒ[¨øâ©[üößþ÷¿î©«Ã¼8xü+þÄÿðk'¿_(ÿ3•Õÿû÷ÿQóG¿ãk×Þ1óØ»þÔwýɯ~˜�îý_Éôï}Z]þÃ?~åùŸ];ÁVwåîÏÿ½|ù[¿ý_sŸNcüñ¿õõW¾þo}NÎÙ¯nùÄéyŸ`oò­òCßþ¥ŠÁ•·~Ã_øé··ügøóæç¿óŠéöxþµãe~ íǼÊå“K^}M Ž¿è›¾ó;¿ñ­Ãß÷8E ‘Ù7­TU½\.§Ó)Y›PÓ„Á`T"[–Ã,+q0”Ãaàèc k€ÐfÙ`44ι¬ÈËa9—†&0ͳ²( ˆ> %,Ù’á Rf¤ÜeíEc˜Y’&ï€A„$Ô¬šéél>],ç«ééììdzrrg:=¾®W‹ééÝÕbfPŠÌfÎ\¾tp¸?‘ègg'g'wªj© º4‡>”Ê G<û¬Ì.]¹”òÆWMÓDªÕÒYc«å*wY‘åóéLD¬µ@v¶X—×>Ns%JdˆE‘9gÔZÅxoOË"^}àê¨!cœE[¸b8(œ¥ßTÆâþÁäÒû“bXDöÌÁäà£oY €EcÁ"1Y4b䎦`ãd\äyî g2g²ÌæÎd­DàÂBêµF$bc� ­0FÌòr²ppé½ýÃñd4œ”ÅБCn9ícïc,‚Æ8k3k3D@"£h`B÷y A“'0áØó&õd ‚"14„ú! 7Á×¾ ÕçÉ rR;}ëIòºC¯Jb²–¤4êB &€ª@:¶",¢Þär0އ@ À(èClb´.›L&ƒÁ�oݼ~r÷¸®–Ó³“ºZ H¼qýãüüìô_~ð™Õ|v|ûVð+á°œM_yùcËÙ´©–“±¯j‹”úcD2€„@�#ë�©ñáèöñªª]–ûв¬êºñ¾ XdUU€ˆd¬�øïYDÁPC …Àl¢×¸÷"ϵàÈ@äP7™±úER‡¿"F"ƒ`�-i¬ #G`!ƒh‘Q"D!h¿å` ­ês´Ö„à›ºŽ êFB4ä¬É@È7ÐäåP€Èf„¶iBã#’µ.2Ëj†F{“ÉÁþp<°ÙÿÏÞw†ÉQ\kŸªê0yw%­Ò*ç„9'È ¢DÎ6L0É`09ƒ›dL²‰ LŽ„ÈBHH”�±ß�� �IDAT³V«Õ†‰ªÎ÷ãL×öÌìJÂp}ïýîžGÏ>£Ù®îêêªÞzßsÎ{„'ݬ›ó”çøŽ+]äÈMŽ%H}f0 Òñ‚W�VÔ2lÃEÏõn0à%ú®ïÐgdÊ“®¯<dŠþ«@JT( &QyÒW€Ld¢àú®¯\_ùž’>ú |D_¯À÷*"HDÀQc¨”Oõ¤”¾§hy* :·B4MiÒ\ cŒùe ÀŒ Î …Œqƒ “qÓO  ¦ú¨<`Â9*¥|¥|Å”bJ‚”ÒÓšÂ:4k�¥t^ÛúE2Úè¢�@¡ Gú'•LS�m?CÆQÒ7Œ#ýTŠÚ½iÚþ‘à W$¥SR!ýñò‚`ÒçdÈMÃ’žï¹.g?x£‚-_¿óu·³þúÁ7_¿vÍà/®?í†÷�ßÝ{Ê/Ö^ý΂9O³ñw“.z±°eÞ3¿™¸ãø«§7´•nUKî?ã´—ênúhmãâNLß{òå/—!pµöçžð Ö´yß¼zõÄIg=²¢ aå>»yÒ¯gzf;ûŠÂ'7Mºðãmÿ0ká§Ùmîå“®‘�ÿË['ÿò³˜³¡aÞßöûþš“nü¨Ä›[üÊ­'í²ÛyÓViªOœ²¾‰lÓÂGéÝ÷¨“Æ'üÕïýég{ípò#ßeÚ†Ðýä¦ã/þl‡ûf¯ß0ïÑñ .?î†P›†.ÖoÇ㯙òÉ¢¥óß¼nا¿¹èÁ…åenÿwXöý)¯À“÷‰µ}źísáÍçïÑeó{ÀvþCÏõþ‹7ܸñµ)Ó{{ü8ãßk¯\Çï„íØÖ&6L¹`òƒpþ›+W½{ijÊ¿xbúéšÈLÚÛó‹²ù|>Ÿo}û}ùÿ?ë±Ó:í¾Y#&_ÿÛc‡þ[ôëOj{ë‘Ï÷¥ôc`§�Š1 `à\.G¾wйÎ)ªÙ°¬d2O&£Ñh4D"v,Ç“U©X".„ð”´ Së@°Ó•RF"l�r¡Ó—Ò#¯©v` > j !¥$¯;ç77776llÞÔÔÐÐÐÒÔLU,ËbŒe2™úúzºÏ‚ºèÚ¯eY–®ž¨”2 #‹Ä¢Ñ¨‰X¶)ŠwAz”°@•ÛªÅEÄt&ãy‚BDÏsèKË0ªªª(‚@û~#‘H2‘ð]Ï2Ìd<AE8甆ÐÔÔ¤”2,“À€Â`EÑ5EÑÀŠÑO(Ý —m§P|Êá a½Mo³ }À÷<�К®ë677¯_·Áq]röRÍKêm*Y‹Åè3©èår9š-:Á$ì&¥ÑÖ^ë°×Tw$Œ|´33ܪÌÉvK¶;z.A("C!ßáó`iuÛ¬ ¼T×âä1$!Y¬CÊ4-;åœoji^¹råŠËþ9íùž›úɬ™ŸúñW_~¾rùÒukV-þ~aCýºÆ†zÏÉ›‚r™tKÓÆ ë֯㜓æ?¹OÉá¾V[ßJǤr”Â÷KMŠNTÅÈü<-7/b�©T*‘HX®纮iš¶iry*/‚ˆ¤z 9” ¢è%„°,ò¬�a’÷˜8}å‘÷XgPgh‰éÎK)]×/Ü|>ïy’1f6L>Ÿoimµ,‹ NëË•¾b`Ø–mÛÀè $ú’ / áhýSçY´£”©�É­í+O/‘n<R"úJÑ£’¯mBª¶Y­ó†hU†s7Â3_/„ðÌl[¯íMrÆX½C“O ¿]#Þ¶3ûå,×ÓÝâ7í EEu’vŒÄC?‹Ò‰œ¦qñ3ï¸+cL«â„ÇÂ_3Æ€’m~T¢ëzèOß}öø1ƒ‡ïyÎ…{7,ZÜŒàóÌ“_»äwgî8pèþWÝp’ùòc¯nD�¹fivÏ;ß{úŒ^mõÍû6ºÇ1‡­NôÜ変£3K—6–<µæÅÇßíqö-—ì5xÐnçßòó>öÜÒâV=3ó†sŸw߇¥*»æÎúû?ÖL¸ú–cÇô5ñ¦ßºñéÇßÏd¿·¬÷þÇíÛ/™ì·ß õß°dY‰—ŸmX²aÄÕo¼zÙÈ4cUÅ?RŸß}óÌ]n»{Rorñ_ìÙÿÔ6"°æ%«jöü{·ïné6rùŒ÷WŽ>é‚CV%ûìsùÅÔ?ÿìçn˜zRß^‡>´Tµ?tÀjw8xÿmö¨‰ÛX×Áƒ»læyŸ~xÕþž±ãŸÏ9ôùÇÞlÞ €Á浯]=õÂÝÿ|ú>OÞvÇwkÀæoïÚýÑ'¾P��îªG'<üÀ �À_ûú￞ž>å5óÈÉ{†3mÕò{öNîsoÑî}þû×D"Uv?ëÁÙ: ¿­¡·ô¹‹Çé1#5½ïÛÂÇW ïÿ�!;lxüðÔÈ«?÷sóþvÖnýªl+Þmè)OQ ‰¿à¶MƘ½ÿýk@þÛ§/>h›ºT¬zàÞç?ý�ØðÒ%{ŽìÓ%f™ÑÚÑG]yó¥·ïWu~è ÿj ®økÞ¼þ¨±=ãv¼ç¸s¦®j[¬eÊŒþÇ?Ö€²«§_9£G—Éϵ�€\t箉=ÿ¸T6ü‡IÛõNXV¢Ç˜‹ÞÈ��€ûá%c,:éy�[¾xðœ}GôHƺ ?øªWWK�Kž8c—a½ª£¦ïµÃÉ×Þô‹ƒ¶éŒÆ{n{üŸ>KSEÏ^zàðnQ;Õw×+Þªôs㦷®9h\ÿ.QËN :ÿÍ|»Rëß¾î°Qµ1Ë®ªÛíÆO<�ÀôWœ»ïà.ÑHõÀ½ÎyðËR¯¼7ë²!ñ ­¥/s¯œÞ½Ï¹oÀ[ñÊ5Gíп&–ì³ói̦.bzÎ#?Ûk`u$R3hßÛ>ÝBHõÖ¦\>o~~»‰Çíï:öÄcwVË—Dý;ÍÛÖ#�¶65c—=b‘H$±I¡÷­ÇNë´ö,;åØäèk¾ô�ÔÊ{÷Iî}ÏrÕÑêð¿¾÷èíuOØf¤zÀ.'ÞöÎÚŸ64jËVø×åÛö®‰Ù‘šA{_0eñfV-nœù‡“wé—ŠD» ŸpñÓ ó€O™êÑ �Àù×/út?ý•<�€7óÒÁ]Ox¾5ܼâíí¾ýó^½Ï}Ç(̸aü6º%lÓ®2ᢛ®ž¼Û .±hõÀ}/yq¥�lxåÒ½FÖUG-+ÞcÔA=9/ór¯v$Ec±X"Çã±X<Åãñh4ZL½&ÃÖÖÖÖÖÖL&�„rÉWìú~skkkkk6›%dÈ9xžÓÚÚÜÒҔˤ=§àyŽç9Š˜Õ;H 2„©T*•JÌ`AÀ³>^eØYÕ[&7 jKF)¾R¶mG"Ò?3M³9d1 _˜&åcg³ÙL6›N§[[[é'å6å�òùl:Ëå0(NÙ tBúLqàÉd²¦¦&‘HD£QÊtˆÅbUUUñx<‰X¦M›uÎAù^¡Ëçó¹\Îõ †ÉcË6*ßóœD"aYV:^»vmCCCSK‹ã8–eÕTUuëÖ-•JédxÂ�„îôn›�rÆXà?é0P$UÌúîÀ`\Ô\ŒeYÍÍÍÍÍÍ---­­­4VÄضM#F#‘EÅ BFª“eɬ=ƒR^  ·‡“žB•YЕ؉>—Ѻ –x>Û8ÁeÝÐÑ×<$s€,œ&°ˆN2 Ã6­žÝk{õèÞÚÜ´è»…Ðàìãf®Z±|ã†úÅß·tñ¢úukÿÝò¥K¿¨¡¡È2*ÒAª„,ä°-C•e£§Ç$<nº‰î¼>RÓ 4·Ãbþœs˲ Ù\sc1ñ‡ž8ÅM$“IR%²@g¾0Æ87„0…0JA¡àf29’´ ç¶vq“`*çœ1d ×4�°BÞÍçœBÁõ}EÃÉ Á"• øA0 gÌ2Méùžã:¹|!›ó Žt=ézÅÌÎP/b(!P¡Ð?œ³4Ñ©t*Âö$XX‰ÀËT»¨8Ü:FËes¾ì´í^.üLËfu¸“z=ç†"©B ˜B¦°Xý“±9Sȱ¤Ÿ•A»]-k‚!znów4úÖ%QÚ¤í´á"�’öÃfÚÿ;V˜ûÎŒ¦í&ìÕAþÛyKkÆŒ!2À=n”\0÷{X—ƒ¯¹çŠÃ‡–H™;z ˜vÓu//j^÷¯?ümÉø³P*v·pî·0rÛ�€:nŒ½pî� 7ëæ_¾¶ç½¿?¤[;;wµþ›o6õÛf4‰äÅÇŒ’™?o¥„䞇íµáñkªeÙ´?NÉwö!UávÑÝ/¹çºIckÚSsTËž¸}jÕ/~;¹'�ž¸Ãn»íe`Œ9ç·¹K÷°{Yô9"¹ðµ©ŸÖ|§y]«4ëW®ñX´ÏØÝwÛ~@‰v_hè��pýCâñ^Ûÿü£î¸ýè›yPÆðm.xü´û§Ÿ~ùÑìã[g}ÖÚÁq*=ã·¯¾Ò0àœœñ‡vîþùô?ܳÆMÕ–_<·EÈek¾ot–ÎÝ$Ôšuß7u½ÕÁ¹¶ÊZÞ™òfü¨É»G:>„¥v¼à‰¿[øÞï¶ûêÊ£/}ƒ°h[CçÝ[~ùlÍU³Ö·nüî?;0²ýa‡Ô~ùÆ¿�rOÿ¬jßý·iyþšK?sÏ×[×Ï{ù† =9�€ùëYé|>ßúæ¹u<÷Á5Gÿbư›ß[²lÆ ýÞ>,’€Ùå³ç$Ï~}ñš•³ÞgÑ=wÍÙá®·ç/þò¯û¯øý…wéÈÅ÷ŸrÂ_ÿþå’…ÓÿtÌ€6ÿ–ZûòÔ†ü(eWOî}øxþÁ3³�€?˜¾`àøñ}–ÿíòëøÌ¢¦–Õ_<÷«]â��`îq×wÙ|>ßüÔ166¾táQ×|¿÷Ÿ?]¶è•óÄ“güòéõØüýçß üÍgË×.ûè¶a3ÿàúÃú`á÷Ý>ê³k.{d™p¿¼õ¸³^êrÉ?ç-÷ê ô¨\غðÙâ¤iß­^5ÿkö°Û»ÿÅÝÝ“?ýí•ÍMË>züÌÑà¦W/šxùìqw¼÷Ý÷3ÿ´û‚ßyÁ‹Cosûƒ¨ùò_3[�Ü/Þý÷>h71ïÎI§LM\ôÊÂ_þy—¯}êmŸ{�éw¯<úWŸŽºùÝ…K¾œzÉ.[’rØÂÚ4F|pï÷ï¸â©y›6~úÇ¿|:ö¬“K:þæáõ¨6Öo”‹¦ÝrÓsASˆÚêõØi¶õÖÁêPõß|²z‡ß}±xù·ïß{ÈÆûŽ9ú÷sÿ³’ö¸óþñáµë½|¶1åüßLë(ÑF­zìÌ£ïXwèß.^øÖuƒÞ;oâÕ3r5{ßvã'/‘�þÂgÕ7}þÉ@.›5«a‡ñ{„¢ð*ÞŸá7˜¿æëÏ ‡?¹põêÏMÎ<zëÛu×¼4gñ¼çOr½àæwó�˜]úÅìøéÿ\°lÉìçÕ{úù‡œÿÒÆŸŒ(ÂP i3¨�*•ëûRI�ä'Ïf³Te€Õ×1'‰D"‹ÅLÓÔZ}i(ç_ÃKSíX.KÄ çµ†}_ű«ðȉ‰D‰U€#§.ù½MÓTŒm( \‘·?“ËQ¯0Ð~§ÞR·iÓïºn>Ÿ'5„\.GÇ45µ466’¬@6KaXyj•Ïçµ^€TURW¡³m;@>›#VÂqºe 4ÀÖ¬YÓØØØ°‰D"•JuíÚUÃìh<fElrÔ“€B%&)û+|Œz„õø·;-XP)]'†¡õ9ç$ë@š‘D7d2™¦¦&’`$ >¥Õ,ðÞëÞv”bÐÖ“À¨¸òÞÃL”"‡J˜¤McæpzB™af‡¯Hâ16èBqÁLFD ‰F£]»v¥†Éd²®®®W¯^ÕÕÕݺuëׯ_—.]zôèAÜS"‘ ä"‰arRîÀŠjsº?¬­iŽ@wX÷S÷P[8šƒ¾ÑŸú³eYT§£¦¦†ê)d³Y¥¼d³Y-Ü��0Ÿ/ÐÜм qFz`ˆ7TJ™¦MƒF_£*ŠGR™ÝŠV\>Ÿ×tŒ¿'ÕCê3irJãR‰!äÏ ˜·R! ºÊù>^ß¿ìõÝžºÐÞÂÔ‹±£…Юa)¹î­–ŒÅ`LË¢u*×Bxª‡'’ÎkÏ¥ðñeg¨<-V¬tÝ6ܪ¬Yø`…í„`´×‡²~nÝøþ›æ-{æg'>R{ÓÃŽ€¹tFÅ y–HÆ!ÓÚÕϺM¼é†Ý?ô‹ Ãò Ì…Gôã¸ñÑÃâ†aFt—ß-lMg#Éx°x2æ¦3.€¿àÏW¼°ÍÍ×oެˇچafÍi/oJg!ž,éD:ƒÀœþ»Ë~z×é{ yÒKÝÏ8oß®[»ïw>þó}óö»ì‚q„Y÷CoqÊ»Å;8<yø¿(ñÌQƒR‰ÛyËôF#Ü㪩Ón9¨M ¿dèèÄ=Ï}«yÓ²™÷íþå/޼îãBÇ=bÕÕ}ûÆb©äðI#‡xMk×·ÏAáÚEïŸpÅNÛJt5â”_ξ6¾›³[ÍšÏV·"6}¹¦0¤KîËÕ Ò_®Z3¤ÿè.[9(í^¯éÍ)oW3y—ÍQ ÆðñGï9¢ÿ€íO¸ã†£rÓžz/WÚ'R±ìòyßÖ{±îC‡õŽ€½û Çuÿàù×ÜÙÓgŠÛ-b&“vÓâ9‹6©D¯‘Cº6£P¶) 0㉧6rí­G «í±í)W>ø«7§S¸�‹víݳ¶×È#Î9zˆŒ÷ßnxŸº‘Gœydÿ•s¿iE¹ôÅg>tþWNѧï¨}Ý¥g@©ÕÿœòÉÈIÇ À*®^uЉ‡Á›Ï¿ŸÈΚþE¯ƒmD’ £aáWËZyuß‘‹ŒÅ.rÜôúcÿ'ÝxͺÕívÁ•ÇÆg¼1+��<Q[×£¶nÜÉgÜÕ«´ãº¾ãN>}BbÁ× <ðfO™²x·+ï>oÏ!uý·=ø qU @~wÇ®QÃ0 #~èß6 �ðD¯A}ºwï;¸.ÑÔÞ…X<•ð×,˜»:ou0¼_’aÓë{NúÝïŽÛ¶_¿1o¾ëÌØK{9¼ìqÔÁÑ÷_y? à}õê[éýŽÚ/òÕSO~³Óewœ:¶Gí°‰¿ùÅÎ+Þ~û{Yøà©ç[¿þ®SvP7h§£ö±…,-­ÍøÞWÝ1±õ™Ë3pÏ›VîsÑ #ÍÝ<´y÷C®¹ï½MŸÞÆŽc&>°°ζ~=vZ§mµmnu]úê[7pÜ‘×=zíNß<úäçÿQž€u²ÍàÕ5}÷üÅi»ºß/\Õ¾kU-îÑé½Ï¿ûšƒGöé¿Ãɸåèæ§^è·ÿ„aßNŸ±ÕšdFjù`ÆJ…?˜þ혃öµýeÿŽ·÷öuîéÙ³¶Çàñ??a{Óî=bt¿ÞCÆŸ3ilë¼¹«©G<ÞsPÿº¾Ã÷úÙý÷œ&^üëË?O€¨8g\.¸àBo€Ó®7ŸÏS¦}*•ÒU�…®'¸‰uéV[Óµ7LÏ“œt9¨¨mFmÓ28(_z® U=À@aNo^}ß'75 ι`œ ‚a(—¶¿D8“3?•JUWW[;U]•H$Ñu]Ц�!D4#÷¾aº"ºÞk€Ä9Î1p¹KÏS¾_(ˆ(Ïó!VRÈfs™L6“¡p bœ|>_ÈåBÞsßs•àÅ"Éx,b™Òs›75®_»nͪÕõõõ›6m²,+‹Uw©©­­íÖ­[u*EñÑh”1F”„aÂ4̈]Ó­;IX Þp†¥µÜè'Ñ4š"!àG+Û5Ïs�¥ipÛ2,S�ÊB>ÛÜÔ±MÛ2lË0è ¡Då£òu:™¨&P§#@ºã¹¹Xå2ÀPIs„Ÿi»�ƒ±ëšK Ó•à'Ü0ÒÐè7P΂ќ!Û.]«-Ûð|Ç0y$j557ÖoXgGL×+(ôGÓ©ª„0 Ð·LÓ4 %%gŒ>()‘tðˆÑâ“aWv8 G…”áÂwÁK«Í…‡NÊ’ s r Û2¬ˆiGc‰TUMך®µ©ê.±D*UÝÅ´£È„'1Wp=‰L˜Å’¥† ŒûR9®G¢ ¦e+_*×ó=_*æKåx®ç¸(#7˜°MÛ0,%+ŽœQœ>çœbLӶ̈¦iˆ¤ž_Aö»`œ…Z7!<Œ1dL ¯ Az÷ª¢ŒL»0µlÆb¨h_Ù‡²q.ÃÉšÝL¾L»ËCÄ„¾N.Ð7ah÷ÂKƒ1P ãÀ‚ÁÆ ¨lKKX`hóý ßT°Ùõ¾Å"eÊ�RÕÆvÿ‘6ƒžÊZ#!8B¨"„¾/ŽðÃ¥ ȼ%ÿ8í€_­8õùg/m�‹%<›ÉÐSÀL: ‰TGÕ“üoþpÊõù_ÏZ²|ÍŠ™¿øÂ¤XŠ5ÇÜÿùœ9sæÌùâés¦’ñB&[Ü©`6³ _ºõ~vÞµÃéÌq—¿6{Μ9sæÌºm¿šd²él¸ÉƒÜÌkO~¨×½_-[¾vñ[—÷Nüùs [wßùþþ\Ë!§M¬ÝZZ÷œpÃËóê³n¡yÑŸŠCG-ÇIåC§›Fjìþ³{®Úké3S;Þ"ª†÷>ºkò£çî~ÿÏ™¹ÐCÕ‘×§!ө:ÌÞU5NfSšõÞs@×ù+æ·äçœóóG­Yùͦ‚ê{îÙ¿öG„VcãkSþõþ"ýú÷pê×µ`ICsÏ›þyß>‹³{¿¡\ôÄÜ4‚µói'œñä󫜹o¼“;`â>qHMüÓ?oþáyÛöuÄÕÓ;å§ÆôÚµ--/œÜ=‰D"ño™/›7m*yÙñTuäry��–¬JB>_�U¿¶úôïSñG[-Ÿ6õ˱“Ž"�Ú¹zrÂÇF^}òõM¹™¯Ï¨>lâö&¯;ëoÏ]š:iDßí&ßöΚŠ·jX½¶°ú¡Cª"‘H$’:衵nÓ¦t)žªJæ³yb7’©„“+�¨úub}û•"a1ðì§¿˜3gΜ9Ÿ?pLÿÕþ…øèKŸþû¤ì= °ûY÷}܈jê5²ïàEŠÇè?¸®]]B@Åö™|„ýΠ諒¯^|¥é€ÉÕ¨u«×Þ¿xp,‰D¢/þÀknÜ$ÓëÖçjû÷Ýêo[X›jåcç\0ÿäw¾_¶zõìGöú윉·~åýtÍÕîpÌç^üÛ»§~üÉ]£>¸ñÎw5#°uë±Ó:­C«p'lÕêà=† M­_µö?)ˆá-éê#·P[•ì:æÊ])Û'ŸA®]½úî_ü£0¨W~Íšf>òÐCú}ñÖôƆéï¬<à·Wí·ìÕ7½÷æC=xP×ð_öš-¼½‹Foèl€%SI(ÐÛ:löÐTÙkêGc 8pœ`Ì 8Žœi‚šššh4J®c’ß#>� ¢çy$g@[ÒH$‚Aî4å6»®«|©ý®¢TŸÐ8  t¸²VŒ£Ý[xOI‰Ê*PD/F®rFnXrà�¹¾)Ï™B’É$q UUUU5ÕV4BÇPH ’Ôyø*Ú¥I'Ï*ù~igO¼ ®NB‰ˆN§=¯ø¨iôèNI}.G{öì9xðà#FôîÝ»¦¦&†áºn¡P zbãÆ MMM”Ö‘ÉdÈ· íÔ�„言.Pº)§ofn„Ý›,ð—RqG&„!7•à:vtƒ±X,ìi‡RÌÀJSèõ”hxµ&tèá–…ý—¡ 2‚€…|¶e‡Ç‡…"öËÆMã±0¡ý´ˆHÔ�Ñ",!eJ¨innFÄ.]ºÄãq¢~‘nD糈@ÞŸ†‘‚etnyÙ}A}a)) '@S�AZDž,sùò"@.[@Ä|>Ob555UUUœóîÝ»Ç!p¤çsŽÔø %�žçe³ÙpäBÛ- +(,…Ä>c¦08ß0ZC„j”Äãñêêj¢ü4ßA˜Â�©ˆ/ µ¬ƒ˜hmJ,ê×3Æô;TÏŠvgŽž�áa,7¼ÓîÁ•¿*íðy*½î›±ð™Ë–OÙôд]xMU®2ý †¢$*'Ue´WøCØÂ·Sv9}~ý9ü=tlm÷¥°C0¾© SíT¸w•ò ›ßûõÑ—.?yÚ+×î^]¼Xtä6ƒš¾ùf¥½ÏŸó­µÍ°ö=‰òû—žýzÔñ'ŽŒ€Y»ëùWMîöÉôÏr¢ºÿ¨1cÆŒ3zp·øˆ±#aÁÜo}��¹èëoœcGî'¯¼±ò£+FŒ1û ¿®[~ï¾=NUô6z̘1cFìSÕk̘.+çÍ'À•ýfÎ’Äèmú ïËçŸ_µë‰Çõ3Àî³ÿÕ—ê}ð¯9[µï÷¾xííì¾Gî—Üò¡åæ/}âÎ)¶=î˜á%°³½¡+1�èøe—L¹þ[qüÄ»?<ÿái{ê”óÚD ¶Ö‰ÜÞÚæ&;^“1bðŽÝÖ~ùîâÙKzÛ­ÿ¶c6Îywñ_%wÿc°qëS߯;nëþ²K­µò�� �IDATö®«†Ò†,µíé|mþ²Y7õ{ç'ün¶Ƙs.ØcÎÃ=õü«™#O™�^»û/œþý’·i>uêY,U\p7xQ$ºwOv;å…M´»(8nþ³«G•‚Æ€ë€1 S½úôÂeß/+Ï –K^˜:g»IA^LÅÕ!º÷ù?ðÎC{ê…wj&¼‹�FŸ¯úûÇK¿}ö˜wOºpJ# ÃÀ¶.òn=»Ûƒ/y?]ì¢ëeß8§,²=üz>Š^}zæ–.^Wº1¶º =f̘1cFõ/Ï›éèBÑaÇÜüüìesÜõ›kŽ»ú·{ŸÞbÕ’åEm ù’¬WŸÒ”†èÞ§NJ½5å•·§¼˜=â´ƒª€wïÙ=~ðëóÁ0;KîÞËJöî¨_¼¤u+_1[X›ØðÖÔ÷ºO<q§$ãUÛœríÙ£Mÿ0tû?²yØDÝØ1]Óõõ¹Òžoa=vZ§µoF<n654–lEØV­lY½:Ó­g÷Œ?½µ¾tÕYO羺¨1½ñ›;÷í8"LôîÓ —/^Q|I–/YíÝ»š㎙ØkæË/¼ðêüÝ™pàÁ;Í}}Úó/ÍìwÔQ#Dø/»ÝÎû³] v¤Åÿ€þÜfrÍʵPÛóǰۥ÷V„çRJéK)—°RªèrD"ÕÕÕ–e:�JËF£v,Âp¥Ï³Ìœªd2‹ ÆA! 2·-3‰`)Ú‡Ðæ9ç”ÜÎKáÈ©…R‘¸�c W¬(WM™óžô)q€P4pÎ92 B LpA$Ü’H$⩤þK&(©ž°/©ô˜Ñךø Ï”[�çXPpŽ1æº.SÈ‘s.¨¼¤RJúžï¹JJúÛë8N:Þ´iãš5«–,YTß°aý†út:M‡NEw‰Djkkëêêúöí[[[kXfÁu´úZ±’9!a¤]dËS.wøÐ…:šÚCK Ͳ¬ªªªnݺ‘�A8Ýò,˲\·à8yÇÉ»nÁóßw¥ô”òmÛ4ŒbÎ9ý#•·0)eŸ¡"Ž€¾$,M÷¥˜§ÌoÌCUÜ‚B’mJ4åÂÁáá0ºÓ£§‚"—á� ½¯¡5Í"Ê×°„pr9ôýD4j†t]ßq|Ç19÷ ¦”›Ïç3¯P®‹¾OƒO$•Z „¸ŒvŸþ^Ó4PIáK*üÒÎSÐõEà‹µ"6p–Ée©ðgCãÆæÖ–e+–¯^»Æ“~²*•¬J%RI;)¸Žãx$[È,+‹%l;J•/ òí¨iÚTƒsƒ&›ÁCŽ(Œˆú³cQ+°-…l6Édr¹7 Z×ô0”îaš¦eÐ?ÃÂ6͈e—�] "(-ÎZ6ñHCŠtÐ2£ŸÔõ”Æ>CÚ¯íáÏe ¼ÝEžÛ•—¨<²ŒæÐÖn[:1i/ê‘§!¥@)ŽÜ䦮jÁ±ü„eÓ¯²c[OnÞÂ'¡¸š-7iOÂPW§ cŒŠ$ü›&¿¹ï²G«¯|ä×ÛÛN¡P(8ž0ÆœxÚvsîùͳ—/~ïΟq<óˆní÷š÷9<>繿ÍX™ÎmZðâão®<fDIÀ¯;úŒê¹î¾–.ûôÁëZ¾×“ û°Ç7Ôyëg½\ô~ý‡‡òÖî§žÜûÛûâü•ß¾rÃí¯w9ñŒ}£ ŒÆf=õØçë³ÙúÙOþýwÄ6C¶ÏÊ¥³>Þ0f]ÚXÜðÆ5Çžô‡O³¶QNkýâYSo<v¯>sý_.)�2³~Âq×½ÓˆíZ3ã™çg.X±få·üõÒßÏèuÄÄ:Æÿ4k=Ç÷1К`†m«ú…s̈ Ëë%ôºïÎÙwïú|î²ÌÆo¿ûÇŸ—Æ=: jwŸ{ß'KÆ ŒÝ¿ç÷ÌšÛsè΃~Dþ5®{yʇŽ;~lìÇâQoñœ¹Íåˆ[µ¬]¶vúE3þrÞÕ¯v=õìý£¥ Õê/gÌ[ÓœçµcF×™é–Vàu'^vBúÞ ÿR8îôýâ�à,ýôÃo×·¬ºmGvÇÖ– š÷Y;ý…é‹×®üæ«Å­‘}O”˜öÛËþþÉâuÖ/Ÿ÷ùÂÆ­™ë|àñgï¿þþ ®znöŠúú•+È•/¿nê‚'Ý—v£•W#Ϲ|Â77^òl“Og`ëÂYŸ.Ù˜ñãÇ­q[Z ¬ëÀ‰…¯?ÿ銵K¿úz…ÓõÐSI?våuÓf/_¿aí’9³—¤·¦‹Æö§œ¹íìÛϽíõù«7¬_±ºÑÝB+Öî…TÃÜ™³WlÊbõÐmúdz--~ÍagƒO_uõ sW­šÿòuW>–?ò¬#j³c1X;ï«5°w=óÔ>¯]qîSlÒYã�æ'ž4øƒÛ.zðý…k6Ô¯Zðé7ë@dß³Nëõæµçýù½ï×Ö¯]¶¶½Ú£!kmªåS~yôÙ,ðYõð‘=–¼üØ‹[ò-KßyôÅ…]G®åzAýÈæ —¾?í½¹K׬YúÕË·ß<eãöãw¯b?l=vZ§µgæv{î”{ùO÷¼ÿýšukV5û´™Õ™yï¾9{éêeŸ>~ùï÷™tÂÎÿÉ9§”Ê)ä]¥ÿ¬è×xÛ{€ 8î¬ñë¸ôÖ·®^1ûéË®ýgÕIgã&×ë­k®ÿl÷£Ç§ºtÌγn¸æ­~Ç7²ŒìhïýùƒL®ü赿]¹jÁ·þ扦ýO:¬;cö–›mÕ ÕÆâÂdLÐa˜Â°8áêuëÖÅãñºººx<žËåš›› …‚a™Íé ©÷3&Hõ<¥aïA/ß÷óùœFþe[s ­÷B†ˆ¦ij82äiÛÚVÊÎ4ˆY(zb#6cŒ*áiµ<:²ˆH}Ð4TH’€ø¤”ÄSD£ÑššÒö�"Ñs\ºŠÎÖ&xL¿%8FÍÀèöI"#yžÇÃ�¥#"à1™L†Dˆ• Xëy^ccccc£ã8¦iF£Ñ0ü`Œ!�P *¡# BxØuFzG B~u=ìMdJX‹ž˜J;×Bú~ -k¹D òmaØP !Â8J“>:õ€…*„ïW_”n„ž±ôXJÆ­,g¡l4 È/ÐJ*TÎu]Îy,£ÈÊ¡æJ)š–­­­RÊx<nš&©<è Xz@ô¤(g’" )YER½ý¥¬�Ãí¢J½Ü4¢”©hêR¦‚ø¸x<N1RJZP¦ivïÞ½OŸ>Æ ëÞ½;ñk4ZãfEëÐèqñH”^ÄÄ©JÎuP@8:@*/NÓ°PˆM6ZÑzÀéµ]ßo(ùe¯ îB¼•nÕÑ|#dÊöWAžH8ù¥£@™Wv¸ Š—}É: !Âg«¤NôÜÖë¥òüecXù¡²c•· í-ù2Óghû@ÐŽ×Àf^w?‰áÆg.hùðŠ1‰(YÕ‘5 ˆáý㱉ëo?bìäg»\1õÞ£;�`ÕGÝýìåÝ_>m›nÕ}ö¹nɾ÷?ýë274ï}ÊCϜǞ8zÔ!÷æO{êoçôß*ŸAd×럽gç//ÞuØNçÍsçs·îà}Ïþë?N•MRS3ø°û²“ŸxäÜ[s:тű!ÃB2Kªuéç3gÎY•ëèù¹o_8jاÝõyïó§}öæU;Æ��³+¿úhæçK[°ý¡S sŸ»íä=G ²ó ÷5ú×Wîß‘öÄwåÀ–GŸ¿pûÏ>øƒe½ºt2]÷8y`aêÏ}¥ìݶ9¨ßÚ'nZÐÂ’ûÜrø‘µË>ùñËÎýdÃNã/¿¤O�€÷;|dŸ<}`¿(@r¯¡#¤×ÿȽ„OF­þçÔYC?>pÙcNºøˆìý'^73³Ák†ï8rÉm» èÝ»ÌóÄk·ï-mˆ­_?yÉû§ªûOø+?çÞË÷2�âû^ø³‘8è¤Óv±�@ÕÏúó¹{êšê2tÒ«}~}×yÛc‡ n?'6õ¸QFré´Å2yÀ¯üe亮OܶÝÀí¼ìùE[¥Îûùä«7ü쪆×Õ<éØ~Û¾–šÿ쳋v›|TO;W�V{ÔE§ôâ;žvâ0�jåÛ·ž¼SŸêd÷±ÎÞñ¶ÛNîÍ#\yÇQ­÷N2pûãn~gê1ùá—o6ë׎ê[7t×I7¾µz«Bg1—¿ðâ…Õ/Ÿ·ûàÞ}·¹ðÃn;î¹Y§#kïBþ¢}ÔØ^UU½v»yýáºîð$ërÄ}¯Üµýì+÷:t‹g޼íåû­e�‰Ï»pèGýüñõ FyÞnéÆAgÿl�ÀÚášÿq|î¡SvT×oÌç?úu�b{Þòê?&;œ¼Ã€º;ܸpØNÃ7§ÒþÚTÎúpæ7ë]°÷ºñÙßýì¢{¤zìpþ‡Ão~öÖñѶõ#›czÉ»¹à m½ÿůWŸÿÜ3 ðCÖc§uZûÆûœ~ßÇd8j›þ}‡úHËØqâls«Cmø×Ç yð-+öhÚ»mF ö'·Ô‘7þé°ú;öéW¬péìþ#úÄYø5Þö`ýÎzüÅ«z½~Î΃GL¸aé~¾ü»}��Æ6§ž>&ÇÇOšÖí°I{ù¹q§Ÿ4¼ìíÔþûó‡¾}âܽ†Þé¬výÕs=­Ž$~’A@(î¢Û�ô) –m³÷®„ÁêêêH¡0®X±"vïÞ¸E8Ö²¬T<æy^ãÆ ™L”‰D¥9‚išŽÛV„•zh¡"Ø�P*B¿m“J9œóªªªB¡`Y²"¨sœ<É®_¿vëÖÍsœ–––D"Ñ¥KOJ$•eYˆÈAn¡æm›cå“nœã8‚qnݺåsÃ---�à8N,£m7qœ‹±q^ŒdÖQ œ ιëy<¤ç'%úJbÀø¾OøÐ÷}ƒ ú¯D%„@ƤçÑ~vóÄG¸® MÓPÒ÷)À;Ø‚#c̆“@ôN—€�SçYÝMØ›FžøÓ4ãñ8AV(-‚À‚€íB!‰DhJˆ@š‘´- ÅTÓŸ=ϳm[)Eµ*yP(‘"óËÀ0cE©9VáŠd¡Ül}S¡(r= Y–E�˜uÆ0Úª0„ñ›a˜áïõ™ Á�+€ ýy¤§Çí;�hV‚Æ™ÅÒŒDèqPóŽÐ ]…1R±Pð¼>˜±6ϼžxt×eÈM/ÏÐŒ-Î ¥”aEé<šJ sÒ¼¥¡ELR©‘q™L†ÎžtZ}×t6ƒsPÈ9·,‹ó<Oº"F#ß÷=ß眶Å9W <I‰LÅ ©”ÈD¹ô&tKa¦R!æ}È 4ƒÍm}³¸÷…Pšö_öšªœª”({Lá ©ŸZøTákéæô^*>åUžD_«~sf”O?õÄ€ª V¾t5;¦$0Æ Î…´œ9+,"¢ëº×á&/»5z 4XuÒ‘é!*»5 Yø0ÏóÚ¨±2aÂÒà;äI%s†:¬½1àE½EyŒV1ƒë.¾üècŽÙ|`í»ï¾[UUµë{oþ6;í¿Â>ùèƒÿ#ßÖ+µìžý¶rø3¯öÃö|[lˆ¹–”õoývò¯6^õÙ”þóŠóþìkÇí?ç²…¯œÙÁµýLKŽ¹Ëž½äøÛ«ïÿò/üù)ÿ×ìæ|î´Nû‘öïOl÷íŸ÷?ƒ=¹ü¡ ?ªØÌ²ÿ…ËP-¿gßm¦7ÿ½‹ú•2ÿzëõƒGc›}PR*ÄóçpÀå¿cìÅiÓ®¼þZÎ2†€TŠ1Æ8cÀ B­ÉdÒu]J*ŽF£µµµétzõêÕ†éÑ£W,kjj\Ù¸‘1EúôéÓÒÔX(¤’ˆèKÏ4MCù| Sc9 0(7X¶Í…ööÜô¥ Éty²ÍGmš‚ .Á ²h4JEôõóù¢4�/j1jÏ6í ÎljD"ù|޲̚šÒà¬Xd¼å ÓÁt! 5¥匄��(D;!ÿ0�Øv4‹ˆÍd2tB-if›qÊSRJ_)åûáØøl6˳m[0îyžR>aβ¡£ûRªmã®7÷J)rÁš¤:A©^AàŸj+ÐåÈÉL*’,¨I)„p‡+4þw]7‹AàH'`F|iš…B)$D)EÍ‹µëBh¡¶-o´4ºÐ#À‚0{ uI‹0pRJù~,©D)P ãñ¸RJ†äâ6zÎ5ýD7B!*$1@} Ñ„žÉ‹á= c9ê­Ç  ©ûî|»k'ŒZËT$Ë ©>¹aIQTë>„ç•ïû---Ùl6›ÍÒ\ŠF£w�¹\Nky„Ç”Â�Q\ P¯Mš9†i*¥¼b‡eÛvÞÉ Þ¶”Rˆ „P ŒbD:D_Jú©2Á˰++5ƒçÒE„!'<æ°%$~e×-û²ì`P •S±Ýg¶²¹Ú®•žœáW"„bR¨!ƨ`„î¯(£P6ýÚí†^ P1’á&úRŸ †±,” l`)Ö�ƒWq¹��ų`¨y‘êû·s:­Ó��ÔÒiÏ~µí¤Ç†üP¿ÐªUŸ<öÂÖÈîxêáÉÿUé¼ÙS_X»Ï5GtïèÚþü?°ÇM ª¶;þúgßÉtZ§uZ§ý¯4Æ@‘ó„öÅE cmóù|"‘ êÆÆF °mÛ÷T¶µ%ƒ(¥L¥RvÄô7NKD‰h™¹é’Éd"w§±±©,œµlkÈJP´ù#¹i ý)¢M<77ŠŸ`J¶÷<Ï`Üq† ¥ô�ˆæ(¸®mÛ”q�‚›–ŠçáezqÎ='ŸÉdºT×p(*æÂ>:OIP 83P1TÌ–ôÑE�,3bT‘1”Î8@qóÉÇ3M3‘Š(¥<Wf3y!„a[]kk€4íœ|¾©©És\}¿vÄ6 à Šx®kYHI;\ѨZ© rR%@�DI)ÇX²‰×¢ºº:sÎI{‚œu�`[†’^>ç""ÅG3 @Ié#"g(}v˜sÎ)ž¾L$�à8>¤ó#¢mÛ…Bèâ0ÄiT 7ôªB§#4‰Ë·*á¹T# ÁOx**¥+©‰XÁ[!Ñ7Jù!í:´9/ƒ1Ö(ˆ‡âù)€Â²,ÜêFQI—!’4�CP YÀÁ8g\¡BEÎy@UU   {¾Š©Ñw§ÒÕÃ­Š‚‘À8G_)_úÀç&�øÒ·M“NbZ³M,FCx©D̶m×u•ïzªÚê9yËà´´9 rÆ@qð<GpÎ*‰ (É”Rœ àÈ�¡XýNMAàU/4º _IàÀ9G�† QJWúLðJàW†l+'Rå|hw²éiáv¡o»§-£�ÊŽ ÿ·ß"Vêqb[ õ­ìFô÷:Š�€)Ƙ’¨”2… j3@`¨ãÚæöðW›·ð‘ºmG4 ”¾»ÈŠQ-A€�uKwŽ1zÆŒC$æ㊡BäEf)¬@ëd :íGrÙG­—ýW4äýÎ}³ùÜ·_?…™;ßñí¦Í`l{íç™kÿSÝé´Në´ÿÍ:ðáukÿ»;ñ?ßø€‹?H_ü_uvÄ¢ì¹XÊÓˆ†çy”­—i6›%=sD,ä³---Œ±x"Ê8¶´´dÓ�Ò³m›1Èårä`ßd¦08ª-!,®6�Ð]¡_è&*H \Yíð²ì„ã8L¡a„o Á#¯fÞq\צ!¥–éyžtÛ ×QôvãSº8¥U755¹Ž}©1'IÓQ—,Ë¢ŒhÒ`7M3ŸÏçr9&Ú¶é-„)La±h,‹D-)e&K粞ïKÇijj"Û¶‘¹À(×Ä"‘ˆëû§I|!‹IÏËf³äÃ7„A‘?¹ÅKÐ(•é`¹­3¥Uð¬aN—�’lÔáëˆHÊ T–rCC!ðl6ë8N4M$4mÂHXÇ{�y›‰ŠRJiŸ³ iXh“>lÖߨ¿)ƒCÆð ÉÃ0\¬lþ^/q*PÎ £/)¥Ä˜Miº­eY”¢B‘Z7±*¤!§Ñ „‚_ôÔ §ÊWv¸ PU]ô[=…´zExbÑÒ£3!´:5¡è˲h(4”D"©TÊqÝg 8Pù=×sQúÞ`\A!­GJ9 ’G”뺌±RÈ]SÅ@}F…ò8dÀKàq˜YÐ(š1ùmeçׯ¬v …Žxðs„ Ÿ¶¬ÉèY]v•Êž»žüÒóð²uÁB9,•EdDâ–ý*Í¥3móПÃ(Üœ¾÷‚¤-!ý •R:ã í¯ 8#Ö üÑo ô#þoðÈvZ§uZ§uZ§uZ§ýO6¬pDQ¦!¥¤hpò‘jA U©DQT¬w ùB¡ „¨®®VJÕÔÔd³ÙææfÓžçeó…dÜ�YŒê.‹2å¥%Ü5Ž *6oÔ ‚R‹ñ£„%cJÉ\.‡RÅãq$À×ÖÖ"ARJU>ŸïÒ¥‹iC©ÈéˆUÉx<w]×¶mßõr™l$!Ç> êüM@qã$YG5HhŽ‘¨X1K”/;jFãIjžÉcV4VmG\×u½Bkk3ËfãUUUœà%mbÆ\ß't‰Ec<îÃ0<à å6”J2¦�yi¤.hDT*â¥Ç_)E%I ­`˜Å˜yÃh4Ês]—æƒÆÆ,ð~÷ìÕƒ‚lj'b]ºt1 £±±Ñó<j›Íf=Ï#.@J)}Ezx„!é6)CÏK bÚË"äÃÖ £¯JˆR {”’’i¬ˆÿ±ùé³€öÒ°„˜äL×5, .x>Ê"ò¡ ¤ Àw}Î8¨ ‘ž )©€·ÉjȤs7tQý}»P³l4ôFDßóÊ9Æ�Àõ<Ð14zÒ÷(sÝ2 ˲"�LC ¢àLJŠç/¢r&x!ŸcŒ))In³P(H_˜¦é{Œ±b µ@…ˆh™RœsÁ‹ê ¾ï[DOéY(%}_rÃD)Ý`òÐñŠØTJ!�p`LpÎg��×ôEú`1Ɔx@ä� €�R¸ƒÆÛ 0,u‡\”‚[J©|4aÞ*¼0uÃŽ¸ƒ²©[Æpµ=Ph'v <Ê>hº A*�d 8#ÁÆ�S �8Aü¥Ä“ %‘mfLÊ,|ïb»Úí¶>^¯ùM 1¤šçAÄJ9ÝÀèïKeJ–Në´Në´Në´Në´ÿ«V ¶d7¥mWfŸsNÀ|>OžgJ>÷]O¡”ﺮ.š‰DtA¡PH&“äˆfB(éiV‰WYEt4“!‰/¦{[’W¯BÚo\€iš*H ·m[ ¡”*ª¦xžGЋ@%ÆÂ7…¡+ÿµ6o²,Ë-8,p¨Z–ÕØØH|�КgŲAtAÄb§Ì"ù³„IaØEï+g+êÛ[–USS“N·4ojÊd2¦J)_a¡PÐ’`a‡?}Cå ãɤeYÙlÖ÷=Û¶)ê¡Ìo¦¡ IDZÀ³ v�qEX(%ŸSž9Iæ —–dŒ9޳qãF&Š”±-WO´‚ÎÒ×.q�°L›à.]:#bkk+ *VÒÙˆ&ð}_kT§ð ÿªu„>¦ `”ÍO öÂÐM±`ê!EÎ93„v§+V@Q5ƒÐ�šœ¤jA¿Òõ„LpíØ×Qå<T!B§ÉhQƒ2�Y6\ÔÉ0ã o_ è¶ð*£®êÛ§GY( ƒE /JÄ¢ô|Û´|%•RÊ— ŒX$Š PJgšþ�…RJÁ8 ãÔ„‹Ž³à‚)_Jü—s};%j#��h !;H(?ë0bgŒÑS O°Ê9³™y¥ã!QÉvd¥tC»ÿED…E/½”²ì�MC„¯ÒÑäïhE”Œdñ ÓV‹K¹°bÿYÉÈ„[9›_‰úEÚî°„Þ 2¨6Ê9iÅrm/\ý@@É ó+`§Ú.\ÑîÕ:­Ó:­Ó:­Ó:­ÓþïšÞéµmùÁšïû‰D"Nçr¹l6›J¥¢Ñ(U=hÞÔ‹Åb‘HÔ¶ý nܶíD"…ˆ‘HLJô¤4MÓ¶£…BÁâ%0 ‚ ¢½a7”RÊ1Åàa�� hªÏж-.¦í·mX}¥|¥"‘ˆ „ë ËdŒ™Â`‘(¦”Rù¾ã8i7Ïç=ÇUJ™‚Ù¶M„\D$x$!ÜB'×qæä§ï5_ ¥dÜð£ð ÎÑö[!3­aZß÷}T+"ÃX,&=��œs]IÎ)ÉB)Ïó„ !†Á"Dô}—sîH©Ó’/úÍ ä¨@>�Òé4Á0:- ¼ß™lk4ÅbÑh”À<a$!,¡Y±. ò£Ñ¨i”bÛ6É:Ò9©¸#}FD<i@¯–Ô`�� �IDAT a�¬BûðC/e˜DÏ+úFU(;†€=€mÞ•'g!ªBÑ+Y,š@D\ƒ: Z‰Å“ºà‚Î2 lLc®—=š0S†6Ã7^Sí×ð]‡ç€ç2\þ&<Ô¦à¾ïr�ô}Ò›¦iºnAAÕÛj^Z–ÅXDÎ Ú±H”J{2 �Bð€ŒÁ‡JJ)]¦Ln2Ž\€a›��Lañ‘ˆR*PNEmQ�` #íP¤'Å™ 7�UÌ& ÈιN¹…š)eØ6DáÑ(äú€•§~èÏ!^¨x–= ð„„Ò6*^ O¨dyœXGF§-Ò@Rñ`è‰Ç!zˆS¾?„üðe3*|­ŽºÚÑÕ7ÿ¹òfõø(¥$J�0™@(Y×<(žP¤¿Y Rª²€†Ø±þI§uZ§uZ§uZ§uÚÿ-C@ ŠyšÅÀ+:±©ÆAuu5m¼›šœ|žs^Ý¥+"š¦Ç%ª††úL.O™õ®ë’ó¹¾¾žK$n¾à+ÉBŽjªi§( "ä‘¶éŒ1Ž€Ä/P‚)¬¸×ûT€€J)†`pŒ«ž<9¥…‚q×uóÙ\Áu Ã"/nÑÏ©%Ù9˜¦i›¥âg³ÙB¡P[[µ#…BÔø©ÔíAË|¹ô¥Á™T TñWxÂ})c™|®Í¹Ò§DnÎ ŽJ¡ŸLÆS©"æ2ß÷™(²5�@±*”ÎHÏ/ìx2éùí+@TŠ3¦1/� H€¢˜y8=éRl…ïû™L&H))(�Ý{Û¶©†œišZw€ÜæžïC*‰gYV.—#m � ¼ zRÜÁ9Ϥ³žçUUU% :€€s¡ç¥+Qô§2"�gL1&�¨f�Å�‘#c’ø/¬H'•$B+UN@Ð ÄRТ4ç‚ÁÑ‚ÿœ )¥”¾‚��CX&—×áÀDá"‚„łsœëA·lŠ¢K\}›¬4Ý Lmh([Æk„Q(}££Êh@¦˜bÈH¦Q‡ 9çÀ2) ‰Æ¬ˆˆÊ/Æ} 50×õÁ“È<i0Îs§µµ•f C*x±Î %XHHõ$ Ë6,ÓV¶u³m›¦çyNÁEdB¤` ý?xÑOŠ1FìˆE•G`À€)@Á87J¥ X!(@<˜ X  BŠe”Aχ§JGÜMY²�”úóÃÏ+xâ‚,›‡,” SÁü0È[LBÅ1ˆä— ±­ @˜¹�àŒ•pvá™Ê[ÞÖ Œ(ÑU<*!ÂQ$Uë{èKŒ›08g¬ÈÜÂ@DÆÛÒ¯Út Š«¦È‹ ”t`‹ôJ§uZ§uZ§uZ§uÚÿïV”0 @0ÆО+„pœ|]]]&ŸkÉ´Pe2a‰x"‰D ÃäŽR Xu—n­­™õõ 555¦a��Gˆ˜JÕܸ‰sŽ�¾ò� ™Læ³9Çqª««óù<�’Û €ô¥*F8“#Ë9B°Jú:‘XL)?µ=%Ð2L˲|×CDé{ ”mY¦"C”žÇL!¤”™\.ÍšB�ç Pa˜†R 9#¸kY–ëº---DÝ›¶Åw}/NsÎMÛ2mËó<�0¹àœ ¥)€ù¾/]OÂ4M@©|×¶¢ x¡Pp<wÐÁv4*¥42iÆ8)¥'}å¹ ÁàÐÚÚšËdmÓŒE¢J)Ó4)  ¥¥Å4ÍX"aš¦ô|Çqb±�΄i�€DŸ‰ÆMæ*žã*d¦!¸RyQ*�L)Ò÷%�Ëà� =‡Ä"–òA0Á€¡B@0À`À˜d¶°AJd* ¢ˆ¦a1Æ<×7 ˦ïIÓ°�AúŠœ!¢ô•’ “É_‚4GŽ.Á ï½8G A±,B1)šŒ ¥|%  e¾TÌŠÅ …‚'•ié(‹Ú¾ï;NÁ²¬ÀSÍ9o ¿/á¡P§S Cä ¥Ð0 Î"ù9Q)àÜàx´àÀyÝQ"–É9§P|àL¢ $&¬¢x„ÊÔU‚RB†:;CÉCµƒ˜BO�ÀHI”²%Š} › ¢iZ>ú(AÆ�˜¯ÈXÄÚ¸©¹gïºt&ãùÒ´¢›š[¸a²¹d"á8€JÆn!—s;S M+Š�J¢B”€Ü0]_j°Èü &œ .•ò¥R ‘)åú†ÁÇ+ ãV$¦�‘1Ïõ•B!„”¼›“RšB³u|Ÿ1¾TJ“„$UÑaÎ�}ßç�‘*…‚1dL¢_�㌀ƩüµYV™¥BÿB~þ0ñ>L?G=ÇÂ9aä/‹õ)K#¥1 ‰úÒÇ#¢0x»=ׇi<O-ž¯�0� €!WÀ3Î9úTX”yÒ/’³iâ"M'À*�Ί‰!"(%KŸ*ªvAS”Ð+…`šç¡n#"¥a¸ànÞBpƽ‚‹Å y×W˜ªªjiiÉ{ùhÔv2­ñx((I¢išÜ°Òs¤+E7äû> ÕÎ0Q‘Æ CT*X’ÖiÖiÖiÖiÿ­¨:Ïh‹ˆ� ™B�ÆÐ`ŒE"Çs7mÚ”wÓ4«ªª,ÛFDÛŽº¾”Œs�‰ A .âÉ„a­­­©xBJ™Îeóù<ao. {÷Þù|¶¡¡!N'ã‰h4JÚ{$w6½6MÓ2MÆy”39çä@ÄbÉXÇI·´œG£Q(çS|;yíòù|kk+!O*Gm›â,ËJ$“œó\.×ÚÚjš&…ôkW-¡Žê.5�@šK|×åAõ8r¦‘¬Šª•R¨|@!„°¹Í #"×÷ò¶CŸÝQ*ß-7¸0…0¸ !}Ê¿¥LuÏó6mÚÔÜÜLÜ¡kâ’B{§íD\Jé’ú ã*¥8øôŒµ_Zc¨ÜÑV ;ÖÚuñ¡d‚ˆª4n_;`ÃgÐ'Ñ�‰»öT*¥ó,´k*’ˆ%ø‡1E§1c 8À™/E:SPŒoZ»F1<` ëUI¦Ü\kK!ï ƒ†Å˜@å¡}Èö—Á?È.ÐE9ç7Á+þ¢4ž*þdêÿ±÷a–UUÚk‡“nªÜÕ9“ºI‚$„!) ((¢b@ň£0£ŽÊŒbE (’snRÓ@Ó «»rÝpÒÞ{}?Ö½»Î½UÕq>ÔZO=õTÝ:aŸNí÷]k½‹sI]•͆Èz•³ÞÚ–NÎögK'¤ ÅAûW{}šº¼Y-?ëŸÐŸ€)c(š‡ s8rdšsn¸ÓÓ;spt´ÔÖµypý†­U¥Ò!aÓVHÒ ­äÎ+º~É¥xT@´3Ì�d¼Ž®2Î8r@c¨Àžm$"jDmL’ª–QÈ&YùBÇaRçÓ‚µelO¢Ö490^Ä.5Pý;küÌÇ»&vàÄa¦p˜ãù�û±ÃÚD7LáÄæ\�•µÇ´$ó[z &[wÙÖÚ㤘aÀ±«5>å2Uoac,&½ÁTíÏ^§è«ìéYb.ût#æºnœ±8NÀ áçòµZÌ…S(¶!jÇŒ1?$q’ª” ùH)8çL¸ÈµÖš×ïHe]´ÑZ9ŽÃ8Å$MÛ´MÛ´MÛ´MÛ´ý+[#°ßëÙìR)EIécccœóR[‰tæHÍ^3.2Î 3”‹íH×uÝJ­ªdtÓF£Tg{" äóÁºuë …œïû£££µZÍqœ8Ž;::jµd¶Èv{šêÔq¥5G¤÷D¥„üÓ4MÓ4NÓ‚.ø¾ï .ŠLÙ6­u­V³Ê‹A�€ïû¹\Ž1FÔˆRʪR’|’$´�úLÚÀ¹°ú‚s)@qá8ë±âZkÜ`=úÚm€d¸�êÆJ­JbøRJ.�¨Ô Q®ë:Ž  :j F#‚Àõ=­u5 “$!ç3F¥£ZX‹BÚ:û¾E‘ã8.BîyÑ„$h¸³NrhɯÎü`B3˜l€„rnø½9‚1 $Ãf˜ÝrA–1hv€[F�hì_í)ÆLl0ù]Ç·óh çœKÇc޳vÍúêÐ Š¢ÔVÈçŸzpݵp^wW»0P chL À×EЄÁl³³€' á�†KÉš»q\²¡6±XN%1Xâ#K¦ŒÃ׿ì•[~µ^Yë9'YMšÌ–Z¢¼Ûì–DZÓº”8c °W4>R ¹R&TÉX¥¶pђѱjÐÞsÈÑvwwÏœ9³<2Š«cå=862ZêêPi¬ÓH§ a£ò„L…¸õ{Q³¡!>’¢DÆa&Â5dA¬t"=¾ã8¤ëÙ2«[ºÑâö–š&íe2²"Ï ‰íe[ÖQvzOd…¦ºŽmÍÕj¹r¶UÛ€â-|½»¸px #@mk¤lŒ¿�õÔ S=‹½B–¨²ý0ñ",Sà£~|c&X)!%ç<ŸËwttÔ¢8J”1è8cÒ RF«Ô�r@n´IcLR¿¯6B2e´1)”œKǾ�QQ­dS>ãD»ûŽ[_øÁÓö2Ú+³ç_™­š¶W¾MÏœiû§´¬‰ýÕÚ¿·cXkŽlÝ$ÁŒ\. ‘ËåŠÅb¬RÚŸcZk.Ñ0Mù½ëpyþˆGú^>ŸwuZ#'¿çy¾çK)Ã0Œã8»©ÍîßB¤Q«Ôçõ­*ñ˜2:C•$ŽçåƒÀuݱ±1*4€ˆù|žØr3¶··C#صV«À… È'iŒ ‚€Ž´TUm RÃñ<Îy”Ä‚qhlXëÅG¥c•rS—¦«{ � CÎÀ¤J÷oÙR©T|ß×hhœ†)"ºÒóƒ ªUqÎuFO§*Q©Ð’KY(”RQ'I¢’”sîû~¡PÈår‰JÇ·6lʽƒÐP§¶z œsÚðOD2“†´�Œ:Œ�ñôB³ãwÒé• ;@^M’Ð'Õºc²ºôãŽÐzd?A& ôè:NÐ×?¢´sð‘ÇKËöܽZ«­^¹r°˽÷ßÝÑÑá:FΘÍ€sé9: ³@tRIJ¡8ßRÇ6?þ³Íça-ÞžÞ‚î&½5dP"Ëè:ŽS«Õ¨ž¥¥*²‘#“ÈL¸U¶‡æ“z·�gBòN+/W\°döŒžY§¾ým çÏ CVÂõÏ­Kuÿ}÷Í’XyŽÃ5ŒyÆ(�@¨+CdX’¦wÛNúN°?K'ÑaZ H"4iÂØ>¡ïÄ#0Vç°²,L ƒZ¬‰5hæ¹&ž5Õu²G¼Í7Û& g5ñ&Ðü¶l9wegHK?4Ï[û `êŠ “ÞcôÄÄ¿šæâ¸Ð¼ Ød| :)ƒRÉX.ë„‚”’Õ‹/ ©ŒàB8€”’¦€1�Θ0ZCÆ. Œ’8M’$MŠ…4ˆSöàdöšý_ûbŸ¶—Çî¾ãÖW`Ï¿2[5m¯|›ž9ÓöOiÿXû«µÛ¶þüÇ¿ý"SÀA��é9•¦£-WǵZM‘+ p)j¥‘1Æ€3�H•ÖZ §i\jo«Œ•ŸZýôœ9sæÌ™G5GÈ-[7“Ä�ÜRX¾äCl’ËÊ9&¸Rʤš²¸)ã€2|×£Oâ0TQ=Ò˜ÆR•¢6®ç9B‚AÏw9°8ŽkiJ(”sN{@ÕØ.»¾ïyžçy¢Qèž kªµ”R#:œ× RÕÀ$¥»SÁ¶z±xRXäL:Ž”2¬Ö�@0É¥0€RbX­c¢(Bxžç8Ž+’…3©ª£e@%Iªµç8Œ1.ëÕ@k)¥t‚ëcâ0 «ÕÑáa×÷‹Åb!—§­³ßîi­ušZI9F¸‚”Ø9GDÓ,ÈçööœsCš–Œq`@Ù%Æ@#f›2Šë‰Éˆ`íñ€Éf[L²cmþIŽ2äl ¼,MЫès. §Ik ßcϽþ·#O׉ NaëPå’K.Yõô“¾$q-QiÎw]ß3ƤJѳX8š½òDâ€M–Q$o<),Œ?0)hIS‡fÁ‰àÓ6c*ÜÅšåâ,)FF+ÂÖãh„¦Qœ2;" ëë‘îN5Ài0!ŸK¢ä‘ŒËÕÓÿG7ðwXºÃæ}}›6æ|—1L ̾Jâ:ÄL6DÕ½ÄÙ\ý:üÃfo³%8X³N„Å„LaW6òƒ°UD¬Aýa3’ÍO=>Zº·åçl{²GÖ/ò|^ô–Q˜Ø˜mÿZOÝgãÑø–õ€)‚_&5{|v¶�òšÀ¯Y#·gªëocºÚì}§Š&€Ìz±œˆº¤¶È9—Rh a–ûú 〈Fc�Œ”R0éH8çÂõ'æÜ‘çLJGëÔ4F1Î$7 ¸pŠ¡@6UÉ´MÛ´MÛ´MÛ´MÛ¿„m#ÚUrÎÁ*õA€ÝŽWò”1:UÀ•WGw—�Æs<7 Ãõë×/^´ÀxîÈè“°V£Í=)HÙ¤E>ˆÆcL£É¦õZZ’® îR¾@>Ÿ/•J�Ð××GÕ�@)U«Õ”RÕj�¨<©ÇQ)uª}èû>¡Š2àœ7ÃX.—ãœSxB’$žçI)“$!`£”2€RJîh(óSØ<c¬~5ÕJ=ß ’‹°VCÄHÊ\.øy)„R*ÁÄó»w'Ö(è8ŽŸËQ³¡!ôíKÇ•’Ê ŽŒ(cl’6�Æ<Ïc.(¥ŒÒˆÅ!¹j…¢¡rg•[Á gZkck4ïÔ³˜ÙÜĽ>ŽG³ÿj¯Ã›•Þí•i ì4@[f2CCØgΩN[=Šž1Ø0ˆè»Nß–Í·Þv㞯޽«­ä‰'FFµÒ±è2©Á(­CÔ)äÄÞh”³Ïh‘ªýœ€¨Rª.²6¥ÄÖêƒ-A6Õ|bê·ÅW¼±Ð¨—¨JaÛHÂÏq)NÈ ÉÞßöVH㌥i‚ÚHÎGúsù¢ïÈÛ¯ÿ3�`ëV¯®Ž‚JzçÎéêêŠâèjPZ+cãØ  cŒÐÌ€ÿñL ;¯¬eÓ ²ý£H·R:×Jâٜ̄«¾´×´ãØb,C]Ù.¢ñͰmÏš8^-㘠�F¥ læË`2*¡ñÃ8#M™ôømÌÉ,µÊ¼�TŒ™H3ÕOa¶çí¯-íÏi¡e½`£z(LÆ„”¾ç%*Õq,\O)¥UbêóŸ±h¼Òi(¥8gqªmP•Ã8we 8£šACu3_ÌóMÛ´MÛ´MÛ´MÛ´ýSc ¦È3•� ”ò< á9Žëû„)á™jÁq�c1dˆ‚¡,Žbß÷ –Ïœ9cË–-Ö­|w§í· kµ¾›±X,%á€:ÆC´€`ªÂq\Ú:ÜÑäÞ×Ú(­µ¡�Tép€ÄÌÈçOéÍRJÏóÒ4¥}Øk­µÑ~.çú^Ö-iŒa‚sΙà*M±…®ïÕ¢K‘ËåR­¢k­ÑqÏÕQ$gŒ@œ1QGE @'©ã8X’(?È ©u[±D…G‡G*¢œËåòù|¡PHÓXfÅÃ{ ¤_¯g(¾�T;ŽÓÝÙ%Y­VGGG£$a /·pJXHã„¶]ßµáèÐìT� ¯àãwcêp¼¾SçäGߨ{ _¢iÅØÏ7íZC‹ HXyër$jƒ$ñ÷…̉ÔQŠ1‚<ÅXÌåÊU+û·n8úºkwßçÕkÖ¬m+U §è¹Ò…$‰”N<é†W‘¢ÙVM„Ù6‰£~Ó4Å©Â(˜É>N–%a™ÄIïÒÒiØì‰µl…ÖÚó<l¤0LJýdñ­eêa8õôa?ƒÍ–‚¡É¹~Çc#}sç-k+x]{í&„ ã¨V©ÎŸÙé:Ž1Zr#g�4Êè”õDÍ9ÇFÎ�ÉÚUaÕõ1¥Þà6û ¾^4]¦ñÚR “R’F‘e–l¢K©F>ƒ›Wl!M¨ß4Iö3fûÑdU šGxs½…hùnÇ´e˜²ôÁÄϧÊèiâ8`r >•Æuml°2ãi¦B�M¦b‚¦wÝÔÏ;ugŒ_¿e¶OÕÐ<Y#ŒÊˆ¢1ˆèºn Ê3Èéš®ÖZ©¤^êÂhÁˆLä �SʤZ;®Ç‰ä:€&I’8MÑ$‚I*3U2Þ´MÛ´MÛ´MÛ´MÛ¿‚±©}&’àYEuð†TNÜqœ ”Ö A0.cÀPžã&QÜÖÖV­Vû6m^°`Á왳žX¹rÅC•J%]­VÇ) ärÇŒ{™’óÛJ¥D¥\J�«UDä W¡ëºÌбOå\×­×,tÏó’$©V«¹\Ά�óÜS(\ßcB)`¿)µ›Hh¤Çû¾oŒ ‚ Þ B@’$RJ×w(•ÖÆ×us¹œëº ¡2V�GJÇqÀ £œp­kµÈ÷ýöövD ð\.GÕšø…R>5õ óÐ�J© ¨UÄb¸® �`вR©TÆÊ”‰@"‹�@Gj­yéTA*=(]EQEÔ ¶8¤õÉ@8’Ûtq)Èg–õö³†!¶n«'źֲòv.R{ A @ c#9¦ÆÝòÖ÷H‘&¦^ôQ»ì¼xÝúM®‹+ºÛÏå!.ëfÍìÌ„áˆÜt÷<'ª)Éy ø³-Ïú“í³S?Ó$±Pת˜Ì,̳ó 2ÜÁD0Ù¤@"fµ½äº.1,6ÊÆdJXRÀžKÞlž‘Á£°v›dMÔ«H(ÆMwwGß–~KU5Ij\:‚ñBÑó¤S©T<Wz¾ +_ø�Ð nYGÜãŽâÆâŒ1ÁÇë_fs"¨ŠEõv,z{gT*•0 cù|^A2(«Èq"±‘Í®’$Ë `cn7ÅM4ÏϬÌAË|ÎLû)SlìäÁLIK;W'æƒdçsË÷ÌWþ·Í³“] RÈIÛCa&ÙTŽìü¬ÿ ãw4/R›`*k™ç¶ÓpŠë·Œ£Y’i³ëºŒó°ZƒÚhžh…Æx¾ÏX½à"e0áø{Ì‘B*¥MUkµ(ŠU’r®çµ• ¥B%hd¬‘p3mÓ6mÓ6mÓ6mÓö¯iŒ±©¶C’ˆ¹”ZëZ!“c"Õõ]»”RˆñMc>ŸGm¢°*8¾ŸÄq±X\¶ÃŽk׬ê`Œù¾/¹ÈårT°€ˆâX£M6³:ISÂ]Œ16®ëJ)ÒZkÐÆ7Æ!}ß�DŒã�‚ R£R©@¦Â_EM-Š4IE  ùSŠAÇTˆX Šd&´_‹kIù<gŒŠ P‚p$ç<NSQ8ŽJ`ÆqpLÒˆsHÓ8*g¨Ó¸V6A´K:—¯V*å‘Ñ0®i­óù|¤zär9¥%z„aÈÏår¨M­V ŒB­Q©â”sÎ’`q#!çÒõÇqâ41 |ß§Š�àyclddÄmì¤Q[l`  ¶áôžð=Ï£Ð?—³”Ó¡•r¥d 8MØ,‹šqNÆ4¼ñ-HxÒuƘAc'†1Æ -¤§€MÓT!,‰FÅaÚÛ[äÂÑŒk4ÐSP%œ“Œ Î!#höÀgA²f�ëX­ûäœ3š$t°• à|<*DrQÎ@àÀŒÒBÁEg$@½8)­5ûÈ–Jˆ{[ ¢·lˆ¾=€0áÒLÃ8Œ±†Ô%�ü[ÅÆ.ëBq:U†£ëJá#d¤"/2�Á K©‘*„Ç€™0­ _*¦9I¢ õÆ!ˆŒ°p×Î&Æsj0CÝ.¥2º§Ã9p†  F322B/¥-R›²NÑ”ˆA! ¯f£iè¾uAÖ ó0w…å–0 ©Ë{Z­DÈ Y‚ÆÞ2\@ËÝ¡㡟­L¦1ÚfXØíÁ-\†%SZÖ ÉÙT;‘ljƒ4N�¨Ñu]Š%Ž€zàÌd¢6ô Ä·ZU{;j­-U@?C3ƒ@/yÛÏ-둆a>Ÿ¢ˆ3ÞÞÑ^«0Íâ8ÀPr¥� wfš¦Ð ¼4UÔ<mK'®ÔÆÊaœª\®4cn—A½fÍÓµjUΓmmm¾ŸÄZ­2ñ§mÚ¦mÚ¦mÚ¦mÚþE¬î›Ì„—Z�é:ޤ XGk õœtµF¤IG&5~Àyel, CÏs ¹<"¦µ¨–/ !ºÚ;jµZEišjPi?iË*•JªUJáR:Ž#$GÄJ¥â8C ­¹”R¡ÑZGI,ֲŧݼMOã8N麈ÇqµZ¥¬cLµZõ}Ÿ˜I€€t À( �„ Á˜DkÍ”RŠª�€Â¦tzhì¼…ë‰ã8ÔIœ$Iâ:N.—ëììì9sý¦õ ‘‚#8çù|Œ©V«Œ±B¡P,£$‰ã8MS"YjåÝ7Èår¹œ¬V«DˆH)½ÀBÄa”¦JiÍ P¥ Ú={ž—-2ßbŒ1Ý7þØ�� �IDATè:�ˆã˜âJ¥R¥QÀ’f nDI"ÐX%Ëx¹-ì‡ Ùõ“NF[eqTÆÇ8îxog5˜92#(c@Bî(¥o¤N0¨®DÇŽ#ˆ„"”EÎyëÍÎ’Œ1Çq!“,`Æ2êúäN¯ÃÅL: !(¢¶Ñ-^V “&r+ÿgÙ™çéO˜Ü‹['ŒRBÆ™�Ƹ1†#£žÁrD4Po�€AàuéÊV�&,1WÏ«¯gÄ4IàO‰lBÿd­²ã �ÕjÕr7¬¡B’%¤Jƒ¦.çœ5÷3kvÔ·LÑmŒBvÛÞ΢ôdŽÍá�ÖZ Û�ûú²‡aœÙËN|Š–¿26~{‹IG<{L=¶ÂdªŠˆú5µÖÆ>#çT®¥å ö“–h#Ä2“6)ËÀ„ì lPÀ$mÃ�*åJ’ÄœqÇq h�Йû€#DœÄžër!]ßó7nÞRÈ—vß}ïý8pÙ²eQÅqˆJ=üÈC7üõz@E!,•Ú·ÑWÓ6mÓ6mÓ6mÓ6mÿ܆ˆÙmšÝÐ2�)ɤˆâ8Ñ ­D\cÓŒyžW*;:ÛêéýIªµ‚Q2<ð<::62"¥”’¬•R2`Æ:Œ`ØDKÓ” žß÷ ÇÕZEäߦ�qîHR»ÖZ·çÛwi5îÍv„TJqÆÀ Ö �r~ „`‚ G¦ZÛâˆuå6`Q-DDÁ8C@ƒiœT“$¬ÖbE’ Ïó80!!eX†çÜ“Òó<ŸÆ …†#j†¡b‚Ûšmœó$Iâ8¦&ûA0oÞ¼J¥R­V+ce£”ïû9?ð×300022ÒÖÑAµZJÓ´½Ø^,•ˆwزeK%¬yž—/Çq]×÷}Ƙ`¼R©$iÊÁ¤¥uo¤xDQäû>E³ ÑË®”6.À¤*#JOÈù¾ã81Ic UdLÓÓ„r@¬ dÖˆ·…Y¬ ÐÄQeO¤O8«—:@@„zå3h‘À¬¯‘À ­‘Ê2&çŽÈQ 2Ô Q%iÍCD ‘•´°˜G7êRòz-ƒ Ž%òeìo7W(ÜöÊ´0 e'„Ð쬞k³]:Îw aŒ¡àŒ1 %ƒÈ83h´Ö`!RÄÔ€J#ãh€1É‘ãÆÌxó 3 H4��®—‚´0M}¼,+‰­Â²¦–·0 Yä ™7”ã8´Žˆù†")cŒf&|SຮšBqÏvl–)È6£¥=ÙÎç™Ú-íÏ6qÔ²§ÛAÉ^!ûà8虬UMK‰1ØÚæI§‡5¢Æêk ˜]€rþmúOêO!O`Û–=Š6,“5™;‘,ȆÏPK8�"º®K5wòù¼çºiª<×CÄJXm<©AÎldcÌu\×ó\Ç_·ac{{ç¢Å‹¶nÁ×oX?³wöü…s‡žzê©-[ús¹ÂÖ­}===QVªÓÑÓ6mÓ6mÓ6mÓ6m�õMæøÞO…¼ã8©ëzZ›f0�èhïªÕj£å±-ý[Ñ¡ê‰\Äq˜ÏçcJ)GÈîÎ.DDB0Â`ˆ¤ ¥Œ¢h’¦�äóùT+¥T¹\n8˜�(%•10õm« §(�rÚ¸\j¡ô ÊèT+ày^¡P�€$I\×mkk€ ²1É9"æü€.kŒ1J£®çðFƒhÌèèhelL)Ååå½@ÃLÀ&ŸÏ§iVkqœV«Õ0 G!DWWW[[[yt”HzÀ¶b)—Ë%*ݼqc-ŠÚÛÛçÌ™S+×¢(IæŽì*tAàû~6Úõ½œ1QÇ:MSR7°!Ó6 < Ì8ço”Žô}_¡R522B=æx±“¨¤çy©Ñt¢ëºôŒt—0 [€ L†|²Íò öó&”ù!‹½³Æ¹0¹�Î9 wîh#�Cn�lr gÂqYšÆÔí4mêé-TîiV ˆšñÖ¨r‚úŒhDd0Æ’(δm’â|“¬‚ÉÜÚ˜±,™’í„–NnaÅ5’&êò$õç2†*^rl\ÁÐ Ñ�Öén2õ ëeæoHT¤Z;®`œ½"¥´ÑØ0Ì>Ú Ï·Ž$lÎ$r‡gä!­;›ãЂÛ'rÙOZÀ6bÝ“=ÌÊd»:Ë#dÿÚ2oÇ —L;i€g‘éýc/kFZúgª~›Êl> cL0n»×ÒW$¡õ¢‰“ÑR¬™ì³?Û"&öIíSdyúÕj|ŒÇV4xcL­Vãœ3`ÚhßñjaMHåi6(J�£0ðó[úúÓTÏš3YùøªM›·<òØcÂótô-P \9cfoš$Ãý[{ft/]ºtÁüy}}[¶Ñ]ÓöazlMÿX[ïÜ.†Õò³ë̬ÛümÆ-ýÝL=yÕ7ÿœ?á¤yýƒ]{/ïmzO¨òæAœÙ[š´etâ[?xø¼WЍ¦ªô èž™m6X¬úà/.~`á{Ï8 ûÿOßN´ttÓ°˜5£ðwk{nÅj½ý«¿Ô[`<ÔWfu/o»¦mÚþ ì¾:â­+êïÚ{y/ÿ›ß_{QÕ¨¦4Æ&É1¥ /W*ýq’DqL_aÙ¯±ZÅphëê\¼téNË—/Z²¤£»CºBº2ˆÓ4Œã(‰Ã8ªÔÊ££ÃQÅ*¥DbcQQA„I-MSÔ ¢®crÎ9 ï`�•FIœ¨T£*—Ë”Éïº.*sA[[[wwwoooggg>Ÿw]·££££­­T(xŽÆ„Õêðà`ÿ–- Q§i†Q­–DÈù~¡PÈùA1_ð]O0.ç M½ž|cÇ/¥ŒY'¼Ûðm’>I¢ry´\­ŽU*Õj•"#8J¾èÛ´yxpÐ(•‚B./ש2J;B2ÆÊåòÈÈH±XÜyÙ²ž®®M6T*•ÑÑÑr­*„èîîž9sf¡PÐ 'Õ:QÊ��g\Š|>ßÛÛÛÓÓ“$‰RJ] kä*‡Œ¦d`’I3È80Ïq …B adddxx8¬V%ç‰IgžäK¥R[[[¡P ¢e•J%›mñ€™Âµ ͺ�Y#J‚téê‘Ø¨µQ\0ÆÁ�CúbœŠHDÁ5ç …¸®/„†q& m9&Dl´+ƒ !†Z­622BÂx-üÅ8,GÈ-"¥ÉFuC)r„Œˆ!ß÷©$'4GÑÿíÆ&äqXÌŸÅ·-Ä"2ƒ`?çÀ(Œ‚;Òþ•sÎ�À0®(�L3L�«Sÿb`!gX/@@ƒeû 2F+7Œ` аìÏ€œàL2õÞÁ™ä¢žÙD›ÇA8ÓÊõ;i¹•Òí´|‘,ýÉþ¬Ñ k¥f&#ûƒ]-–í>™h™ëm zVRøàŒ ú¢_'~a#Ž€B °¡¿P_whh( ›d³Ïh;™7ŒÆ…V‘¼öÃm¼r¹ñž´”làF¹\ö}¿»»»X,S@—£Y¹`¨´NÒ(ŠjµZ>—äÑG:º:½ |¿­½=IÓ‹l·ÓŽ‹–.êž9£cV¯— (‡eÏ}öÃð®»îÚ¼y“#_)¨lÚ^²©­×œýÛß=l� rý|áÉÁÿ_”ñíÿyη ŸýÚ1‡ñÎ�ÔC¾zæ~=¦A=xÞk¶ÿÀkÛ:1íx©³QÅñ䢺/Õôc_>p»÷ünl¼'GþôµþìiÙöRsÅ/Ïv¶aéÍÝu·OÞ’¼¬m2ºò̃Þó«u/¹ÝñUïXxÐWž<ãvÚ¦í_Ú^á«#}à¢úküo~lÃpðÊ·Í[ðΫG^úÿ,zYZƒÆ~¯e×Ç{zzººº‚ Èår¹BÞÏžçyž—ËåòÅBPÈsGFI<46Ò?<842<:66V­8¾EQœ&Žã0!Ò4&‚r­Jžêÿår9 ™&y:€8Žcù|¾£££½½=ŸÏÓñ„=꘼‘�O€¡žs ”OB ÷÷÷ ŒŽŽ’4:…¿Fµ0MS¢*8ç½½½sæÌikk«7�‘²ÓÃ0$@^+‡aHµ ™AÂöžç1ƒ�€Ú��Õ(‹]]]¬Qò¢²Þ?ºoÇZzÑÆ˜|>¯’tëÖ­[6÷ŽŽ¥]×%¥®j¹¢µÎ9¬¿¿¿¯¯/Š"¥T[[Û‚ fÍšÅ/—Ëa“Š•¢  †Q!Æ •ykévš)@¾YÐ&Žã4NcŽã” E­õèèèØØÝ‹èa£(*—Ëô×Z­Fƒ˜Å YÄh§à¤Ó`"iZöÊô¡Z iäFC=£êrŒ\�c`¸ÑL#¦•Á45u.€¢!<0…ïyž-‘l4¸V1>ÛKg¿`y1ºélµôÀÄ_'öž ó™ô¬lgNçÔiÙ¾¥H"Ú A(!€1àäÎf†ˆA‚¥™6ÌЗæu Ð�Œ “)l‘má $JXƒŒ³ÏB­âº 6Ž &C/ ë~Ï“=½¥ ¶{³ÔIK§ÙMC°mVÿdWËž•Ç–ÉcçUö‹Õ³×lé™–OwyAlˆµÅÁ–i?)׳ífئÒˆ©¡å`¯–}Ø–+Û‰MoZJ¤ ‚€H^×u©ç}ߣ,.ÄUJÑKl!Ueqç]vÙÿ€ý9çù\îƒÚyçå;ì°ýn»îúšý÷9ú˜£N=õ-§¿ó´7¾ñÈ}öyõ 'œÐV*ärA.7y6ܶLmüëEoßIgà·ÍÝóįÜ2P˜ôÙß~ìÐ¥A¾w·¿~ÇP¦óªÿ× :Oý}§Ï^óé7,Ÿ‘ó ³_uÂ…7l™ø¦0[nøÂ1Ëzr¹®úìu›šÐÏ]vò|oá‡o¸Ã Wþì½û/(Źû¼ó¿®ÚOñ¡C¶ë ü¶ù¯9í{÷ŽN2e’µ¿>cYû?ÚJùù1AÓ” ŽýÅè„“ô–ë?±w׫¿ô„-$R}ì§g´¸#:–tæÏoAÅSuÝ$ øG´ð¶+®NŽ>ùàqwàiïyÛ³9À¶ÖiýÄC /鶵߼uÆž<üòò-ÍÅ¡ë.¿¾ç„“ör^ÊÅ̆>ãðþ]öÙ¯l{þ·óó¬M³å¦/Ÿ´Ç삟›±ìÈOþîÙÖ%ÿ7žž\wÆ ÑXãÞÁß[o�ª\òîCvêÍ{~ÇÂ}NýÖ]Ãÿ¸+rÚ^Ñö29Ðþ‘w~ãg¼aYþ•§`Ð@f' ­Ó$ÕJ8ÒñhcæJב®ãxn.—ëéééìê*´•ÜÀ—žëç‚Îî®9s繜ã{A!Ÿ+ä h…9KT=6¸…C£##c£a1Æ\×%7]ýÆ0^ž;—ËgQŽŽŽ–ÇjQXßôKõúØ…jäw� ÆhÚÑÑáû>íSÉ¿DEcÙkµ±�óù|>Ÿ'8M2 u£ *­ÐP‚‰¥8IÓ4ŽãJ¥2:<262GQÚA, A>W,{zz …‚½¸ïçÇÎ4ªz€´A £p…äQµÖÓÓC•”R###›6lŒÃhVïLâMLCqÈq-ðÆ5L)E¾ë¹Òñ·T(Zü“{ÓøÙÖ#$–$‰JRrŒSµ¹Áááþþ~¢ªÕêààààààÐÐÐððp¹\&ÿa{{{ðØ»°æäjh -ŽV;Ö–k±¿ÖÙ"d43wa� jct’&I”ªP™¸U⤪Ó(M£DGqZ‹ÒZU� õ•þ° )Ð@¼&[) ÛZ hì!cÈÒŽãÁ Í.š™AAS-ˬ϶e¤&~Ѹl'·@ë,ÓA·VhHz“6öt –Ç̤ Rf”ÐJh#Qq@A¢TÒ¤\)¡é»*å©p¸­t#P…3*X Á†¯ž¾Sý‚l²?g"²b™—$Iˆ5£µÐÂÔ'“-e¯ÖÒ¥Ù~k¡&e ìä´]Š“…ÉLz)ld.d™‚ì|Àf‚–kÚDZ�»¥ãd¹�¯Ód6X6£ÑR‘�4 ™â6%èøloª'ªÎö˜¥ &¾è­Bof"U‰p]·R©Œ UkU¥tÅ�ày/ðëÿ¨\'üR¾P*•|ß;ñÄÛ ¥Y=3öÛwßåË——J%@=sVw®à• 9Gòçž{fýº5qXMãZX+ I†áîÝ© G¾þáîwý÷­=ü‡s—ÜÿùÓλ9�ýäwÞvúo{>}ýÊ—?ð•“Îþí à裿ú̱{òéûÇߨæ™ïŸ~ÚÕsοcÓàêß¼µüSϹ¦›M¿xß[~¨ÞuÕ£ýþ,÷g§¼ëGÏwríÞ Núäí©3ÉðDwŸÒ‡îÚíw®ºç?÷}䜓>{K�Ô_<ùƒ÷îýƒ[ûýñÁO{ÊîhrÁÖV_ûÅSöÙ÷ýW­ç“Ûßzyß0ÙЪKŸ=ï¸S)fOJ7Üôí÷¸ç©?z²2>ã’»Ï?ñÃ÷îùÝû¶>zé!+Ïyóywe)ê)ºnÒüZõæË¯…7ž|Pnü#Ö}Ї.øÀþÛÞNrâ‹1L“ôï¼áÆ?\~ãìNÜ}ò¬Ïk&‰Õ4$˜ÄžgmbÿågüCøÀuÏ ®ÿëGK—Ÿ~æÏ6š—ït�])§|óéj†a8ö—3çq€Üü½N<÷ò»Ÿ^óøuŸÛþžÏœýÃU“—ž¶i›¶¿ÕÜOþüœ°ÝK¢__V#õjlÞÃÁCR§Ë†ÂÚÜT&Åèèh†‘5ð†Ö¨Ñ0&ò…BgWOPÈ»ßÞÝ5£wV¡TÌJ¥¶¶öŽ®ŽŽŽ|±Mz®–¨Ô�7 qlúÎÃ4IÓT ¼¾›Lµ 㨮R¦Ò8MhÇI;ì(‰IÍNRY!‘Dõè0·´…¥ðT¥CpÇ(=444:<‚ˆµJµ\.—GÇ*• å“{Ž›êÿ¦é^t)¢(´Ösîé9®#ê»jf¤…PJ nÚ´©¿¿_§©NÓ¸a”‚KŽ}BûÔlh€ÑÑQê*ÓØ=£ÇÏýƒµZmlllx`p```°`ppph```ëÖ¨–GÇF)/�XàyŽ˜ëºFiÇqÂ0¬Õ"J˜J?’1Æ™h•¦©pëó„{Ó4¥ê A(¥Â0¬V«åÑQŠ>ð}?—ËÑe“$©ÕjÔá-Û6‹1²0 3R‚u3uX’SOm Ç\ƒVJé4NMŠªŠœÄ&}×÷=ד®'MCRžæ …™”Ëe {,S@C£MÚ’A·Ÿ@¿ƒŠÊ A®Qëoât&뇉¿f»Å¶¹…R) cö‚ã¹è¦I〮CqÙ)£(òœ±ñ{­5r†œ1Á‘f �h(bRÆýùˆ„i9@“£{Ûðµ…&ÈNzrDCòƒú3™v8²ÀÕvTöÍf/Þ‚W[ú­¥W'bo6…³½åa'»–q´XšD6.C0‰˜bKç<e–(±7ÝFWÛÄFo¹,bÛ?é3fÙ™–§ b”¢T² ŠMˆA SlÆUI 9EQ’$Ää…¡. žçJép»~£(J£¸V­ >ôÐý÷ÝwÏ#=Ô·qãSO>ZUF†6­{v¨Ked¨¿oS‡€zKßæ¾Í›|×ÙºeÓúçžÝv_Mòø]G]tÙ·Þ}ÈÎKv8àŒ;»ÿéÕ#ê±_ýü¡Ý?ò•wîµh»×ê¼Sœk~òûÐ×TøÚM—>kœ+TO?úD°ÿñoØ®½0sŸSŽ]^Y³f°iÈÌÆßþô¯½ï¾ð#.Y¼ï.|ïÂÛ~òë5õ—HåöóÞ÷ëÝ¿ûµ£K›–Üù?¿Øxا/<açËŽ=ÿ3G \öÓ›C€ê®ýú7¿n~±8ÿà·¾`ë3k›È¶õ™­;~úO¿ÿØNèäÚÚÛÛÛÛÛù}ߺàö}¾ô­“fsЫÿóßf.xûoØÈ3ë{ÞsåM_Þϵçègo¹yÝòSÎ:rQ[qîAç|øÐ-Wþï})n½â”y³Žºd™¼ë¦hÀ–ÞsÛ§^ÿÃÓ÷úÞG]ù“ëF¶ñjÅ‘MøôÚï{ï8èç_ºèÉM1àÈ_ßïÒŸÝo��’õ—ö_?¸E�¨M¿8ú’ï^ÿ·Å¯—o¼üÎ1'Í´5Ï^üÚâAß©»ÑÓû¾zè’ßo[¸ß»~ø  è?1]óë²´ÓwüŽ…G}÷‰è®o—ýÙaÿOßPÚéÓ÷©Ú£?~×¾óÛ<7ß½ÝÛ~I&jå—örcÞë¿¿Ñ�„O\öáÃw™Sʵ/zí.{2�ì¿ú#ì4·3ç:AÏòã>qÁGÝc~[ëÞá¨ón触¨×}þ¸]gæ½üÌÝϸbýxßâ–k/¿eÁ›OÜUBËÝËמÞÛyò¯Ç��@?ýµ×øæ=r×7NzÕì‚ëzw>ûO¤šÜö‘E‚1œte €£÷ÿðŒ×íØ[Ìuïpħ~¿A€~æg§ï³ý¬öÀqó³ö<õ³çŸyø.³‹A~æn'~ûÞ251~ú?úo;t^iÞk>þç‰~núó¹‡ï¾ 3p½Òâ\Nz#Ó÷—Ͻ¬'çzmsöýÂÝ)�`ù¡½ïuK:¿}Ñgüðf¯|zçÇ–æ»d}X»ö3æ¾ï/¤Ï]{îq{.èÈçî}Ú¤&byÅÞsà¢vßïXüº/Ýó<!Õϳ6õ³>¾êØwíÊwíúÖö6kW7E½”ÓÇ×#�Ž `gooÎ÷}ß÷�Àzö<âõ»-êíÈ{X×’%Ó™YÓö"­zù Ååç> ��̺ïT|íÅÏš©V‡zø;oÚcñŒ‚çøí ÷yë—®ßôò†F=¿E7œ³Û쎜çw,~íY—¯ÞƪÅÛ¿qê>óK~нÃa¾lU8øócJ ξ%�ˆo8sîŒw\�¤·tI×[®Ëž>áíüå½³f¿ïú ºå¼CvYØ]ð¯céagŸÿé“÷]Ü™ Ú½î#¿]§�û¯ýè;Íi\7ß»ìð³þhe[»êkã( ~üF4BP $ra7Œƒ d¢Mµ ¨A§50CÒe€\:Q’&©öƒ¢ãæ4ã~¡ØÖÑŠ®Ÿóò…|[{±£3ßÖK¹R[¾­=WjóòäÀóPHÙÔ €PÆhD€ŒÅiªX ηŽJÂ*ð›2š‚í9ç®À `HbÀ‚QZ0žržãª$Õ©"\-7JGµ$8ç:I b½¦ƒ ªiš `Œ±8ŒT’:B"¢7ÈŒÆD1_j+¶Kî8B2*Ö¹�úÕæë2Æ\×Õ€‰VÒs=/�àX.Wèèìnïì.¶uJíÖýÈ ¢Òê¤VGÆÂòXÿæM£ýý•‘á¾ÍayÌa�JÇaÄA…F¡#ÜÊX Ó©aÈ%w8ÔÀ�)cÁgRÔ¢°\­Pg֢Рü0 Ëå2ð]—!2DÊL±‰ÇÖ¬àxk›EÎmØvvBBÃÓhÓû9çÀ™p$Å’hT©VÊh–*­´!:‰qÁ…Æ ‚2ZºŽÌwe±”÷}פJ)*“*ˆ’4Œ¥Qé )—\Æ%®t|×£° É…ä‚ Ïq]éÆQš'F§œ¡ PGaµ2V®–+i¬tjŒB•h•hÔ@_Œ Î¥Ž¥ˆ3&„p(|žsɘ@d¤kÁ¹ƒ4?ÉÍK?«$¥ùL}bØŒqÇñ„p�¸MG§Ÿ)`bjº�æ é;®ä‚@80"¹ °=žw`P2!40…\s¡¸ÐŒk.Q: …á C&™LpÆ� I •NRb$šˆV¯m'ý‰  ã¹€‰?TÀ 0ƒ .@0®’4MS›b@äZ£Ðë8GÆt¦2¥Þ´àp;'lj�ƒô  @a”¦¾¢î¢z(4@-¤ d ·ÅØ6^`bèÀ8Uk³HìÚAD�#%'²€æ�kT `Œe…X`¯ŒQˆš¾�ŒýÙ~Ñ1Æ(Ñào8�ÍŠ¢ŠãPë´~ŽÒÀ1Q±]ãŒ1Ë—a#¹�‰® Ã0ŽãR©$¥TJù¾oSÆ(CŠªÏZQ z"Çq¢$Lu‚ÌÐʤʤ ÀñéI7pµN9΀1ô\§VâT¥ZGIb€#rDŒ‡ÕZutäñ‡W¬\ñȃwß}÷·^Ýï7?÷ÌÚ§V®¸çîçž\Õ·~mßú59‡oíëÛ¸¾VNãªNbÁX!÷=¸��=rý-ï:ìÀná®éØyg"Üå»/Ó+yJë<âÜ‹?þ†íš¤œ½Žú7qÕùŸ»æé‘Í7|ãÇÏòîã6‹Ý­zä Øi·%�€Øn÷½U¬T��µ;/øàøÎWìždçnú{lhþ.ËI$/¿óîK+?ºNCñ€£ÜúÓÏ~íÆõ£k¯úæåÕ7¿ûȶìyÁ~¹øs'íÚ!&^ÌÚŸ}ùж3ÿãä™ �xiÑžûî»ëì€ÜùŒo|éûÌÈ{1k§‹«þpÅ=["lÓΖuSÌÝu¿}÷Xؤݗéºm7 Õ仜õÓÓ¾ã û �� �IDAT;Îy»ë‹wÞ;6Åq¦|ËüþÚþ…güâôoü`ï÷Ýø‹7&¥9Ë·W?2j�ôÚO ÆkÒ�fãæ§†g,•;ŵ^^ùuùãNÞÏŸúVÚ묟Ýõ䪛¾òª‡>ñ¦þ‰°èø‰ñ_/üàÿv|êξ±'¯ÿö ‹ü=Ž>²ç?Ý0ˆ�P»ëÆ{Û^÷ú]F¯<÷£·í|ñÃc}^sÞa39�€Øé“w–Ã0»î}sxíÖsßtæ-Û_pÓ3ko9oþ_ÞÆ>­«Ï>¸¢øî?®Þ¸îÁÿ:èé‹¿¾bϯÿåñÕü÷ëŸûꇾõ€Ы¿ÿ¶·üwüÖÿyà™U7~ûø…ãþ-³éš+îXzâ‰Ë¶Ü½øÚ7ÂoýÓíU�À[o\¹èCæ>ûãs>·úß~õôðè†ûýïûä�ÀÙÿëOVÃ0ùåñ^ý¡ãÎ}êµß»gíÓ×¾_üüô^Ö‡€#OÝ÷Ø¢ÏÜû즵w|iûÛ¿úþ7\r몧îøò²{ÏýØÖ€ä/¾ù]Ww~äw®yô÷çÚ;qàØªÛn§\õä†õ_îþÞd7R÷ëì‹ÃwüeÝÈðÚ;~úÎåpè÷g{΃»_tÓ“OÝþíýV~昳~›Mˆqö8âÐŽn¸} � ¹ÿ¯·ákßW<úµ“ÞvEáìkW=÷À÷öyø“oÿÒ})@ù¯Ÿxӿ߳삿®zæ+>²ÏóI9<Ïڔˎ8böÍ}ü— ÜóÍÿ¼g×wÚÐñRNÏ®G3°e@?}Õ…çó§×­ÎðB}—–ÏÏÚã½w¼ú¢/¿©÷•=mÿ6Åê0[»{Þ_¹õ³OÜü#¾{ü›¾úÈÿ­d·ûûqÛªM}O_ónyù>sÕT‰6fýOÞù¦‹6õ_÷¬^õçÏ-¾éýÇ~ú–ZLJì6p÷]Ïh�µê¶;· ßw÷J ×Þygÿž‡ìŸ‰Â›ðþ̾ÁÔÆ‡ïÞðóU6¬üõÉ•K¿ø—9ç^½bõ£Wž_zÖ °ºæþóïøÝʵÏ<xå¿Ï¾ñG~àê—(`HŒ³ºLÁ€3ã aÙ-µÝm›L²qýZŒ�ç\2Æ((À�Ó!Q©Òˆˆ pÆ…#Çñ.—Òñ/ðƒ\!W(Jí=3{»{gtvw•ÚÛr…¼ã¹Â‘LpÇsÏ¥¸�ÚÖ¢¨EÀ¹á 8#Ñ>áHB•„')ÓsÆi#i)ȵ!Ic jãû¾ç¸žçù®çy'*’7i::eD#"*MÒŒq'Ql¹ýŒ±4N¢(". #¥U&ƒ�@¢qM¥áúÓZ ‘@ ÒzëÖ­ëׯ€\±À‹Â0‰c×uÛÛÛ{gÌèéîö}¿½ÔÖ^jó¥Kj‹€:Žj£Ã#µZ-I”Ö¨”ÒºÉÏÎ�2©ˆˆH#ÉD½˜YEµ(Œã˜ò/¨P…•Ф° ¶ÙÚð|âêvÚlû�ÌXö¬–€mÛi®(+„Pù´ZLiŒKÇMS5888<<l£N²¤eXd%0è�NbŒ¡ˆh� EªÜac¤³òûSùí²Ê,¢ñ9�ˆ £€F<9ÏèAfÿ´îe/4A;­5jÓ²´í’· „à\rÃ0‚!D†Œƒà ˜"DÌ0¤všÉ³â§rAoÛ¬‚@vÜ[üäSYö˜_}öWfß2.ÙsY³`AËjôÖØƒ0ñ¹²=q†O´‰HLÀKíç©ÌŠPáIcŒÂ‚±Ï’ËŒdõèÝ�tËéA@+(—ËYr¤R©XŠŠälòù<�„aX«ÕÇ“zXC2�€³݆Z£1`Œ)äó³zg.˜?wöì™Ým¥ÎööŽR1çJ“†Ãý[ׯ]»ñ¹ç6®î¹gV?µêÉ‘~0Úh]­¼ä‚ˆéÚ_½ç­?ê9ÿ¿>´£�¬•+&_(Ðf…b*cSQý¬ûØóÏÛwõ%g¶ý’#¨ŽÿÐçs¸ôè¼”RÊ`Ÿ¯¬+Wýb¾ŽX¾˜KÊ•@­üÞdz˟?d|¤wž³'¥”NÇi× •«/65¢\Aà ßñ•-ºçëï8pñN§\=ãô÷¿®ë…îû㻾÷ÝGþØY»fd3Žúòo/ÿø¾ù)/¾á¢ÿ9»ð«ã— ½»sáƒ2ð÷ÿÔW]xø¸@~S×½Hcííóæår¥â'í´4ÞÔ7yè nzúæ{ó‡}üÕ»-.t/Ûñm\RýÃã'Å÷íØxï†1Äá6FK;kl4P~`ýÆ¥ –w¾Ø¶dï7|Ýåi?þä}¶E5ÈyÓ;.X¸Ç[.:ï¸ÚU¿¼©Ö|"/”rÕg}bKš›±Ýö³}ðö{Ë›gÜzåû’o¼]zô¾¾S,zëW<=d ³vZ:£>ØL¸ ptËÏ~¹õÈÏ~ñ¸í{zw{Û§Þ±ä¡ën¤ptÍžÙ3k§7žñ¦¥:¿àU;̳ÓßyÌ‚u<6†zÍouïâ|í‡í8wÞ²×µÏÌÆØ˜ ¿»üîNzóöØ„»·þÖ£áº+o®�Tï¼ñþYGµ\úÅ‚ì_õÐÚ1Þ>o§EõŒ Æe½‰‡þø“߉S¾pî¡ »çì{Ö'NÈßò§;C��à…ž9½=sv?õ]Gt¥í‹÷Z:gÞî§¾ã°ÂʇW¦>xùå«÷ýÄ·ÞÀÒ9 v;âðÝÛ€~ò¢×RJ)óGýx+�/ÌZ<wÆŒyKæ†'»Ë— jãÊG6„nçÂæÿñÇ¿S¾ò•7ï6þÎÇ^ðõwæ®þñ5Ù ¸¿ÿqG7_{s }è÷.|ÜÁþC¿üùc¯þØEoßµ·gûc?sæÞÏýå/OéèÖ_^9ö†Ïým{-œ³øÕǽ~Çç‰y¾µ™í§.:vìWç¼qçEœ¿î ³ß²“ó7ŸžY|Æ‘ç~÷C†ïùþé{í|ìVÕÁ›ù¾? ­½ý»û=pæ1Ÿ»k’:eÓ6m/Ú¶µ:dç¼Åóæ,Úý˜Ï]úÙW?véÏïû?å X×Ò]–ô¶wÌ;àÌÓ^“<µjýäajæÙ__zãì|ëÜ#vš»`ÏS¿qá›F~yéÑü×¶ý7Þ²ÍÆ[n­,_6zë-ë Üzã;~°øÉøö'ùdoïL3¼Ž™3{z—òÞ·ìáx³w\>öÒCÎ8i×±GÙ@-âù™‹Ì™·Ãïùþŧ‰ßþ÷5/OÀùøn6»­€º×Ë~dñX}S@žäìãlc{Za&ÍpÚÈrÆec¯Lðž"‡]ÇñÜ„�'gÂu‚B¾ÔÑÞÙÓÝÝ;£ÐV*u´wtuvvwutuvtuvõtwÏè‘®# R à)ÃP§aWj5.E]΀2:Qi”&¡J4*·‘¤ÂP‹ÂjX«ÔªaQ Ú§išh•h•m¿šD׃±Ap«ößÑÑA¾GªVÀÈ!Œ�u’êTqRå7¨SÚ0c”ëôC‹dzzºÚÛKAà¹^C‡ßq9ça ×)µµ¹ž7:6¼ný³Ï­vëÀ–$Õ=¯1Šs�Æ´1ÂáÒŽ'…ÑeÒFf8"3À‘ê vlö`ö<Ï÷}R ³"és¦n§Í‹…+Œe‘ÕDe§"ÑUHY€GI($Ç�Ä b'] D¤ªÙHo+3Aœˆu“fÛ'6 ‚î5cÆŒîîî¶¶¶ êAÑZ×ë€N¦5`:û³Öš3ÆëÁM÷µ=c¿OD˜S±MxÕàø¥›:Œ1uZ-ÓHÆA¥ ¨�ACŸ1¬“†hêÌñ-”ÇKµ‰è·…ûÈ>õT=õ~gßfÛøµe¼¦¢¦š®6q·üi€S¼¦[Žù[úyR£µ`É/Zø[î… ¥Ãì‡ô¢àœ“ €°a t"Ui­V«ÆRô@DJ1 z±DùAC-EkÇ1…uh4Žtâ$VªžRÄ„ÓP=Ì1.•ÑÑüb{ÉÏÚèj¥ªTÚÕÙ±páüÙ³f– Åœr¹®Žv×q¥Q½Tš }æ§úïϽýÊÿýÐr�X®XàÕJ…ÞKX)W¡Pšªz’zìoû|øÉ;Ÿyvãs·eÑoNzëÖ`Çñß¿oÅŠ+VÜÙ‹JÅ|T©6è—j¹æ .^ýÅï³÷öØlA:g÷sþðàŠ+V¬¸óKwóP-W³(Ônÿì©—ÌúÎCkŸÝ´úÏ‘ß9ö½¿îa(¼õ~=zäiÇö¼PZÏ<ì¼kÝRM¢‘§¿wx^n·Óv­8©µë^œ™þ›îøúÉ—¾o¿ï¿÷øÛW¥h¦òúôW†¡4cv}ãâÌnëˆ+Ce6û€…]?÷øhøø]å߻ײëŠVÞ±eæ zþ†ÐjüÃå7¼…?þ‚ÞxËæQl:Ñ9àüß}÷ ÕŸÙoþv‡žý³GÊîÞ§ºè–Ÿ_¹>~äO××=ö <”ŽýöïÎÛá¶÷ï6wÙ?}Õê jXÞ´itô7§Îð}ß÷ó{]ø¸j"Sx©½ jµ�€ÛІ˜-›¶ÀÜs'7æÙ«®x`דNX*�&¹{ñ°ÓOðÿó?Õnÿã-íG»‡Ãç¼ëÇ¿>«tÅI;Î{ÕÉ_º~ã„·éß°)ÚpÉ‘m¾ïû~éðK6%ÃC妑䥶bX ‰Ý(– q-0[6oÍ͛ߌ„Å¢w_vÿŠ+V¬¸ïÇ·ð_“߈/ÿèeÿsRõâÃ/Üï]ß½kÍÖõõ¼% ë\°d>nÚÐD@å:ùÞõ¿¹©œ>ôÛk‡=ùð³yÃæèæ/Éù¾ï‹>|k:28¤Ë›ûj= æ½àoϳ6ͺŸœqÖã§^ÿÔÚ üÑ÷žqìJ_¾Óõìyüéïûð|늻îþú²[¿ðµ¿ZF€û ÷{ÏÅŸ:pͯ®ø¿…lÓöOb­ïf|A«ƒ÷n¿]©oý¦ÿKAŒôÙ«?}Ì {ÚŠ];â¶„Òô'1½iÃ&X°dAýŸš¿pñ¬pãÆ¾ÓQGοÿÏ7ößxýºCÿãS¯½þ†-Ã7]wÿÒ£ŽXÜ•ýÏÞñ<oïºÑºZC�`ÅR"z[gÍÛnÇ…¦å5õ·[v¿gÑG=µ~¢eÍnŽ[üuöjk¯'(•u7µxó8çÀXý ê"üãûu)¸#Çq|/‚ Ÿ5oî¼E .]2ñ¢Ùóç͘=«{Vo÷¬^¿­èò^>çæé{Üu¸+¹D(£S­Ò4¥ï‰JÇáR ¢2�¤”^àû¹€´Öê‘ãB‡¸!—õ¨x‹0£("‰AŽàpA)_pǤÊq‡ ιCžZ×i³¹ÁœóZ­644ÔßßÕ º»»;::òù|{{{©T*¶·Í˜5sÉ’%Ûm·ÝìÙ³K¥‚NTœü?öÞ;n’£¸®ªîÙøÄK 'Q>’2‹d$‚  °ÁضɃŒíßkLÆÙ ¡`D" !ƒ0B :eÝéNwº{ÒîÎLwÕûGÍôöÎîs:@‚­Ï}žÛgv¦§»ººŸþVtEX„ºN$I¢ À²,SÈð¿—¢À$¨CA ”⪙]c}u\jzMÓtiiIË:ê<ªò(ü sºœÌH‹ DÎᨶ\;±°…;‰H£ÖÕf«=¤²€f¦Tƒ³Ù y cù FõJ ‰Ph³×ë‘ZY½÷šžPDºÝnÈVÈe8EJ#q) &Ûpå3FNÃ|‹9ð§ )¿˜ð”Ž:ΛøÀ‘÷{`2"¢ôy^éÉö{E±%t5Ö -G± }¹§U¸·0¼9V~uÌÿaÑ­�þðë=ºH,÷àŽ~?)DI‘¦w ePpTYG\’ ×@CD$® !"Y–é"U7-a «µÕjÍÎÎÎÎΪÆM+ËèjÒg­µÆ€‘¦´ KƒT Ø$÷Î{Ÿ4êµZ-˲¹…ù¥^G€™¹×íeyV«%Íz#MÓ-›ï"Böì\žÔ¹lÿö›Ÿ÷—7ŸrÁßþ„™b.›¶ï¶k®Ù¨aï×^u½9ä°F[ý/¾pîÕ‡œø¢ƒ¬~Ükßròª|늎™ÙëuëÖ­[wè~«Ú~0\÷Óë�€ÿåÕפ~°Í~ðůÞúý7d±þŒOl¼ùOÞåe_2»pèºuëÖzðÓ»­[·âÖŸ]«€kéš«ÖOzØž&ÿñyçÝö¸°§…úOyë럙_úÍ«vêÜŸÿï—¿¾ôäã~òžo­’»ñ¬øÜæ#Nxþ°sëî -­ÿÜ;¯7'ÿOß{íÇ/xâ!˃rZ=1+ó›Ê@î|Ãömõöì$˜ƒö{ôª ?þÆ W®ßíÈÇïuĺ-W}ã†ÿýÉ䣽?زùKç|gí ;Ÿáoiý/ïlî¾vÄ©#^öÿ}ùÚ›.מ—üÉ ÿîJvÝ«þôè«>þ±Ïœ÷¥Åã^ò´I� ÕOø³~ëë¿þgÉg^zÚGnd2²4+÷ä‰5k&W½äü»{…Ú/ë^ñÖCÁ?"”5Ou»³Û»ÉM¿¸©ìןÎU8©Œ‹z;4ŸôÚ?Úû’ýÛgοdö¤S[�»ÇÓßòŸÿsãõç>Ë?túç¶Š±Vú]¤U»®©ï÷çßY(º˜åK_}Uų=ÞtËf·=víÜxÃÆÁóFmÕ¾‡®[·nݺCöª†­,÷¢æÏ÷yWÞtÕGwÍÛNxë%Ùš=v7·­¿¹ÈMán^ î¶Ç`HCóI/=iêâÏ}ñ럻pé9§>chÍ®kÚðñÛ»%›ÓõÿôÄÚäî»OlºaýüNnÍ÷°6宋Ïùöšã_ô˜I¤éÃ^òöWòËo}/þý|<&³öðu+6mê ö\Ó-<ÀZé1ýö“m·“mwmÀú¸S«Cæn¿}qÕ®kîµ£Ù}§ù/¼å´³ì«¿ôË­ [®ù‡'/&0»ï±›Ü|Ã-Å&Ù»yýÆæî»Ï =òùÇïvÙEçŸÿ¥kÿ‡O{ú<æ§_¹à¼/\¶çsŸ{‰ÿ²×GìŸ#©„EÅ/>÷ÉßqëX½ëýÑnPôç`  ÞñĆÔSñÉvâxŠQp8JÆì”"¢¤^³µDXıÏò<Ësµ¯zfç}æ]–ç½<K³l1MSfH[«C’°1`¬©Õí‰úädcjª==3±bÅôÊU³«×Ì®^3µb¥ù·&¦š­‰Z£•Ô&©#YA™ã^š/uz ‹ù…%Ï ÿœ—Ìù4s™óYî‹g�2$H^À±äžç—:žsLdkÍVkrjzzvvveÒhRRSŸY[oØzm„¶–PbËT ^4߃=7ë Y˜ß¾ùÎ ï¸mÆÛçæ¶mݶ宭›·nÛ²}~¾Óë ¢­Õ&¦¦(!'Î$dk‰&n k¦gW´&&“z¬!‹d±ð)(0kÞ3DÔèõ4ïak¢½jÍêÝÖî¾f×]&&&¬µZϼ(NÉœ—pšéaá0Ôéë€F‹#j|>–pÏ!P\ÿi<¹#QìŒÚ!kµÚôôt«ÕrÎ-,,t»],C¾ ø3kùÏT¥š@›­xY‡!„’!еñ¸0J們“?–~1¯*DYH@Æ«/Ø]㛇×f¼„ED=}B„…÷E¨Ž¦?(æ‹ÐƒhÖC�àrƒ M ´òÞCôaâ2"xéË(«õÈwU˜PÙÁFþ*‘sG̽åºWZüÒaC,áql”:Z¾òDÚZ>;VÜÂAeVÐ|AÄÒp[ÄTv5h³,›œœÔ}#H;—Q<� jµééikíÝwßÍÌsss[¶lÙ¸qㆠîÚ¼ya~^ Ù¦c’zbë5BcÑh~MñÎeyžö2×ËÜb§k’z{rÊ$u¬Õ›«Wïò°‡=¬ÙjÖëœK{) 6šv»=55é=·ZÍ©©©ÉÉ{ý5|ý¿Ï¼é“o~d=íõz½4g�»îE§>âªù«³®¼ù†oÿÙŸÍŽ{ÅsV–wÚýàÛW}þß¾{ëBçîë.üô×6î·î ˆvZû¼—?uÓ'ßñÁïßxÓ?úŽÝüÄ—Ÿ´Ÿ©?ëÓw—Þ@éÅ´ÛÞg|gÓYÏŽ òµ'¼ô”Ý/yß__xí­×ñoÞ÷•/zù“›`ö>ø�¼ü3ŸúÑKK›®üÿ¼4;è°‡ï žõ7^þ?›×ýØ>‹dóWßö‚ÿ¿.-û §ó›n¸üœ3_ð´¿øÑºw~范 ÀâåÿÂÞqÉVͺ{Gºëå©s¥:ÏÖë¼éç[:’†…»¶Þ¼ÉÃîû?ù¨¥oüã~zÓâ–ëÿïì½±õÌCm�˜Õ{Zû§üÁú#÷;´Ý8ü)»þâ#—ÿt×ýÚ÷~Ä_ËÆ‹>÷½½O8ñðì·ÚÍü†«~º½Š¸ynÃM6oüåw?ôš·~iåK_ù”æàƒ|û¿û³;¶wiõºC×& só@k_ôú.|àôõNxÙï· ½ñ‡ß»þÎù^mí¯‘ù¹EIöÞo ß:ÿ[7l¸õšŸÜ0ßxòKOš¸à¯_ÿŸ?¸aãæ;oþÙ~¾ugv ÚçÄW>åÎÿé[>å-›6Ýzë]jÊ÷¿øü9×uòó¦›ÖðÛÌÁ¯zÃÓ®9óÏÏÝå”—™�ÈüÏ/ÿáú-‹®½ÏáûÏfss=\¹Ï>?ÿÊy?¼eÃ?¹ú–tå3_ú‡ ŸzÓ;.¸òæ;7oXÕ•ëv¦‹ö‘/yÅW¾ïÕïýʵ·o¾ó–Û·f÷ðŽ|ßõÓË®¼åî%™Ùÿ°½ÚKssnöY¯|¾ü×[ÞzþOo»íÚ‹Þñ¦Ou;í9«ë­løÙOîè @ýq¯xé_~ã«?ƒ'vì@ò¨½x¿Kß{ÆG¿óó;6oºíº^³‘O>íÔݾöö×üë·±aÓ†›6Œª=ÑèµÉ7îÏž÷ÊO^çpæÀƒwYѧ¾zÃ\wîÆKþýŸ¯<ôÐÕÔý|üß¹àÛ?½ñŽ;nüÉEï{÷ç¶<òØ'L#ßñÝÏžwÙu·Üqëõ—~â/ÿþ»»=çøGýæó°é¡EÉ#~ï1‹þù_¾ó‹;6ÞqÛ]ª}ÚÁêÅŸ}ãkWÞxûM?üôÞÿ=NzáQ¿N™cf@à´×Í8üY ÛxÀ½O8íØùË÷\üóÛo¹ò¿^ÿöÿž~ñiǶ�ì‘'Ÿ°ÛÅo{çOxÞ±S+žñü£.ÿ›·]¼ç‰'\QvŒÚ?ïù[¿ÿåï]ëm×}õ=uÖ¶§¼øYkï½SÞ(*­/#þ” èXM�eª9} †=ñ‰9%Ãm©mvø"ð÷F "M7¬ j ·Ö’1h™‰Ä6†„ E£ckê Õ[«5Z­öäÄÄÔäÔÌôôŠÙ™³³«V®Xµrf劕«W­Ùm×Ý÷X»z×]fW­œš™œ™6µÄÔJ,­$ç}Î>ͲÌ;ÏÌX ‰Œµ­Vkbj²9Ñf„mÛ¶ÝvÛm·Ü~ÛwnÔÀ‡Ôå9C+ØmkÖZ, Tu»]=[2ÖÚÄØf½1Ñj¥iª`5MÓ-woݸéÎ-[¶,..Ja ÷NŠ $¯Þõ±R†|ªÕ\Ç¡q›7o¾é¦›nºé¦»ï¾gff¨ŒAH’¤Y¯·Ë„ä±#‰DÚ÷M.„‹%g°é•Ø#¼TíÿÌœçy@ÂÚsÅðÓÓÓÌÜét‚<Ç2¯8G 7ð ez¿F£ˆêƒMDFC­¦e©ËFˆ8P_ŒÀgŽÜû%2ÚWpl|[eøñeHÏ#ÆáÇãnÄx8¬Üø(]Z*�•Kß“ao‘Xo8üÕ}“Ýd ŒTFŒX^=1RPq0Çaÿà 3‡;ßYq|¨XÜ~Ìäs£Ò‡ØÛîŸG’:Å�€sniiiqqQ>¼]_ò¶¨´„ŒzE#’  a�€Ê?B:¹KKKÞûýöÛïÙÏzÖ1Çsä‘Gî¿ÿþ{íµ×.»ì¢ ˆHÓŽt»Ý…ÅÅ,ÍŠ"HdÀ I61Vk¢Ýν۸qã-·ß¶åî­wmÙ²þÆõ‹ ‹i–;ïr—giÖëöD¸Õjyï ´Ìͽ"Ùò?—]7÷½7®›h*M÷©»Ìgœý©ãïüÛc:üäsW¼ñœ<o¹�8óÜ:÷ k.:õ°U3{óŽõOþð½¹b†¦Ý_ò±Ï¾?~ü¡‡üầ~æß^µ×NÙ {ç¹ÿrÔ_÷¸óšËÖýÃçßsL€öÊOœýRÿ±ã>;»ß³>¸tòYŸ|õ>;Óœûåu7´~@”f‰çoüÑe—]u[g9 ξ~ú!}ê?þh÷×^pÅ×Þòè�ÈÒ­?ùþe?ºqN–aݽ ö¾'¼iŸ¹?ïô£?üÊ?¸ô¦ÝV¬l"˜•GŸ²O~þ'\üaÏØsÃYïºn'ùÛg·úæŸòé׿ú›sìþ|��íùìƒ÷èâ¡Oß³ 0ùÄýòù^Ç´ûý°Éðíÿ}ÎåûŸxbi²·ë^üºç,}øEï¸,ò٠٬‹Á�� �IDAT}ðú÷>vïÝ÷zÄ ?ºøü³¾ü¾cšƒÊüÕÿñçO?pÍÔÌ^Oû½êoxb�Ð~òét°ìûâS[�Þtù¿¾úIû®œZ±ÿI_ÚãÍÿøšÃŒ}ÔŸ¾ïU­sN8dïCÿð//¸ÁO>õ¾ø¡'Ýø¾ãØkí><îõçýr§ò†Óž¯ø/½ëà+ÞòÔ×®=øÅçÃ#xX¯=÷Ü_>þäç®UþŒx;�àêçžñ’Ýèѧ¾è��|ë×ßsÊcö˜™\søéW>ú½ï=ewj<õMïîüžöð}y»/¹ƒw9ùãyÀåo~ú![»ÿãN:óâÛwJ_d×½áü OŸ¹è5OØo÷‡vú÷Vuè®;4:⨹_^øæç¾Ûôôn÷Ïþçw<{W<çƒ_üÇG^ù¦cößÿè×]vð{/úð V#ÀÄÓ_súþß?ã?}§�˜C^ñšÇ/lÝ÷•ôØ:�@íQo»ðì;{ÉQû®ÝsÝÓ_ûïW/@ë÷þöKgŸœ~ò”Gí½vïGùósàüTF¯MÞòóË¿wÙ5wfPâ™çþýáWœqÔ.S»<êµß;ðÝç¾çØfAÝÏÇeaý7>ô§Ï8bŸ½}Êë¾2óÚÏöŒ ð]?ýü{Où½ƒö~øQ/üàÖg~â‹ï?v¹\$cÓ2D{¼ìƒyþâGž{Ø^Ûÿ™Ÿœ;üȽ۸£ÕÁ›¿yæñGîwðüí-OùØg>~©`pš:îÌ~Ö¦÷³çÌäôÞyå^íÑÆxïï¸çiŸ¾ð-»}åUGíwÐÓþæÆßÿèE÷ä ��{ØK_¶®CÇžô´)ÀUÏ:鉮säË^|`ew½Þ’Þõg½ú‰î÷˜Óþ{å_|þ§®%€‰ˆ €HˆC©Ó�ðI/<^Ãa¢ã/aqÎ>—ci›Õ›>«¸b<щ֗eäÕŸ€‡þK%dâã©>È€ÆÖk Õ´–¶¬a®X¾Y��˜9ä'·D‚H�^Äç9“£î³Œ…×yï}î{Â<Í€rï\–‡Ô\>Ë‹s3"3 €&ÃsÎåyŽ�jXsÞƒ k<�ˆf/gÑNÖj f.’)†Ì½kL´5@-üD¤ù½¦&Z¥j¯Ó‰KÝŽµV|Q¶ˆôÌ» ˆdç¹Þl·›MDÔh…Ž–"«×QSßQ_à½èá‘ø­B¤f9˜î¹È>(4˜Á¢ØŒbdПæ™rXgAí–ZMs~~�¦§§kµšÆMXk ª M ÙÚx0…!‘U€÷^•&"¢¿bd7¦Òg[kõ…ΞûÁBaŒAƒ®hšŒÐfr 2ãGbL‹‘Š˜"©¬_Í?º½‹˜µ–¤€&" ÌñÞ“IDDÀ÷G¤ëwÐ~’°c œ ššbö rgˆ�<„ÛCŠ ,ýÔmDKP�…ûcxK—Å`p«\©ô*ÌuøU;sF¢h‘aªèõ†_1üyø¶0¹#ÚºNçX:‰&"æi:–aX¹ªPíd­VSgÝyæææšÍfXæªk4½^O·çÜ#ñcQDTW«Õœsóóó‹‹‹·ß~{(lADõz½nD\\Z0Æ€ˆ±‰&%i·ÛYæœgɳ'Ú-õèt}îDd©³ä=OML"ÂÂüBšöêõ†ê5kßù†·<ïùÏß±cí7¾ñéééÇý¤Ü3¦_ýàû—>9ßïßô/¿Äçžý¿—½ñ€{wæ»Ç¥37'~ÓÅ}ò_lyËŸ{á¯?ã¼»òíG>åª×ÿü‹¯XæÝnq®ƒÙMçþù‰ï›ùð?ôÔûŸò»FNyÓ˜î'ÝwÁξþÇ{½ÿãæ=í~›¹Wô\†|ó¿<ù° N¸öÛgì9x’ûæÅ_Ùgßýš­%}`ïYäºk¯}êSŸZýñ .x˻ގ`Š�‡p†D@À~i´ –+›îƒçøx­G´á4Â@Ùm�ð  öAVEEi@õ àûÇq¢~„¾xÀ3,¢1y/CÈNÕXIxF@�`¡o¥A@vž½0¡!`(²‘5Æ{¯V*5!³*šÍ¦ ÄÌ9vECµŒeY–õÒ~b?žË ‘ªïƒvì1!³ÔíEyÝ 3—ë À±WýBR¯9çÔŒ†eAõF£Ñh4€]»ÝV/ƒ�€`¢ÕN’Jø§¹!‘ ¢� �I‘‚E‹¤:¿I’LNOc–� ë%„SÌQì°}o)h£´“rÃ(„Ä"ô ”Òxí¼”.Ðiš*bqŠs¼ëû#ô«WˆÌÏÏ>ˆˆ²Ý“e½t@áÛXM´lPÔXG�ƒê€x±Gƒ‚xt0W<ÕGBÁП¾š z*ô0‘Ðëê˜âœ«'Öà ÀP$. ‘¡ð¨aھǧ‚ÛBŸ'£šŠÇ^áC¼i„ý`%—ÊKcAh3þKÝp7â «âë•H\ÆÇ !îɰÀŒ|ü>“ˆ¨?ŽºG‘úÄ5 ¥Lü©þ_ªSÁV½­ÊU³Ù 5S¡üK¡ªUÔjµÙÙÙÍ›7_uÕUÀND„PuFCý¡²,ÓœˆØëöÔ](ÍzÖ {öàÀç^ŠT‹d»Ý.MOMYk·mÛꜳƊ÷ÍfkbbÒ³¯%5fžššª×W;—çyÎÜhݯ‚ˆcðœû“#NúÔÃï­]èäÛþóäÃOÿníàg½ñ3?ù7Q•.¿òœó7ó¶ç¬YîÝîÚ~êÑïºnú'¾óÜ¿ëÆ4¦1é!IÌlÍ`&! ë™É.1‰~Ž¡ÖšØ8&eØ*—Ù­´-…aDäÙ� ¨Á·x9³ Z°�„P“2�d¯¸¥� "yé. �¦Lç€,‰µ,ÂÞ³s¤ÈÖZ"ʺ=Åuj $cŒée©žYsì<2êtèØ£€ Ö�/¬:fA)q­AHóœ¬Ac“z ’$@ÑŒ‰"¢ÐÄÝY–Õ©¥Ã…afçsŸ™ZÀ äYDXuÀ"k!t‹Ö^D  ¥"Øg>ç\À‹ˆ%ŒI’˜ÄØœsv‚ˆ’ù\Ï肞kEÄåÎjvp"…^ž@jZžçó‹‹:»ê-œçyÍ&j Ôišº,o6›Ž‹ÈáXNÝG’>¾/…�À„,�걉¦ 6®È$�ò€ê¡ÄÀÖÚ,ËÂýzEçB¿R&u:f®'5ÄA3%¤¬�ÚH–eÆ$PV1ÐëêS[§õŒª-Äý—Òšªmꃪ÷‘"µ$—I AW!@c‹@ÏŽÊÒ•EIxcHLØ¿NýX¡Re@8à\P|.ƒƒ<ƒúX  5ÞGy‘"½†¬îñFƒšÇÐ¥ŠÇAE”¨Œigf*â;YýnË̈”$I�©Ë ñ`a$r–‰ï‰½ ªÒ‹±/ ¦á„êë¤C”¢eöÇÝÓ “Ïløuø'–$Ñ®IaÉï.õeáq* ß*ò•A5Ë ;ßjµT ÆÝwß=55¥ J]/“““›6mÒý?T7PœêÂt4›Mõ5@ÄN§£á<ž ™ynnÎZ›çùþûï?99¹}ûöU«Vu:]/(PcŸºÜ!˜zÝŠˆwº›nÚ!ƒ$l Šw[6ovÎÍÌÎd¹[ìttc\´‰­Õj€h‰ ¡hpĘÆtŸ‰þúïÏ¿þWñ íùê¯mõ}í×AÉQï¿þîÝ`xûßþëêΘÆ4¦ßBª=ýã7ü¦;ñà'Úûu—.¼îWÖ:‘oÐ@`"2d+£b SOïp%œ\)JL¦Ã‘7†yMWî4 QÅvÇ"¥‰X_­j‚â\ ÁV�Ô!Â3{!âRÆZÃà!žˆÀ–)Ä´o1’¥Â½9† ¢Q  bk " BQ4DU Zß‘Àäì™EPÐmÖ­&*œ·¥cžE¼¾Æ—ÑéR†Ž33x`æÜyï½þ×l63ïE-#ˆË½ó]¯yøјÄZ$"ñÌ âÙ±N³÷�P¯×‘Å{ö¹�4…—~žç™sj!¤6h¡àAç˜6 á8h_cX*ØfÇ"‹AAGÞ¥©3ƒÔj5U@–eÖÚÉÉIkmw©XêxW¬X =ÌoŒñ¾ðZÇÒ]|<´ +‰ñ^¼X„Wn+ÖEäG8Ƈ1$"?¨†¨ð¤²$cæDÍë‘£X*I¤À´0 Væ(ž‘°ãÇ+,×á˜?8äT¿7Ì;”³«Ü<,0•oã-+Be¼q'ã;a‡Té@e]Àœ«ÄVº1²KP:õÀÐÊ’È KWPeVÙ«pÞ¤~4 òµ*AÌ.«Ôëõ©©©©©)]š9%MSÕé¯I’ènï !-H¬'""@£N¡öúõheDM!î½·Æ¢*MÂÞ #� c/‚hL³ÑÎó<OÓ^·'̵Z­=Ñβ,Iê� ÂÎsî]îË]fSç{Ö 3;žÁ1iLcÓ˜Æ4¦ßnbö èa–Â'?>b>XØÖ`ðL?13R–p™¨|¹Ã4–†¬øì?¢ÐÔ”ñäˆhô‡* Jc5‚)Ü�BcÊß1@¥Ù µ�$€0C© pH¼0ºªƒ‰MD¤iˆDjëfQÓˆg/¹7Hdï<‹Ac‘Ð@bk�@P�Kiýk4Ì,¼÷šÀ9ž!Ëò,c²ˆ¹wÎyk­s…U<à^fn6€�h0±5cŒ%CDÝn­$RXMsöyž³ÈÄÔtREÆ–�:‹KµZMÓ"uï}˜ßÀšp∥/a"Yžƒt ƒ·XÚŠ›UòJ®jœ •(¾- ¶Á[T Cèv»&Õlô ÉÔA@} 8ª�…³z—J”¹#4zT$¡ñ·•à’Q1‘Ì[*f*^eœ¼Š`äH–Ò` {y�€$BðÌZ&K˜ $°"Ì)ëüN=ìܯÃ<‰¯C´«Ä@ä§ð8(õ5ªîÁÁœ1Ó†C6âf‡}@*·Ål—Q>ÿñ¼C´‰UP}e •û+ÛÁ2©´ ƒŒµaøÊX*µx}.EÞªŒu«† Y?¼÷D¤›÷^ƒžêõºs®ÓéLLLPYj$n$8‰`ÙcŒÏóÍ›7ç½T=²,ó º™‡W� ;3v:½zRóˆ‹Ý4÷¾Vk¡©w; I’MÈKÒhf½3;3»´´ˆB(L†™Ù# 5@dZ­vg³SÙÝÆ4¦1iLcÓ˜~+I!‚$Ã1UD@¸PT }87ê)Àèˆ h‰‘Ñ@Ôzh3à·ø„ŠˆÌýºšz”/’– ¥¾/Ï»�4W¿&ü í‡þ(‘ p™2 ÒD¾eÏÖq¸´ùcigS�õtBDkQHăހ,"À™`"«>d Yƒhº¹‘M ÔjV¬ÙÅåi–9OdEÓ ¸4s>‹m¶Y/Íó<c/¥}Õ {� ­+f,YKd(±ÆŠ�À¶¹9U(ØMèX³IŒ•iؼ÷Æ¡Ñà 0ˆG˜f6e¾‰ 3ñÌSÅŒ©c‘² {,T…X–2¥­RI ›7¡O©‰2޵†²" "v:ªßÁ̈ÆG„ˆj,Õø‚X0‚Ì ãa¥Q^…4aÈÅp�•‡AMSÈ6Ue²wc´<{–RÙQ€s/Þû¤fÛãYƒQxD2¨Ù½‘ó(Ciu6Åh'ˆ™æ^ þí<äjoDñ&0<Šr{)¤%vò—A‡‘‘œ‹"þvP¥E³_Ù^ÑI…{âõzåŠ`fÕô©y?îg¸Ó‹Q¯×ÓÊ ívÛÖéûÚXÕ�ÖjµùùyM+¸°° 4›Í:TŸÓéÌÎÎÆòf–¢p˜ •X±rו+Wª¿�dY¶ÐY*œ›ˆD¤V«Șöz¹Ëkõ–µuaA�Òmm’4œxkjYÞÍg ^/íöRLóJ—5ÈÂKóÛQŠ¥á9Ó˜Æ4¦1iLcú!Mñ?Â<VÄHË(“)"’5  P¤À͇Ô7Åcá®\à ~¢3 z‡!œ¨K¿B½Â…=ãÔ†#qN|°fDKVDÁ ‹�aáõªO…‘ByìŽá¥”ÁÌ ü`hi#þ"ÖZM‚¨�2´0²Q<{ïÑ�ãij0!ˆ©ÕED¢Q+�^èuEXŒj5Ð& Ù¤Ñ,,Ò:eâˆY½’ç©Ï‹hEþ>ϺiÏ-9�HŒ©ÕjhL«Õrì]–kr>€"Ùdžfyž$`aïÉ¢Ñpz òcàk(Âä;)©•ÏÃ?ãâT‚¾¬ß®s¡�[ëÔj5u«F�íXFÔj5)3´AääÐùŽ-D 1„ñ`+02HQÀœˆèxAD@ðR,D/B„"¨‘æ�h¤ÂËX2eP…ÏB˜Dd@ÐZú 0�#0ÿQg:¸í@pñTÿÅC–H‘×ÏHf°²ÌpG,Íó\³èãså¶å°z¥Á¸óa–+ý ¼ o¯¤¥ÜAû08S;ÓŸÀÕ/ŸáoÃu§Ò|´0ƒ™GÃ[œsZã³V«i LŠR6èãLýyžë¯õz]kè^7<R‰‚n ÔÍi÷æ·mŸ™™i6›Û·oO’dÕªUõNsaa49³±V¸xaç…SïãZ³á]ÖŸoµqrrúîmÛ͆ÇÅÅnÞ¨'¦Þ준€HÊ&�¹€f½¡ÎN°Y9Ç4¦1iLcÓ˜~‹)rû¨Iqõ w´Dñ<x¬Tªœ¡+æúá›±¬sšõ'EP<nÇbÕªVÚ£��T/ Ù‘0Êþ-"̹÷$B"¦bŒvFÝKXØï^ *˜PD#DmìÀ¢ÉX¼xŸ;4D€ ,ÐwÞ¶ À,TêH #¸¼((  ú¹‚bÂ1 ˆ!0ÆäÞQ#RX›mR³Æ¤iŽÆ"JbM½4Ül6}î²¼—e™¸~YÁ,ËÀ!pß^jÕ¼ìÙ{OƒÙ\›Ò#g90*fàrNàËŸŠ(VžªÜ#å+‚‚ ˜Ç­µš2PÝ ²,ÓìŒS“±èÆaÛÚáØ<«Þ[Ê\tÌœeY<ÌxàËQïO~k,P)™Ä’²Ú\è'!‰0âMÊro)k˜_],¥Î9§‰BMb­ðúe|ìÃ+.�ñ¼K©pÄÒµDýχ{+QšCZwÃ|Õ› I’n· E Åè:þ·Pá¡ jX–rÜÏð– {‡§¦Ò‚~ö®RŠsp„©2¢‘©l­ª#S/!km‘Õu¨$Go’$µZMU�¹/|Xkƒ÷æ(ÕvB²R-û´ê ¥¾5,´˜ ö~*2‰0ßqÇÖZõMXXX¨5šû�ÊÌ8ìÙZcŒIêIî°=1³mëÜ/Ößê—`€@£±ûž{LLL@l´'Iÿ4�2²„ V“e© È87Á˜Æ4¦1iLcú'D*pÃÀE°±ý§r€ç{‰üÏ!:¶âPº,¢5Æ–³"*‘�GæD£1éaB.ÁCQ’ è-!0€”ñäNÌÆØ2CŽà¡Ÿz® N0�ÂW0˜­=Žô�B´HšJ�E�I„‹Ï‰1êÀ!,TšUQžçPz` ! @AdÃA‘Qù¦7³3{ (@$4@€EÄ�{A¡Z�(Z·X€aËÜ6‹DD6©SD­©”É¡°7²óóóóÀý( dU Vu®ÇŒ1ì=²�DÎØúcž‡£4D`fƒ…Ÿ*`±÷¾:Q€µ¶Ùl"bš¦ÁàÃ$VMkPHlà¶­žØÃa5#y2ŒèÂ)]Œ1®,êùŸÇ  ‚pŠßFDQ1ˆbt">‚ß\²‹ˆÀ—Q9D½gˆ�ªˆpéc¿qhH¬Pá@è[¿è U0p,KA@AØÂ#¯†°”ø¥Ã®ˆ«Dœ‰as¥ç0$ª|‰5ñ«‡Co†÷Þ‘-³®2¥F£¡ù8ëõzØîâ$&á)ÝN[­–Þ©È<^JÊd ëX\\L’dbb"D¤iªAXz’$Zø0x"„ê3T$[-ú ë«^«m½kK½ÙЬ7nC333SSSD4¿°`ŒÄ$1ÖX›æl­æíÝšØkï½öÙg_$ܸqã?úáüÂR³Ñ¬7ë½¥ECÈ Â"âÄ k_Af°ÆˆÉ2ÛИÆ4¦1iLcÓï¡Ožˆ(‡LRƒ‡NŽHh1œ ãði¢>P‰›’2]\ˆý‡rp9 ’€ Z$õ1V+wPC¨­¨tˆ.´3!•AŒ&ýGô¹+k¤ ºÁGF;qßh ˜¢oÔÓ?YcŒñiJ�šý� "‹¨AÕøì—Vn€2‘¢1FS'€Zû5�H�¨¸.BBN<{@@"CFcïQËzYC� âw>'²"^"¬N„ÆÐTcZ˜ÅyŸ b(¡¢Þž&�,(R«…Mгl¯r,– •å¹µÖ“kE=.@F@¼®?‹’™‘¹;È[É0›„˜$‰ã"]x;3«U³ÛífY699©E1ëõº%£b £ š£ÉÉI½¢Áê­mŒÉ2 ¿+ D²À–+ Çè?Å"(Ì,΃¥H‚ì…ìqšÒRX¼÷´ h Oi8 ´ b]²´`¬NP˜¸Ê©ð#'ˆÊ¨Ãc0¯Æä¸ý �4Ä´X»o,hŒègÂJ ˜þ`µ_eFbÎÄ_UöÁx÷«¨†ïŒsÄ\9)Ã_Å{K|ϰœÇ_ɨl 1÷BçMLL¤iªõ‚7�ôò<Ú ͯ LצUö”«>wq‰Ä¥¥%m0Ïóf³Y«ÕºÝ®:,pTXD5t±6-h¦´µ" MÓF«9==ÝëõŒ1³³³h µ‚¾ÅZ eõ ˜žšXœŸóŽ>ú 'žxòÁ‡ärÞr÷æï~统=ûì,M›õ癵äó<uy½žˆ zßÿC&å2`ÀeçnLcÓ˜Æ4¦1é·žÂ!³@¾Pø»ƒˆEÄ$IXEj�D°‰qN<¨¿°°Êkn<"2„ˆà I-Ï\X\‰ÈQ„IeCä ^4µ>ˆ "‚�;iÖ[ Û„Õµœ�¨tZîŸö´  ±¨Q·À€xç¼÷^@‰™}æõ„š4fN] Qñ?õk"ȲžA²¤67ç}áa«&vD4=€gï'osçˆ,aPo4^0Mó"ZÀÖ�€E¼�0VG),Ôž†zh²EmÞ+Ô'@föžµ°™rŒˆ™]^8³Ï‰� ¼gd&"Fç‰HÄ0b‘c‘Á €0YCà½Vð�À’{Ï„d Šc�CÈH†âÓ1§Ì¨ E£<Ê -L÷�ŒýŸjGP{U‘hHáH)¢ ÈJÀ9X¸âkls’$õFÃ{ÏIQÂ{Odl­žeYÆ‚¸´°˜×jÅD¦�ò<ç2ÐZŒªœšÍ¦ˆôz=MX©= × ¨\™&=Þ�K R7„*ÚôÞ‹8@ „‡¦X¯$–¹Pr‘EÏžEØR]"‡— ¼ð:×<€…˜Ù ’öAåÙ+ SÍ–.CƒˆâÙùz’8çØ¹$I¨¬^‘'NhY‘¿ªÂ[UÓ!�ê¿�ýª*àKçsA)ázÙ æÂst^&&'ˆD$÷.륙˽úã Óbga%®¨×ë^œ§" A ¥€qQsðª°QŠÖXÃëD0rž á-9I\k=Š_Ë”“ªèù—Î;£·iåª@X7®*;ªifYÖjµ;`ÌgæéééÉé),£rt½dÞ‰–É_‹×!Ô› ÑÛ N"Bk ¢Ë2pYÖ¨ÕÀçyž¦"’‹�³x_³¶³¸h­µDì\ÖëÕ¬Õú‹Þ¹F£áœK¬U¡ª×ë0??�I’¬\³�ï}½Õ 3ÏóÄÚ´× {¾g^·mß|×]ûì¹æª^ú­¯]Øj´ö}ø~ [6lß²¡Y¯w]¯³8=9•f½Ä ;'ª€bD@!TQŸ¦{¡&øÁ÷/Ýù›Çô�Òƒ“óÎ^éÁOcÉÓo%=´û¡ÕÛ_5!‚gG@z@fD$$2dA@X“ç ÊIé2�¥©P}P•h +¸[=ˆÜPž°1ò/'ž}lD€àÎZtA+faä¾~—Rìê|BÄGAÔ�°x(sË# ¢BÁŠ•¯äkßôÚÑ1*Òp±è0•x¥d! ’êG„Å3”J×Q¼„ƒ8Yâñ²HŸADHª&�$ˆçúæÇ�˜€‹¤uXŒG¸[àó<èbã§^º?‹šŒ"e�� �IDATZ‡0ñ‹‚Z‘ÌæÍ›'''1iš@£ÑÐLìF#Ž ��-¯X4°´ÀBˆ*Þ¡çZCQDÔ !\—Á�Ázßç@iª dï¶”ASvÐ, “ª$Â($Z¡1ÏÃÅÊt„AÉT) ©$ª"¡x2tr¸ýh™—Ù¹º}ýéÓj �€…ªÒ £�YKeV”Ð ï}£ÕÒ…“0'IbÓT}@”óêZÏÌI’�€c_76fEèÆ~R¥4 ÷<f`å Å Ìr©b€¡”uÃðŸñâ'�”eSã=¶P¿zS$¤Ü;ÍP†ciiÉ9799Y¯×É}Dß’³oÕ[}%qDžY†bÒDÄ©c–™;N¯$km£ÑÐäy®íèÖ­ù TÆÔ»Ao(¶M„8i¨¦\ Yu «ÿ”ˆ0¸<[Z³zv~nëÅ_ýâÅ_¾Òˆ°Vk4›ÝÅ…=÷ÝÅù<륬×1Ö²xcŒ0³²UX˜Y¥Ð•iwŽwô“vþæ1=Pôƒï_ú äüƒ³WczðÓXrÆô[I-Á~hõvÇôÍ‹¿rÿa�C¦pèFÀ2}»óÞ"Y€Â¬BšÀ�Ø zï–Çð†üóQ£ë ÁCø6iñ »ï°]¸¼^Üe•lP/Y(ñ0–鬸tšŽÖHÕ‚³·”Ѱñõ ‰ˆ ðÑ c¼ô�#aÀuˆ´Ú—H§,âRº?Šá¢ÉEÄkv=d—U t¨*m…™}Ì 6ND@dï‹z造…)�€P4!ƒ æúY<4*ð{`N£„�€£ø<²¥Òt?A­0^E*x/€^LÓ´H½–çê­Ê‚ð"$t†W4âK0¯ (Ë2ý»N,\¸+�¾.Ȭ© ˆ˜'ö‹ø9L<,Ozèyx©ˆ�H˃iAâ륉×N¥â0¬­ôy9yXVw04¥gA /¬6ÁD¯Ú²$IµK·Ûm©ÕjkÖ¬év»yžWt.X„]À°`Ø0‚ñ\¦ÞÄÁÇ{Z<Ò vÄH#PÍAïQ˰À ú°Õ™Ø¡¿"Ô‹-ó2÷aØ0U«R­EXòÌ;ç¤tCˆß(A´ûÜEƒ1FµlY–MOO«j¦V+¢“ ÔÜiÕD¬ÕjÚ}QP„=Ù9Rˆœ®µke¾C]t† {ïç·/$Æî²jånkV @gi ‰Ò^¯Õn·÷ÝÛ³°¸h|–zŸKž=^ZUTIXÄ©ZvXc2¦1iLcÓ˜Æô»EEAC)ry�±ÀL, hAM;‡ÖØÈÖWœÛ¢ºññ!KG� f–!ã3�HaS”ŠH€aPâ=€za[¯AÙ~ Õ rîe°9Ž+¿"Pô+D÷Ã÷¢²Bxá_= DÅaêq¯Púe#ãŸ1<«|–(ÆX¿2”h¡D�(cz™‘HʺˆüÄK8}œ3øÔg; Ó¾ÙÀ@*ƒ=gù^QÉê&`©<,¼ ‚š`rr2MÓ,Ë’$i4Á2 %Ìà¨ZD’$ªŠ]Bçƒ˺D”ç.t�Ëlˆ˜eY?´{i½™ÊTÄΣJÇÅž Q&üø•…‡$è³qe»Ê=•Ö¤,1<}#eå³²¬F*Ü_i™ÃÃZf¯Ñh •5ô†´®qˆH³ÙÔzµZm~~>O3S«Åo1‘ö¡B•nT†s"9‰Eex•Æ»‡ÙRàð¡+ýÀk;ž¥ÔîWÚ%ff6‰%¢Ì9u_ZZZš˜˜P|."êºïˆ(A{2Ȉ)~„<ü!ÐZdTtÚí¶*#–––¤ôÓ(+ÊÛÄÄ„µ¶ÓéÍB¿þ.DjÕÜi(Ã@˜yÅŠi¯—¦ÙôÌ´^´Ö¦õ:3§iº½ÛµÖNMNæÎÕÄ&^Šå¯ñäIÄã™ô³xŒiLcÓ˜Æ4¦1ýŽRŒ1¡¡5 Eʱ  Â0"ò®“ Þ4X7QDŠ˜lõz×3©gÍû…ED²ZŠõ(R·vC‰-“`3[Ö¥ƒâTKhP(îLøzUA­1\„Hs¡GØpz†è¬ï4˜‚PD47^°ÎU(>Füì{Á›}ÎlŠA�CXû€ms$]Œ†ÊSHQWB�ž`AMÿHB€šC@„™ÕƒEÊìú:¹Q v,B¥„~âN{ß•-K@Ñ Ò¹€AuQÖo§¦¦Z­ÖÂÂÂwÞ955¥ ƒú£\°ŠGuɲLYªX¨`d©“ ¯ÖDáZÝYÀ䱪¢Á ÅA…b± ø"WEÑÀm{t…jöX†ÆÄÚ½ mÑ!ßõx‰…±2¢##Œƒ>>ËMpÜÏxø…BQ‹Aªï�YëØ‡é ¬PtÔÞû<Í‚ÀÄ=/uaÈ "°qijƒÃ¯¼"ì!…¥þߊTM¨«Ñ ÚJ•÷ÞÖjy·«ùæææ¦f¦[­–Þ"ÖZFpÂÆ—Ú"èûVTvËxºUbÑ•Š<Ïu‡Q'‚^¯§žª†H’D¯ÏÏÏk Ãx÷ÓØõЙ˜˜ÐÛŠum(Iu4��UäU8ï�yž“1F}iq1˲Z½^¯×UZ˜9±I»ÝÊsÝ®±ÖygÈ jNñ�D:qÊF @2£ÝÆ4¦1iLcÓ˜~(µ¸´(NʈVqCʃ¯1† ‚5 5DÉ¢¦eÀæò”°$>ÇÖ*ÏEãXšjõtÔ¥©­Ø„ Å 0Æñç�*(†%a•{Œ;/QU³ðÆð¡^¯Ž Ôqßú¿ …¹†pHŒ‡Ã1>f×pôæPðÜ9—¦©Ë²�ƒDMsê© ì@)”9ý–cÀC€�¹,îD:‹Ê�+ˆ(†^�poÑÒ0Ì óËÌ}µ�c8Ê)Øëõ4¶ybb"v € ò¹ï“dCq,…¸èRŠtÈjÓ¨ 0eÂKŽ…0´\¹{dEèsx|¹gu¥ ?KcøZaoEÒÂA³†Ëk*ò°ãAéJ/î”é~C\åÞû"§eßeFGâJÍ*6Æ@¹á ³1îa<((71Œ”#]Ÿ Cª2¿œ¶bi±ä@)'±� j˜SGRÁ¢²d@Úëa©‘T½U<dDL’Dó<gç$èpeB.•xO@Ä,ÏC0"ª{3ONNr™>Fß¾´´´´´Ôívµ'êhÚ ®7Pæþl4ˆ((a}>TòàP™!’™óêf¢úÕ>hA­ãè½OÓÔZÓ¨7Ò4E‚BMÐwì@"bÏ�ª¼Ó˜Æ4¦1iLcú]%D@$Bu ÖêÑêF꣯¥t5WÓ¶Á�˜]€‘ ÷Q**ô"2‹f f@ÒTt ‚–ŒæÒ�S$]gÏ>¤Óã2û:�„ŠYÁ+Þ¡•1ƒ , ¦ãÊ >N ´b[–v†‚3t8ò#"”ÆmT@h›#ã hÁ 1 #,r:ª‡ôšÁfc@(νïïÙ3"0k˜‰� q^­�b´ŠBÄ´ Ī Òå~­LD˜s$- ’ ¥ÄZ2 Ï 0�çˆÈDzŠ¥¥¥^¯×jµV¬X±m۶Ї 7 U‚ú)€y.°©UVÑZ£Ñ`(#äC,™#EN d"BÉ\n¡ðã 2÷Àq5WÂrœ‰YÄ£"ä+B>Ç2? wc\0§”Áº¸‚®!Ö¬œå€“cD:ÜÃÑ£ ªhß¼¨[A–gEÐ"€ˆú)uÒfÞyöâÑ9Ç .Ï,�0ƒ/¬ôpààß/ŠX#ƒZ†Ê’ïßO˦uI…¾H'(� e:ŒÂ¼(eо±Ö¦yVo6ºi¯^¯¯Z³Z{åJõJΞ€ˆÈaïm’ "-³«Œü¬DB!C…îY–õz=,¨/�­]»Ö9—$‰&,Pª”ªgѽ÷µZ-Ë2ð…ƒOB ""£ˆþ¥(ŠÀ !#2‚‡$I:K¥Å%k±VÃOšÍf¡'"�ÍfÃXÛíu{Ýn³ÙB*Âíä „vb ŽiLcÓ˜Æ4¦1ývjØ;!)ndf$B[œq­�öEe;)m‰PI§)ê·—ÆØà\À˜¨D¯ÀZ…‘02éýZ…[Ë×Ç7+ª±õZ¬AˆJ<à ž¡Áïà".‘=0œìJTÒèÙḥ4Mcv…ÖôL,QÞÄp�˜ànàOaC+²"úA׌0ºð"Ž´<� žÀ¾D×ê)Z@h�ˆKéG D8!â'Iúö·åxê«Jâ=QimWƾ¥´p9 ¾-½^Oý³,³Ö®^½šˆ:N£ÑPæk¢ufVóf³ÙÌKi` �håŽ"Þ9 ½n´š"…ðdYÖB$DÔfCiúÅÅE[~‹o+fV8S‘¥ …ᇀ…˜ÃñƒQ¸ä¹b‡­uI¡‹ôtÁ£G› Þ=°Œ’bç)Æá¹ÕļÂWR¸fA¡ÂCÔ†Ün·!*F¨ï&"ïBˆ…ª¸mȽ"îjŒ™+:‚ ´oñVVt»dZ<;2ª b;@aýöÞ13 b¯×k4Á‡%ÏóÜ{dn´šÝ¹ÞôôôöíÛëõú®»îZdôô.$$"-ü‘¦©¦‰¥h_9_ˆ4›MÕTæ.èsëõzžç½^¯^¯OOO+nW§½ÁZ[«ÕÒ4­×ë*uºÖŠù*KÖëõð"íy¬hˆ¹Ýh6�Àå¹þªw&§Yju®—çùìììÂâb‘—WDŠÊ-è™Q�‰ú•zÇ4¦1iLcÓ˜~‰ˆr—0Pž‰ônˆ, äâ h s“ Z²Î9ïË2é Þ;ç9�\à‡$Æ‚ÎQ0 "ZÔÓ0º,‘„ŒžésÐHjÎ Ø-À fCAPZ9°´<½Ʋð›È@ {Hé¾®mjí.J¬ˆ8ï…%¸h’p(Câ±V««™ 8'"ê÷˜`¦¹<«£«!"ÀÎ"%…} 1³–. k t™C! ºPÑ=xá €gVß`ñΉš¢t"�’g®Y’2<HD�Ùû‘� �š"äm¿Hžš“$ SãŠRÎH„(U@Te~•Bq¾aU3‹ äèv»µF]“Ø5Û-ɽ3`ÐPª=¨%Ú4Ϻiorr2±µùùyç\«ÕJ’Z§Ó™Y1«í5>ÏzYj­m#fYn’º÷Ì@d-3³ç$IØ{!“Ô“ºNº¬7ÛÍv»V«@·ÛÍÓT‡ ÆXý© Q¹ª0)l…‡aê+\’2œ¶ 9*p²c€^cÚXÍ„eöͰ^ÂioµYÕßéäyNVSl7Ú^,!a€„eY«ÕrÎ¥®¨OYÈ`7íQk¢ÝjµX“Û Ú1퀶Ón·ó<Ww¯×#¢‰‰ ÉÓ̘bù”«uè d¤q£ÒÂ;+\ë û ÖÅ„A¥C@ÝeÑþã8ä•ÛëõÚ“Û¶mkµZõzˆÈš………^¯—.fZ¡3KSš¤^#¢­[·ÎÌÌ4Ú­Ôå”X0dÀˆ &µ�äÎ�ƒ…¶¸ß[—:Ñp�R¤?„"Ä&TŸ E êõ:3w:,} ‚"žq­ž›$I±¾ˆDdrrRë))ÕúvZÈÀO"òºÛê�಴g˜£Ä&�‚Hµz½Óí-�" k9IkD?:Ó˜Æ4¦1iL¿Ûdm €¨Þ—‚ì¹o¸ŽÍY Ôž@šÚvÔ¤t,Ó¨IËb—ñ³}tÇe ·�Zôz8#Æ ý¡²j€’1ïqœá½Šv´Ï9¨+´—ƒE]¿ÕaÆ0)(4øJ–ŽEÍÔD¤F¿øaø!‘A`;k7°TXÈÅR¥ÜhS †Ýn7MÓá4øúAM|-¨Y/4>ÈE1&Iâ\}Á"ƒêxî`ü‡GC‚ O_åzE Â6Škµš:ÌÐét½÷j᜙™i·Ûú•ˆ,..fY633355¥.ÐívÛ{ßl6EdóæÍNgÕªU333óóó‚˰Õ•¨ä‡Ú«$IšÍf«Õš˜˜h·Û­’š5 M·¦곪˨2”ª€ùƒÎKå9ˆ4”¶ô 3 Jœ ĦÊzWx0=G¹ 3";AáETFÔÇâ6"jµZív[Mßqß ÚbI~_«ÕšÍæäää°P #öøḃ œAW5LqûáCXΕW‡+•ŸÃ-„þhŒ½‚m[«5ÛíÖD»^¯¯X±bzzZ‘¹s Õ›f»µqãÆn·Ûh4¦WÌÎÎÎ&I’çùââbåEÃ܈;¬û›¬«\…RQjÒ`ÔOX•Z˜@DôN¨J¦*ÔïFÛ©ÕjÆÝp°"ŒöJ•­!fax ;I•?jý3³–“uî¾µ<¦ùùï¼}«�€,-Ütý\ï7¦ûqÿwÁßàâÛò²å{ïÙ?é<m¦tóu?¸vÓ=wvùž¸… wÜÿ ºvÏ´,«¥·õ¶;Úê?w÷ W\}{z_åù[®¼òÆ_ÛøͯÓ˜~]ÔßäBþÀTk’ÒS £ˆZ€è h�� !˜pôÞ#‹EÒ¼U ¸Ñ3xv(Þ {¢!ýúÙ ¡êg'ÌzEÿéÍz‘Àh8êÀ=ÅWƒŽ˜ØÃƒ‰ ª‰Œ#EÑ™Á| yž;ï1·K„Slé—# :—Ð8^ÏýÃ:„S8‚ €cËp°¸.MÓ^¯§�#À<�`/\z#!B¸‘¾CA€aAM€ež¹‘j‚ .•!5D0–Ñ)T&¥©ô?ïÕ¼eŠ·Õ>_¯×Q®išÎÍÍmß¾½ÓéˆH£Ñh6›Î95]ªSóŸ€æ`S?j.=ð%Bã±Ú(p@Ù¥­©ÂÂZkkk‰IŠ4}ñcÑ2À sL?èKã_© ˆ§¬ª¡Šî‰â;1h¬ÝdO,CÃ󨤾èõz}fffvvvbb"Lb£ÑP %V·Ž²°I]ÇcîáÈ¥=ü!-ŒÙ¹Gª4t ßEärýÑéVpÌ h­ñ ¦–´§&W¬^µj—5kvÛu×µ»¯Ùm×ÙU+9lÝ~ì¿rÍjkm·Ûíöz€Øh6‡G¶”˜Kaj†5Œª)€RMô’A\ƒzKÅ@U á¶xu¡ í‡|ñž\af °gÞ«™ \ED DƒHÑ„„D0ôGeL5r›/:ãÂÿ¾š`ñ’o½ûÌÿÛú›:Ï¥—}è ÿü“|ö^F²HÞK‡Žz|ÇyïzÓ7¶ëTç†(ÿñû{Æ{.¿G”¿lOøæ=ëÀŸ½ñ7QTtYVg—œ±î±ïüŸßŒîâ"¿þ§<åM_Ù~äXî>ïOŽù£ÏÞúkš“_óëÆô"—¦e%|{üU ¹l=ï%Ûë_¸/k½lâî¦'á¸*¢é±�ԹʄÌD<dl‡ÅÁhÇÒ 5Ü© +ö0Ô±unä™;¦ø\[Q„Q„c+†¼ÆGØ0^tpžÉ *ÖgÕc<NàL¬&Wί-c+.CyÐPh1á(« ö'œªC *QìDýª–•Óyu0lE@ Ì`”AEì*@.>ñWÄf9´0<ÑAœ ´ŸY–i•UÅíjÃŽ� lÒ4ݾ}{–eSSS�¦©FJ¯X±¢ÑhÌÏÏ/..6›ÍЙÀR‰p2DÞ+±t%‹ úÔÄ´…8à<Ö8ÄŒ­HxÈ‚‘\½¾¹ àÇHï0’*k*ôvx)ïˆ@ux—'Ô Å¼R¥€z^4›Íz½®Nª ŠX0pH+/C…²a¼±¹oÄ_…Efp|‹—CåEAÇ;ÜÃøµºkOÒ4M]®}[\\̳ÖÖ uTQEX ñCž Ë Àðu*Ý(h°î@ˆˆ©ˆîrK˜™“$ÑîÿÍJ  ªÜ¦išçy£Ñ¨t8toX¹÷êÞb‘ã ¨}'<ÔªäîøÆû_zô~+šé=uâß}wKÑïüæ _ÿÔ‡Ï6Û»qâ?~ÿîˆÇK×~ü{­8心u0¿ù¢·>ûÐ5­úÄîxÁß~s„A—7}óÌãYÝj­<è™oÿÚ†Áü-ÿuòžõ½_wé0ê^wÖ½×TsrǾâW/…«gŸ~ìþ+šé=wê¿^17B.²›>ÿªCfþà“›õ»íÿq\s`=7?{nè!¿é’7µò1ï½>ÔÏ]ºæÓr̾³Íæì~ÇüÉY׎4…?�2wÍçßõ²§þ°»Ÿö¥ûdFýÍS÷{ç|!{ÖÉÇNÜ›‡øö>cÍ3>Z=ƒòmžsÅa'½àá˺6ýºèÁÓ“ˆî«ÇôÛJr×W_{pcòÅìƒûdþŸ=ã™G¬¬×§ÖùÜ3/¹s¬ÚøRçü­yÔ»¯~(ë ~-„“ëžóªW=ûöoÜ^!ƒ ½‹ã+j-"Bc€H€AúGLD!BÐKßÔÌBQ[×G)úÂiKŸã3⎃•ãoÀÄ8gØ} àö‘è�{B¨ =§^,r4°º‹ãv}6ZÍK¥W^Š$óMŠšR½÷N8ŽSí‡Qk—´9f­^úèýNœ áý¥þB\h3tLSh2?,ÍŒÞÐ`®„ 9ÚÁ|ÅX1ÌBÌ„ø,Ê"žY-ŸZJ@?0Håƒ~¿Z>œÄL¥ÂK]Ó[­”aö:œ¥¥¥v»=33ƒˆôž$I/Mè…$÷N?¨™·t°d Ç>÷.s¹zV«ï† J¤}3¦¸9°n¸«PºðÉŒ“º©v Ùlª>I)cCÂZÛÁ¼ÄË'–^L XÛ,”L;ÔA„ïaŒêÇ®J™n·ë½O’drrR5êj$2ºéÈJ‹xñVo]]nXîþ°îâ. ÏZÜ[¢ð­—¥^˜™ƒþˆˆŒµfÓÖéei§Ûí¥©ó^�:N¯×S0¯;•®Ùb£ˆÖËŽ‰"kÌUºiÉÕX¢ÂmaиUÆÑ`Ú " ¾9½^ODBJE;Œ©S ˜áƒbvŠP#]F_˜Ë´;³F*$sW_rõªÓ>qé5Wùmûýï;Oý›ï¤�àÿï/yù…«ßzÉuWý×ó·üÝIg\¸U�dîgŸý«ã}ì[¿uWóú¿üÔ/¬}×÷7l½áü-|à”7\TAà¼áìW¿ð£î´ ~vÍ—þ´vÖ‹Oûä-}™ì\ñî“Þ|YžŒ˜ÔÞÞuÒéÿsÄÿ»üç?üÐãú†“ÞþÝ%�p?~ÏÉvÅQ¹jó]?û·ßÿÅÛþö®;ÞŠâúŸ3³å¾JT©" Xb –hPbl`APìÆnlQc‹F&Æ&ö bì-¢ØEDšÒÛ£H}å–Ý9¿?Îîܹå=И_LòïÃç–ÙÝ™3gæÎ÷ÔñW¿ØW¥ç=Ýø]‡Ÿ>qiþÝþ¨ÇW®gZ7ç¾ÃzlqÈø5öEá²7ÿxÊž;ý×/-_ÌàÃkŽ8÷ãoŸºrõ÷˜uᘫ>(Düe¯ Õ/ó£ƒnY±óù÷Ož3çÎýÿuô%5¿õøópð¸½*¿Ùe:ÈE%R¨>ýÄÔŒ=¬ï¿=Åæ÷§'}KV·Ñ%…sþtôiÏnLîˆÅû¤Óc›QçÜ;y΢/ß¾mÏE7žvã¦ýhÚè[…AØ–øg3È<î×W>Ðýw÷ˆ�(‰øED”BÀ<zá#”}V¶lPè/Pñj«,¼ ÅÑÒ ²!}ssŸ²dkìƒ8YQsg(ñS0÷±ûO‰˜&L×�»ßÖàv>Êë’$æ)voÍé¹ÈÔlÚ˜Ç×Í0Í1—¬Ìf lþ›n«˜¨M±ƒRÅ ô}¿ºº:•Ji­Y‘QÄU›™ö\ŠÙBR4eèEL+ëéP·ÉTWWWWW3gø&lM§ÓÍq—8ú€ˆ9[UUG+°W|*•ò</"m&ÂÖC™!›o<Ûé<ŠÅL6cçÆ&©„°âöö‡ˆLÒx~kžX¤ßálˆ¥s]ÊaÓ%Ó+{‚Œ\©Âz"¦ñ-²ç±”ì¹¶»d?,'ãL–çK‹ÆeV�(¥x¢Ã~ ´€K9Pô¬V¸W4Ò–î_ÄðRVµg7 B¬mß®ªªJJÉ ÀKù®ëJ×áÿA Âó<ÜRò¬™gß¿ôY¥´õ§d9XAR­À¸>aáþf †çyœ“’sÈ$ õ°¦µ¡¡�:tèàûþ† J;‰…Ú1*üù¦HÅÞ] 9PfƒÝLÂNÜøØ-?1lÀ =N>{t¯çÎÛ@ÍøÛCŸmÞ 'îÔoà>—\5Þ}îþÖ€Z¾ y›Þ|ì„îù¾Gs\óu5�� �IDAT¿˜]±ûa l_Ým×ñ£‡6-X°¶@3¥—?óÀ?ºþìÚóöÐø™×žÚ÷ûŸZo¾Mï^uÚSÛß~Óµ¥] Þø‘å#/½öða}¶}Íe¬yì·2�ͳ¿XØcŸ1{÷®©éýã#÷ë³zþÂ+?®ž¿zð¥/¿pÁ|:Hp+Ûµoß¾}ûöbÊ-¿yw×ëoÛC€š÷§ŸtësìÓkpÃü¥]N™ðæowóÌ5jÑä·– ÖþýÚÕôÚëÂs÷]5áÉ)!­~büݸ{†²Wm|ñ’3ßùÈ«wýüÀû×Õ¦Z3[‡½sÉ>>a§;N>`Âý¯lP-·¤ õ/^úÄÙ»Ýqü^]ã—õ9  ³oÞí¾?Ñ��ÁÒûFÞs×ä� ªäÀ»o=hùf›A“Ñ5n �ZóîïÞµwmª¢ó ‘ç>6'�@_?ÿ‹=‡ôl_áyU]·Þ¾0ª’àóúID¬;Õ*jî„'¾ØeÜ¡[�HÏz䜑ƒ:W¸^U§>ÛíwÃG!�@´ìå«ݾGuªªÛ¶£õÂbÆ<ÔøÙ_OÛ{@ÇŠTû~{žüçOÇ‘`Ñs—Ú¶{•çVtè5tïK_+ö(ÉÌ~ìÜý¶éY[Ù¾ßÎ|ìK£Ý)èIÓœµÏV+SU]†œøÄJ€ðó›GôªMUtì¿Wr™šyÏÑ÷êZã{•u#oûJ•í*}ýìy{ éÕ±Òs+º =ä—¿ùÅèz·«¨ì<è€«Þøš;§W¿uãQÃt®ªî¶ía¿5n;¬UÿkÇîØ«&UÑn‹=¯û8�Zÿìiƒ;UúÕÝ·K.£u¯^¾ßö}:Vx~mÿ3_É´À¨ìä«FlÓ·sµïú¶yÎ5—ŽÞ¿ceEû~{Ÿ÷ÌÕ*£¾EŸßvèýëª}7Õ¾ï®G]ÿz}±ý5ûÆ…ÛõèPé§:ôÿÑYÏ !üà¢Uûܵ\�Ð×T;äÒ)›²ÚêUoÞpäÎ[Ôúnª]÷A»ñä2 -KKö«'/øé.•©š^;yã[«yw¢ Sî<iþR®_S7`ç#ïž]¼úÂÅÏ_~ÈŽ}:TÖôÚ帻¦6ÉXnþ3—¼M·ªTeÇ£ïúRµð hÚ­‡ì0 kmÊõj{ïqêU¿>aïAuU©š^»žôÀìØ»�@ðÅ-§ÞZ{õÇõ´J÷I¬¶ïOwÚ²{‡ê”ƒP3``·ï“âë¿¢Y×ïä"¢¿ÏËuÉ:Ý´äÿRÉ*k‘J÷vZûШÚ>çL�roœÑ«îøç3��ỿÐéÈ öåé/î=ixïv¾WÕyà1Ök^;µ{Ó^6cóiåä» ÛÐÀ¦”ø…‰ ìÐב®C¨Q›’ ø1¡Ñ`B°±ÔØ{Q ”‚Ͱql|le•Ôê…yþ …@ßÖÇh¬æZkøùO Z+äðF…™ºŒC;ƒ1J ŠT ÚJ `ßÍ|k«lÃF­„ …„<,´ &˜¬I5�v "�H€šÀ¸5"aò(!’Ãw‚Q1‰üWJù¾ïû>Xù $…¡ ØJY P Vm8J…š–æ·è „1@ÑXSÞsVvDL¥RµµµíÚµc¥@&“Ù¸qcCCCss³)ÃÆykjjj""'D,ÊÙ^¤3¹'lCgâ°}³ù[Û„J4q¥rHVòBö›(µÉhhŠÈ$ß!•D|”²ºÈ—ÁîZj;vœ±YaOMK³fsÆPÑL !@'Å8EM¹Ž "qe@€Ò?…`Ÿ£¨)“nll´u‘¥¢h÷°H\±Z&Ú { ÛO)û\Ó¬èué9ÏŸRÊó<á:”�æl6›ÉdrQI¶‚H+ ´nýú¦æf¥µ’“j/Xö#(×%ó!ê›ÀÒÚ”Õi+é€=v­µqÐæ2ímý2‡–¨$/féa«ÊoADD@dmÈP²5}+ÊN}òúŒÜ³3Bfö : ÆÊ�oèö[«YÓ¿Š�;þôò[/:h`µ-RîNüDN¼æÊçænXñÆïï?âgE¦ÚhÎôÙ0d»Á�€¸ý0ÎôY�@úýßüüÅ=nûÝþËqõÊ3ÖõÞfh-�T Û~˦™_,QP³Ç{®~àW7MZºqáÄ?<Þ<ægû·³¯«Øí¼[¯»m‡rè\/|ð·O´;ãŠqÝ�Dm¿‡ß¶G‚3ìäß_â®u–fd÷!ƒkæ¼øÄG«²QnÊå®Z²<ÄŠ^Ûî6|‡¾µe¯¢õÏÿušƗOX“ªîþƒ±7¾³¦•yqmsÖÇÝ9éø Å®{ÿã†ÚéÆÉW¼ðü×}O~ä„ßßµKÝ”I¿¿uyPÛsèV™yÓ7j�µpùWks ¦¯S�zùНÖ× ý×½6‹6¾þø+U‡ŒÛ-zéý'zãŠîùhÞœW¯ìÿæé£/Ü @Í >™Zuüßg-œ?uÂù=&¹ÿ™ÏÆ#uw¿ùËæL&³áÑÃ|��5ç©'¿>öžhÃ+çxÆäþW¼<{éâiwìµîÃéõ šù‡±GÞ'>>mÞô§ÏHýmü˜?Ö½pÎè §nã›_~õîw›uÙ¨³žYC�Ù®5þñÊ3žš¾x鬿ŸL]”-è|úíË=còV¿ysþÂÉWõ~íô“ÿ4—!`AO^:ï ó>zÍ+3Ìyçá_î]‡� û~Û¤Y g½x~‡gO?ûþ¥@-ÿdÒ¼m¯ÿdñòùüå¨~T¾«Í‹¦N«ùÙKó–/™zÏ^so½yÚŽ7¿6sÞ§ÙgñïξåÓ@/¾÷øÃok÷èôÅ39dõMÇ\úJS1«AÍùã¸1 Ž~pʼùÓ^¼cüÖ�`Õ.çOøtÞüî±øº®y;@ sÞyWŽŸøå²¥3_¿|w¿FEË?ÿ8{ÐCs–-›õÔ¸¦û®{­çåÏN›÷Å„ñ¹ûÎúÍ?2­0ê[’^5ãÃe;ÞðɼE³ßºmÿ5·vèï¦bûÓygNýʹÏýÌyüÌË&®wv8pÿ.Ÿ¾üÆZ€ô“>n·÷>Û:åo“úò¶±‡Þ¶ñ°û>^°ô«×ÏìòùÇs¨EiɼwÅ!?{©Û/_œ9ï“ÛpëáÇýe¡½üÑ“¾âËÝÿ0yîÒýzÈÜf}]èv~qÓØcž¨>çù9‹?½c×Ï/>öú)öP²^5úøg;œóôô_}ôÔu÷“åzõÌ–íò‡éK–Ï{õÜšg®ÿžüØÇs¿|íüÏŸwÙSkÈÚ…�ÂY·žõ—ž×Ýqto{ûjiŸÌM<ª¶²ËÖ‡?ÙýW8ñ{Eó_HrÈÅï7f2™†WNëI%ëtÓ’ÿÿI%«¬…Ÿ r{{ºÃž#¶[óáó@4ç÷W­Ÿòá¬@-|ÿý¯w±»å…Gk'\þ‹w†Ýúùš†•_<wÕÈMÕ&7ŸV~A¾#JEDÄž�€Br¸}|”’c¹9Z˜³1üˆ¢H#—FAA8„’=C…SSj¬ßv´6”œDÑҘë¶b¹m(Nål×¶O„ý­'íCaP7ýáüÞ©TÊ8ýò…¬&`[H$a…Q¤‰0Ék`@á¡\BA#Ø7=gbi«µÖl~ÖiRš Q�i"Hš”Ö*Ô*"ˆã "PTDÈ<_Ï``#*éL†ˆ„»-°B‡Õ�y‡y¤1ã KÏ÷EÈÁ0„ ‡g^£(†¬¦1Z FÑð*‚ææfvbWJqéx"Êf³ét:Çqjjj:wîl"¸*ëA¸8"$^Ö<F­uêHçó °*�t-BDÄeíeÏJÉf„"bÈþ)@fE0ù¾Ïh™ ó½AbÅåöv¼O˜P¾cŽä·&á…C¡,™¹³—¶¼xL' J4šk?i17=¿ö³u\¥ð<Oz®ÙdøaÚ‚‘öMŸM8í„°<;ìT‚ä‹Õt,eAK|3i÷¡ès*!°¶ûq¥ýBQÔ”n6%0Ñqœ”çK)^%àr³¼YñþlxŽ… ¡pß‹ûP8R2¥R “,²A?T*‚l°Š‡ £,Aär9®BIEJ—¬×ã%V]]í8NCCC:®®.X̽2Ê뢙ji^Z"ûòDÈ•R: ƒ0£(Ôê[òÃ…;娿v¹æž³K tc“®ª®æ«kª ©¡%U?v}ÍUÃçÝ}ÆÈ­ìÿçè°³î-hÍ}V9Žã8»Þ0§¡±9USý±ª¦2hl �¢Yw\ôô6¿ùõˆvFBÃ÷/è;Žã¸Ž{n]c3TÕt¢±‰@ô=þ† ú}tóñ{ö2þÙºN߻ӿzRä>¸ãö/~|ÁYÛ³O$ÖðÛg¿hxU ÍkºñásªÿvHÿÚê®ÛŒºvÒZ§"%¡f÷Kž˜xí~[xhôÕôYÑ6ÇÞýê—k6ÌŸpø×7Ž»à¹rÙbn´o¿Å••µ5ƒÆÙ2\_ßB„1ÕÏ}ë㪑í¼]ÿêÎ[>æçš_œ93¨6¼Ãò—5­ÿtyvËŽéO—­ÕÐøéÒå[öÚq3™Röyë_yüµö‡ÛÕ½è©û&õ8ó–Ë:¤WŸþýµ‡nxô¾Iì¿!ªºõïÓs‹A{žrç­ÇÉgþòŸòP8^*•Jù®��ˆf<ùÔ‚=Æê†@^ºç‰àÈÿpÌÎ}»uïÛ§ ÇcDŸ=rÿç;\|ÛÙ?Ú²×€ÝO¿í²=æ<ðð”`ýK÷> ão¸aÌv½{ý››O¬|öÞçÖPîí{ï_²ïUwœ¾Ç€îÝz÷ëV›ü࣫÷ÿÕu‡lÕ¥ëvÇ\rü€Ï^™ô5÷äÅ¿<Gßø»#wê×c‹Á; é*��Dû~öêÕ½ßðÓ~~põ'ïO‹Oún§Þýºué> o,ÛÕ��+:õèÖ¥ûƒO>tKUÕçƒzõrð‰£ú,™>£ô§x{‹Ó~{ö{tî»ïÅçÿ¤éõW¦†…¬†hÚÃ÷NÝñâ;ÎÛgp¯ý¶Ý®ëã¼îƒ‡õíÑkèA矰Ó×}¸ˆ¹¨îÞ¿W]ÝzVo(Ï(��ô;të֥뀧¹ƒë÷<´w-Gœ<vÛ†/¦/S-2êŸ!§ãý·èÙoûQWÞ÷«gÜ÷P¸ì´å6º¶ï°Åg÷Ãà«9K•¿Û‘cêÞžðÒ×ÁÔIïÊ}Þz”N4í¡¿LváŸ.9¸g·-ú÷¬��Ô‚´d'ßÿðÊý¯ú㉻ôë5hß_Þzî–ïÜ÷ä¼pÉ„{^íxê¯=dÛ-ºõлc ÆŽ>{ô¡;_pã±Ûví²ÕèËÎØeñk¯}•ß^s“ï{ ~ÿ«o?e÷=z ÜaX/¯üƒø §¶®G]]ïá§·§]n×·GŸá§³[4ãóy‘µ ée_rWÕE7Œ)Ä[¥ûdLþa­Û°ì“‡_uŨs^j ¶ÑwBˆ2ÞÕd ë´uÉÿíkÉ*+Û¬üÞží½ÏÈ­fOš¼šôòÉo7 ÝzãÛ“—hZóö¤ÙÃöû±¼?ÿËþ¥¨©ñ×Ï›6w®î>d˺be«›�´ø ò݇©GZ&¡Ij‘& Gº®ãyÒuQJ ”œfŸé8BJ@©�A:(.ˆ  ×GPD‘Öœ[Ÿ‘PI,“Í·S¡ÿ°Ežd[¾¼} 3rCrÜdÏðË>2ò) u€Š$'3¶FIáH)È‘Ò÷<ÏuIël6«�IH""BFš@éÆÙÐA  V¢ "t$:±o¹¶ô ÀAI*ŒT! GHÇ‘ÒA @i$:Rad£ Z ¹lZE‚v$‘ŽHG¨j*BR‚´�ͱ¿ã  M )1²Ùt³ŽB�EADª¢¢‚ÙÛ©S'ß÷¹¤ëJ�€´"‘xîš10Ÿí?a‘=›Eº¤<´#H øO¢€PGJ…4Jt8Œ™4773¼TJ+%—*ˆ ›››9¸ “Éd³A.²V@A¤Ié*E®ë»®¯5¡ëúˆ²©©‰ˆ¥#= ‘ ¢PiáxÒõ„ã¡t%¿&A»`�@ "®Ü¨(ÔŠ!]¥Œ8í�p'µŽ½l8å{ß°»M¤•"íúžë{ŠtÅé$&n,@ì’“ CÎH™Íf•RÌ.GH)GzR+Ð ¸j iT!Hó'Ðèðk­@ ãHO £" $é‘FÏM1¸%iI&“¤@GüçJ”HæC: @G:ŠŒ-Þ`Ñ\àÍ+W ‚Òap( U+8“¿ —ãõ˜Íd|Çu06SùVDÊ ÈƒvM )°Dð´Õ+#Ÿd)+) †2Û‘aö´\ñÀÒ’å™ÉÖÇ™NFZó_¨ÊX1ä;.iírÖ†(’(!AiRÚR¢ˆ‚Ð蘴֑RabÒ÷ t¤À•¿UaçAà$ JkvÍ£Èq]Bñ^û!(Ò„ÒõS®_A(¡¢xoÃ0›Í²ºÍó­#tEZçÂP ÇC)J7ˆ´pœHëP‡ |ß%R¡ @ª�% 5¡æOET¤C %(ž-)TèIr°PÚÆ9wc…š”’Õ³±§›ÂmÝ ×…ó9nßó;áɳ‡ú�€•5Õ¢¹©)öÎmjl†êÚê–pñŒßóëÌÅïÏ_´|ñ»7ô{zìQw- ‡Ý9eÚ´iÓ¦}òØÉýjkª²MÍIŒWscÚ«®öhí³×݉§ÿj´·Ýí/|qê´iÓ¦M{ÿúw¨©‚æÆf»5ÕéwuôÝÝoûlá¢úy¯žçÜ6úÔ§6ÚdÞ~ø©û7ºËæªD·‘W=÷Åªæ »aîûU9‡ Ü4ƒsÙv°uïv~ªëî眼WÃÛoNoÉUýæ{7»ï´Ýî<õ°w焤[²ú|Ý´jëzÄKÍíÑ®C®i]#öØ£o§™‹gnÌÌü qØ©;m½|ÉŒuÙYï­ê¶GŸ.ÿ„2­}ññ7º>v'@Õ/«‡>úÄOõíß=³|yq&kàྺ~Y9-Gôù“–îuäÁu W-^õ<°¢°‰ª_º¢²_ÿ®±F¨Cÿ~íW-]®^º\m1 oìáôЛꗭT K—nì:hPû–f‘ëë7n|úèºT*•JUítíLµaÝ:]Ü“%Ë¢^ýû´är!:tj67—L\ù®œÂEmûvNg��°¦] d2YP+–­È͸vçªT*•Ju?±iÃÚuºÕ –-®¯ìÓ¯ä¸ïSlnJ ‰.ϨÂúXµíÛAº9M�€5µ5MgZdÔwC¢ëVkW.­·™.zöÒQ;ôíÒ®¦Ó°_¾(¥¼]Ž;ºßä‡&,ÍMùõô¾£÷jIm“Z¶xyÕVƒ{r©&¨ÆåËëú÷‹u¸²Ï€¾¸bY}¸lñ2ØrH+1ÔjŲÙ·ÎP™J¥RýÎ};ä c¢†eKêúõÍw•Ê?¨›~m»T6Íè×ÖºÙLÆú6óîÍ¿[xÄUÇ÷±GVvŸ4„^mÏŽºéêÑM}+S¦Aý hë´¬äÿR¹UVŽZØÛÅöïýÉ«“Ö~=éõ%û^qɾþƪõo¾òÉ–ü´'û—½Ãè?þýªA]¯­¾tâ¼–B–Êm>ÅmZùùV¤(ÀPW<TFI´®#=’¤úh9k­…p°Ð½®$—ÿHH’S2þá¶1Í6÷œoÎèE6@Û0( ë´™–¶û·m´„ĬÊàßXûM8c¤5¡°Zk®û͹¸Í=ˆr.}6« !8S7WdŒk¢ !„`œ�¥  JU¦P�@¾\‘tAk’Ž£E¹€Tš3ÁE@ 4)$ˆ¢À°×†åÆÿ6ˆÂ TR:1“Ëbeee*•Žq@ð+RÔµ$yö¼S¡Ë:’Pbkˆì4œï"ò<Ï÷}#�ÆŸËåÂ0 ‚À”åcTŸËå2™Lsss.—ãnðýS©TʯdS­‘«PE™L¥@L„eÜu¤ë47eXaáyžãºÒq0q0Q¤…`Çkˆ³ §�9î°p$èb>µëãÒ’Ù1|.ºÄ¼¶™o^˜…fxMÙ·2KÒè× 0‰ X©òòJ! #k$ ƒq…•xy˜Ù䯼±8($ ê|‰–äаÒ!ÛÍÌÿf“+æÅn`.oEþ‹î_ú¶ˆXH|ß­ñAA”$§@DVmí{Òª‚Á „Ĭ\*©©av¿".‰¤Â…Q*Òa ra`œkì>åo’lX‘Âá}IëdÏ @:ùôEÙjÀRõÚ;|1÷ˆ´RP˜o²h72¯”æô7Å3%„€V+z¶J´áÍ‹ýÅ¢£'>ÿ«ÝèU1d›þëgÌàÚpÁÌi³åÖÛlU!«¯ž}òó­8jH Ü.?<ó’q?œôqZ¶ï³õ°aÆ : sÕàm‡À¬é³#��5÷ó¹ÁÛq‚ŸyÉ{ vÑßï/+ݶw×ã_ݶ:lذaC‡ôj×}ذŽK¾˜É!ÁÍ3¦Í¯ºMo~:aÂÒ5¦·~¯}.½à€ðí7¦m–Ù&üäÅך÷õãšM7-¦hÁƒ7=¾z»1‡ Ú¤g¯èÙ»-^À¡ß”ËfÁ¯Hµ€h›ç?þëÙòˆÑ·¼sæ=÷ܺeÌ"ºTw †U èë7¬÷«:Ô€<`§ÎõŸþcÞÔùÝ·Þg»ak¦ýcÞ'ŸÕì4¢ã?£%XýÂoõsÄö�ȽºÓ¢y‹ãÝ(»hþŠŠ=Š1ºZ¾¤ºtë"@:¹Àì á'OLX5bÜ�D‡.`å²E[›ìÑ«[záü¸D­_°pC×^Ýݺ^=äÒù‹â Ñ¢ù‹±{¯®²ªS'íòúÂ8‹°º®®¦ó1O¯Ë2å‚ÌÇ—n-‹zÒ¹[.]°¸•e'­|W ¥ý9ï®D$êºÕù;_÷EsÒ§pÃ#‡ø…¬Q×­KzÑ‚Õ-•¹G%[®(ϨB H$I�Z`ÔwD´qÙ²¦ÎÝê¬6<{ÉI:§½0wmãš7í@úa'Ÿµû´{î~t M£Ž¹©õ):u阩¯/2ž·ÀYÓ£Gõêù c_(µxÞBêÞ«»Ó©K'ª_¶¢e 'êºÕUýôže™„7¹ù·ìiV(VwíZ¹zÁ¢&ÓË?¨h"¢ÉÁn¿�?þÅy3~÷Ã"º;]?kãßkwà+?(·O–2¢¶j¸ÿ:RB~WÛÔ:-#ùÿŸT~••¡–övgûÃFw÷¹§Ÿ~aæðýGþä§;Oiâ„gßí}È!ƒ¥ýËîƒè²ÛÏÿ<é«ù¯ýÜ}ôØ“îZÐGÊl>Å»Xþä»!èx.)¥\!S¾‡Ùt&ÓØ$Љ·9q:²’IÁa®ÆÔ+�ØÓ^¦ñG)P:ü'„ƒ(cÓŽpâØá&B¾ØY~¼h9´C¡Ç²i–F£”Áü¢ ØÇDã•mÛ !9§²šÀcâ¯ørÔuC¶|&*ó ee§çD H—ôм&•Ôu#šP¨TÌA !v6&R@Ê.(Ða„𨢫UY)åõ 6Ø+:ßǃB®#©µµŽt|Ozn< NﯔR*Íär9°Ž‚@´yòdsf“° ÊÁ0Žd6ÀÆ ñ‰ã8\tߦÓi!„ëº\¯¡¢¢"•JUVVVUU1ø¯©©ála""«{r¹\sss:!¶‘f³ÙLSs:Ž¢È“Neª¢2U‘ò|¶Øð¬”bv€æææÆ††\6KVV|£ÀÒ‰×=jL $܆ÊþV. *T¾@‰Ø—eoKصÈxn¿…á)]z yÓg{‘?"’…úl¹åy7úÓ¸uùi©WE ,ÉÄB2=,êgѵæò–ž[Vò[ioGy bEEEEEEuuµ¶ Ðæ«n&‘ 6™h(̽ÊcdßÐÅûgì-•ôYk­€H ïð ï‹4>I\�ÄJW €Z#)Á• 4IDR!ð‡D‚”ÒZ{Q”nÔæmà 7OL"t¬),—“2^JF„’±UÁ)GjÆíÜ×þ—½x?—Íf³¹P8ÃŽ:îÓn½ìÁ©‹æ½yÓÕ Fxp ^ö¢ÇAUÓžºwò’ÆôºYÏ<ðʊç Zô<ô„}WýõÊÛß[°ð£?_y÷¢=O;@ú>°.Q4ç^=¥{ßsÞZõàA¶»±·Û±G÷xý·W<3sÉìç¯úíK:aï }‡l…ï?zÿ”•ÍÍ«¦>ôðÛÁàm¶Ü' µàýVÛ}×< ¡Õ/_~øøßÔÜâ5:×°jÞûO\}øÈó§ ûõŸÎ"šÞÿÝ‘c®|}m b/û»ãâûsïçk–¾úÛ;Þë{ء۵Ø?V …¹(2§|t|_¯š³&­ÀM9ðõÚE«ô¸÷.Íÿ¸y¿’áq�� �IDATÊô…MkfùÈ *:4 »üpdÕôÛ?œ¿ý€¡U©m÷éöÕ]ïOï6p—þÿd Ï=þNß1Gp„¸è;æ¤+îúÅu¯ÎY¶xêcüêïíÆŸ4‚sò«%ï½øÎì%Kg½|Ýe®ßgüu(:õëW=ç¥ -®_ðÙ狳ÁGOL\?òÈý;²Í¡Ë~cöÚððeW¿øåŠs?xg&{%8?8æÄm?ýÝ9w¼3¿~áûwŸ{ý»ƒN8ng¯Ã?;Œ»äÒ§§/]:ó¹+yfÔIwÁнŽ8Øöꋟø|ùÊEŸ¾ýÙJÆzXYUΛ6}CT±÷±c«'^qÁÃÎ[±zå¢/¦ÌYK�E=étÀñ…üòÂG?ZP_¿`ú´ ›µ—ïê¦{ˇ³ëœÛοù•KW­^þՔϗŬwÇñÇ úø†3ÿÚ¬¥+–Ι2kólkØ£6yayFýsDM_üã•© –-üè o|«×Ø#wq±²ªR-ú|êê�´Ö€ sÙL ­ÃWÏ£.8²ñ¶³ÿ”sü«�@/züç‡þ쯳"ô++¡þ‹Ï–gɬ;wÇÃíùö¿¸÷£¥«–~þæÇ‹UkLHíuâ±]_¹êü§,ZþÕ7wÛü=N;Ð8êðmæÜyÑ-o-\U?{òûssqˆ†yœ»ãQã¼}ý9~kÎòÕ«–ÎúhÆ mu¬b¯cÇu|ñʳï~û«åõ‹gNýj­_öA›FŠfrxÓÜX¥Lá'—mÝ_<¡ûAe÷IÚðÑ„Ç&M_¸léÜ»øš¿;ûºGj“Ïj£oInß½ê'==i^ý’ŸÍËìPn–‘üSoË­²üö˜rliow¶7¦û«—ÿúãÝQÛq¿ÃvyÿªË_í}Ę!Eâœ[ðÑ;³W6d½žÛ ©£†ß8a™_ün  Q8 *”N74§›šÂL. C)¥ íùPP8=$›ض}‘'(ÿææä %8¡,€´ÏÙ:) ÔÁ$RÝÜÜ܇¿€"+(h÷Íî¶9‘Ûæ– >ÙІa†"¯ÿ.�·¢0‚é^V±/da™Â4Jœ,HiÊ[Ï JßA('¥Í ¥”Âuk‚�P "ª¨®B)"¥PUMu»Ï‹Mèåhs¤-´\ªÂ`*b‹á$“tc_bÅ3 ¾""žL&ËEãØÈoœJX¤”’Rry6ck%“qMë\6É6g2™L¶9—Ë©0J3ç4ÁNÝ’½Rgwç¤nˆÈ·5÷f‰çÂLìœXÒŒŠÊ]ð—J‘½X¨ˆÚs]´©DSP*9¦™½vDâažDK b§ôneåÓÎz�‰ÛˆÑ… ‰(Á0Ä4m&Ù}0òF–ßJÑÊ-eÚfŠ})Ÿ[iÌ:&­5«Dâã{狱3ΚQ›8)#Æ,~F„ÌÎóJaɃmT'«ìK</ X€99hÑ$2qƒT*åû¾”ˆý´’RrÈ‘"¥U…AãàŸ0dF¾cVU…R‰-e£=M�€€íõ�q‡ZKãæ­ùàÝYß¹hX5kq*Úºÿk9èœGî½òÚƒ·÷dÇ‹ž¸íЖ�`ûCnyòºçŽÛ¦sû^{]9ï;»xûBX,zs÷ßNÇ{FÝzÿÛ2Ç=zïÉ}6Ëfúᯟ¼u—OÏýáV;Ÿþî°›žºn¯*�±ÅÏþòȱêîÑ[vè0àÀÛ›Ç=ø×ÓúmÎí¢¹³æUn¹•ö«Ly÷ÝiKK¼¸ ^;{ë­v?îæ)=Μøñ+—ìT �Ô¼ä³÷Þ² Å|r«Ÿ?ôÀ¸Æ?ŒèU7ô´v¹ýÉ+viÉžSÕÌ/ûm¼oÂÙ»ßù³Ÿ¾½°{ÇN²ÓîG÷Ë>ñòSŸiø6ûõ®ðšY±f¯kÕeÑ=G?pÁi®ÞyÄ…çõJ�ˆÞ é•Á¡?é]P³çÀÁ*ì3jpÂ&£—ýý‰÷qDbV½Ozà™Kº¿tò.¼jÁÿüÜ {ÇžÕ”ýài{°óIïtþS9®§�HíûËi¸mä–ývó›×¼óøß›~2î'í㛋-Nüëß~Þéå3ví¿åg<>?ò}œa<õÄIpïØmû =ôO™ñM¼x;°ãÁ·?óS¹×À»Ÿûî럻óð.ØaÔ-O]³õÔKG ì·ãÑþ< žï!8ÃÆŸ{póG]ùnX³ïMÏÿéG ~;z»>=ûí0ê‚ s#ȾWØìzÄÝÏßøƒéW0´oÿ¾pâÂÍs.ßÕM“ÜòŒÇ&žU3ñô=öê5øG'ýñ½¯u1«ÁÛá²g?A<r®ú þÑñ·}XÜQžZ`Ô¦©£þYҫ߸zôö†üôÚÅûÜ=ñêá)=GŸu\ÍÄÎ^Žºú®ºq¯ÞíkÚõýÅÔ>ƒ{ʼn%ªö>û”!Ôüq»z��zÍœ÷ßywÆÊ�ªrúÙß;çÔV4™uW±Ç5OÝñãe7<¸Ï°Cn|où¾‡-2¡rkŸ½÷€7ì?¤ÿŽÇMhwÞćOí'Àú‹Ç=0~Û>ƒF^üò òS>Bþq+ÉÛñòg9"}÷1»ôïÙ{ØOμïó&»c5ûüîù¿î¿úc~Яß6#Oy`FPþA›æ×¦v¡òW­ýêåÛOÝgXÿÛºzæ7½pט͎¥j£oLÎŽgýöäÊ'ÆlÝwèþ¿˜8O–]§¥’ÿï¡Úr«ÌÚóBŽ-ííÎ6Ç?,-FŒY ØùÀ±{Féí_ìK§W½Çi?êß©¶ãÀ±/ôºøæÓ·ù¦þå~AÊäxúDZC¨<!Q¦±©©±! #>âÞgž�� ‰m×B‰�øœ.\aŽã181ù üL*o¦ÔJk4~øI…³u'Ëš—cŽæs?X0²x ¬¿°ŽŒÚ²Ór ³ŸaÚ^þ|Yræ'­5Eù'ò#"*6-jËÑ@&%¾5 !P8`Y¹=Ô¼†Ä‡\k Aæ-]D$¤‹ˆœx-—DÀÎÞ‰cðhˆP³‡&§€@"¢H‘ÆvA2dQX#Íð-NüÌ#ëÂ0¥!)u576©0DÒPη°h¾òÇ÷xÛCnk!ÊÒ6æ†.2€”R¢„0 yö³ÙlEEEMM ­_¿¾ìã</ÅøVkÍîf윱’’²af2™T*¥ˆ¢HGQ@„ÂA�A¤¤tIJt…+%JéèL&ã˜tz2_º5…aHJGQ¤rqЇ Ï-¦é°ÁoF†%ÅUHˆH‘ö<Ïñ<ÖG!š‰È2Š"GH $„£-$_f‚JœºlØŸ„aÈ’�Žã0VBx~™ôSJ³<™ k‰(Š¢švíR©! b "¾¡ ¼Ãh­I)]Ïs¤4*~4_"¹ª…ïû_ýu†®,o…,ùü¡±ºó·†áˆqOh^ÙòlCÙ²Ï-}hë5ÄcìÖ­ d.— µ’¥ÅY¸}’èÑÖqh+íKñ>V¹yWäo]×eçAq‚L&³råjvADÞÿˆ"®D!xct¡ÙÏ!ˆÈ”êШ”r\rv dy�¥)íy€ "γ…��5Ë Kk:‚ @Û ™y>˜½�Šø­&"Pk%0žôë/¹üÐÃVõÿøÇ?Úµk÷ÃÝÔJ›6úчï½ý=ä|¾Wzá­?Þîñƒ>y÷¢­Z=óéE·î½ÍÄ13ß<§wËX(óÚiƒNîüêþƒ’³5,ýjìÔ©ÚI/ü»ãO™sÚÔOéõ µésëuûÎ5^nå”»O?ú…}'}pÙÐÖO¨¥=ù÷Óæ²ú{Mò¼vjŸð¡EwÜì2”Þ¸‘ÔªW¯wþšK>~üÈ®›vƒÕ 媻´óÕÚ™äíýqËžßЩ×-úª1U×¾æ½xåø‹£f?|H»M_×Fÿ#ôÍ6êo!ùß)}?VZ¥AÞxõ¥~ýTTV´p!�€VJÍš9sß}÷-þñ™‰/¿öÊ”ô3Íé —¢5¸ž+³Ù¬CgŒÓ`-±}OòM —æèiNf”$« UćQcî�!„²Jó/–ÞÈ6ò/jOIàšHÒìCRUËí£||2¶}k­6žçFQÄõ A¹ZkÂö§kcÑeH†‰•8ŒŠÕñ“Qð‘ @B0 LcÓaV�!‚@¬øB“¥„źBŒcƒPX,6N0,²=;�€óá!”ÒxÑ@ ]C‚ H§Ó®”-© JIE‘ð”å˜a”CV ;yº Nã–FÃÚ¥L&�žç±*‡œB.)ÇÐ…$–ÑO.§i¬¨¨@ã �@ ¥qU{€ ›ADÄ�(@ D¡¸\<AŒá´R*åzyáÃX&MÊ={¾ltªKân ÒFlÊB}*§^)bZÙö­lÌÉÿ³•Ø$†àq]פæ³gÄôÍÆ«T¨}CDJa˜‰ˆ¥Ô y`øVÊx1`ƒ „`e$Î>J©–ÔöNU´Ã˜¶R¦ôr{o1Ÿ›!—}¨iSzI+ÄòASSo2UUU™ g®–G½²£Œ7û˜ {tJ)Ó–.™l§yIKöL㛫ãÌŽ®ë DDI1KL.a‚&Ž@”(4‘V*ÒJ(Á®Q¯Æê8ž‹‘rGëx^ “yˆ¥+Â^26Wc×$¦@¬xÈË|ÁJ1ÂÉwo}:Ú¨Z'½`ⓟm7öþ祿Zó›O¼ ¼w„…ÌÓSþtìIµrc˜ê6ôÇÇÝûç¿©Ž� šýÐéã~?eÙú¬Ûy«ÝÇþáÁó7¡#(Û“;}§¬þO%½ôáqÛž=ÙràEÞ3nst�jñ³råë ×4cûþ»Œ¾ôoWíñ•éëI×;啕«›¨z‹í~zê£Õ¦#h£6úo!I¨Â(ÛœV*J¥RZë\.'„p}ßM”†ÄòÐþ¢@�HŒÙñMý� (…PR@AZ�A¦J�ÀµÎ›²ýó „HÃ*Œw"ðñ�¬dc1à´¸À7%Ë© Ž?P >!"NOÀ§Uö¤(8÷cü„ˆ(Ê‚@ 5 õÄ䡈@ Qp4§Ú� øŸŠ€ˆ42‚BÔÀ÷U·6Àrϵ‚ü$9' ˆ’Rì€ë;nDZ Ò‘ÈS"fsiŠT˜ Â0¬ð}æ‘ÅÕRÒÖöm[«ɬåÙn‹!£HB±5[¹¥ëJ­uCCyžElÖa¨„ ¶Fk­+++Çᜅ¬à„¬Ìb$œÉdØ‹›Õ� \ÏŽLp¯ÏQ@ ´BR�ä©"•KÒé±Üº®¡@DɵÂPQ, HšíމZ§Êì°‘Rë8Öpž,›¹ýU©®Ážã¶mƒjÇq4E¥=AD^_¶q¾¨ce‡ûŸ'2À|£H'ØèâYPÅéëJ…ªhPEd6›«e¯¥D]h·iý‰P¨p)ú°lûÊÊJÎ[¹nÝºÆÆFöF©©©éÚµ«QتFÏR‹ .!`˜Í!¢ÃåR‰H“H}e²U§> ‚|öДçµ,!o¹dt»’”œˆ\©A)’ˆRÄ©aµR $j@ (¤tA¤ØÏÅ‘®&KÁRìáåØœ7»½ëº†`)ã–,9@æ-Ïï² €Ã ˆ�uÌÕú ¶QµNbË Þk¸`sö=÷íÆs[oSuÀ½Ë—}´Ï /¾á[w��œ/~cÞÅßè’2=ù·Óf³ú?‡¼ŸÜ³¢þ]!zŸöʆӾÙSäÀSžšuÊ7»¦˜°ë˜{>sÏ?w“6j£˜¾¹äÿÏÓæü‚|{RJ§››Aißa¿ÎßóSŽRJ¨0a%�ˆÈ‘梜I¢A ØÓ‘ƒºÑ2Æ ] ¡óg»(Šmòy Ïô”¤ú%‘ü¦3˜ÄÏÛí€ÍÂæÂl6+’t 6ȉ׷NB!¸«I$äl–ï�Yfº L¸Xºs �¨0¶ÙÀZâX;¿ù­èÙ‚€ Ô¨•âˆË�$PDySžÁHf‚Ì(øæ†Ï†c¦‡jÅîRr¥™L&“É„Ù܆ 8¥eÍêÊÊøž–l¡5¢R2m+GŠS‰K¼­¯ËVÉþÏ<ÙlÖÄös*u†ú&¶™«(¥š››) %àpzžG"2å9«ªª8úZkÝÔÔÔÐÐÀžíqß’arU9vµ0iX�QHÇ YfÉ~ ü\ž¶uç8·¼&"r×ÎVWh͠ȨE82¢,Â,‹Em–Ú+7eÜ.ú–/Éår®[ÐUÖ§¸^`§ à â;xonžJH‰KH,\÷QJr'´JœR¡Ó(`KT4|[ÀÌÞe$“ZPšpK]˜C,—–²´I†Ûd2§¦R)Î8ÐØØ¨”jhh0}Иn…ç›H+{ !|߯¬¬4D„Ç1á&A)ÅÓ†É&ÃíƒlÎÌ å±¥yó7 4ÜÓ¤ôA&E„B¸ž#‚(Ò¤BJMš4o‰E\²[fRŒ$ØW‘’¨¶ä[ ×u˜;˜l*Zk"(´TZé¶\×mÔFmÔFmÔFmô¿La.PQä{."f³YØ¡c§Te*Í:ltf_Yh[C+M€ÖZ…›XÙ³:Š"!-—T«ºq‰B ­‰4G½&çJsÔv\×õ„eÝ2y ìa ¢N°.ZIÝ�€Ýó‰ÙŽãÕII)eËOo­%@l7çc°¢ø­ ì.™Øx‹<‘JÒ1ÄFàc:Ûöeâßk;êÄÖ§u¤BÓŸøHNŠˆÝ°DQ¨’Št� )$€Ÿ¡Mˆ„ š°:LÜgßõ@éPkE*Š„A&»~ýú r¹TUUÕVWK)E§NRÊ( r¹\.“%"Ni¦” ƒÀ÷}A‘&"Œ`ìP`œ>l$ÐXb,Ê3kZ²Œq*­ucc£ëºMMMJ«(ŠL7%È?Š"Ïóˆˆñ*ÇpÎÂ0 ¹ Ë"‘ÍfÇ©ªªbŸm•ÔD‰IE*ŠØÙ^kMV¾OŒ#\Hìi¯´DỞ"Í8½IM\%‘ÃìyìÜŒ…ÍÆöFk„Y"r]7›ÍƉ"×u9€õ_H‰²I XÍtÛK 5f,FHŒžKaîoT6FñÇ2f.|“T*ej<(æ3¿à“¹ gbìþˆDZ�A)Å»˜ÿÌ|ž8~b.—ó}Ÿõ€¶ †£‘½Ra³qµýº¬pb’�ÅÖ^_mœÙßtIˆ³½–P“Ã0dï¯L&cê€ò`9°¥aý^†?sgšššêêêX³IV^OÇ÷Ì2Ôadw@)%�ÃHq¥Dô} ˆ¢�óz̘K¼Í²Û“”¼XÀ÷S66vêÔ9Ӝ޸n½ã{RJ^EÊK¹‚œ(Œ¢œr=ÇK¹D$ø¾Ëš;'žÓL&“øìÄ!lÂÈÚj{^ãIçyK”гˆÐ �¡•F�)d[ÐAµQµQµQý/S.!ß ‚@9ÒIÙ©ªÊJG)EJK)l‰„€šPj dN…η¶å‰½Ú‰8E`é5±Mi­ Êã„ó7LÎål".ú ­ØZ(ôß6†tH*x‘e„/Ev CQ—ìÏ‹À¶ÝضpB¡-±`Ä ) È/‚Óf°dðQR ¢±v ¨±A}Eãmظ1 EQccc.—“(„¾ëÕuîÒ®¦³Ù,æT*ÕÔÔà8Ž+c³|l¥‚ 4”°3ßkû[ƒ‚ʨ»h‘$TTTp˜@*•ª®®f<pb+.,€$K<«ˆ¨ººÚT`g•DU­[·Ž-ÿétšµ�®ëú®ä2F넉‹eZÀ[J)æ!J!‘;™Éd³Ùl ä’BtRJÎ ¡ø+£b3j££$e#�ðøCƒ ìô¼`®,² é¶€•ÎNÑç¶À^ã”8Ô˜y4³f®2‚m²Ùs{JR 8Ž£€l/ Dç€çyA²v†£E²ÙlSSS*•""°ä‡1¼aÂ&‡Ö:Ùºƒ¢{–—sk=4†2wh…x~mÿ&þŸµ!FYɰ?Š¢êêjã´o\„555A¬^½š³u²žx{ÇÄ €Š77ÎNGv�„¹ZdŸ“rNär‰ÂÑ€S§M§t\ˆ T@�®Ó¾®KmmuçΕ 6¬÷<§ª2•ÍåRžc8ÃϵGmo³f/ËBAk´ãá¡•º$ªk.Ú| Ú¨Ú¨Ú¨ÚèÒ™L†Õ55èÈÆ¦&Obªª2äÒ)EćF"dó‹�±QWH‘˜£Q(-d\ºOk­#Æáqñ€üy™4$èTiŪÀ8iDJ \�´° ÉzÒÆë›“àó×VQwaÅ?câ– ãŸlyÕ*¥0vÜEƒ˜„(¨ÅÖíã²¶,É8éävˆh28ÄGW“Ô$Äqì�DøMÄ^ È�,4ËgwŠŸ¢ 8 â ñ„p1N]–:†s®Á´fPRʆ††tc‰Â÷}/~A„a˜ DLg³J©Ls£y:Ã9vò×AHD€™jØ%° -³¶ý “ôìÉÍf³±!=†ý@æ*Ïó\×Íårì¹Íf±¶¶Öó<£Ûâ¬vOe‡L 9æ�°'j"‘0#ˆqýµxŒÑI»mc*.ïRä‘Î7àåmàF¨‘ûl�¹="±«SF¡,?|û"‚]žŠf§èVf5™¨{!{4˜oEaÖ[_€I†æ³ÉEWõ%a :—ËQ¢¼ãây& ‰Ö¤…éŠÆE%мÍ$²Ôjv[R:Ø;Oʦ¤YëÏÍåâT…&¢ŠƒMRžOD¹L–•Y,ÊX'K$�¥L2hÍ¥:HiW:ÂMòÈx ˆÅIŠxO6úJ DôÇI¥â… TŽ�@£¦x .ÂBÈ»›@GVTU¶Í Aƒúõë&PP__?wîÜõë×frÙêêJé¹Z+€Ø±«h6Kƒõ>¼{Tóüä¬+…n2ˆy¯ L&Dke4mžmÔFmÔFmÔFmô?NDTYYéû^ª²RåT˜Éd”Ö~ÊwPèHQam>š« „Ä–oÄgLcù±M‹ü¿DÁx×Àxn/“ÄWÆ¢�(òªôPŽ…•ü%Μ)ÁÂo`yCpŽne,Ñ2‡Rœ±?âšD …Ø€ÿj,gŒÛX|£üI´8Ò›ÏÕ6Œ1‘vgâ>ë˜áöñÃ;$²¹‡…™ ìçòtPb€~E…”2Ô ú¾ïJ‡Ý ª**ššTbEEEª²B)•ËdWHLâÿ5™úêšòµ-M@y¾™Y� ЖÅ]vE†žçI)ƒ hlldg슊ŠPEŒ?ÙdÊmXVML5Xz"ƨƗÁ£b#’ˆn×a=Kœ4­|ò¬& DcÄÓEBˆ0 9W&Ê”¸ Íbјóç;°[±§½™>Û×=ŠÂ(ŠP Î- –}ý‹áž&²a)YVÙ²lψ=Afu“Uh€’üÖ4V²sã¤ÃÁJÁ*d˜°ë8YJ!¥@dÄÈ7aS6YåQxà¶W”,áR1k…l<_v~Ú÷·±n¼¡¼OKdÒX˜m3v�äZ›;vBlܸ1—ËyžgºÁ2{y½Pâob\´ÖÂ)Þ⬇ 'måWu)5"J)”"!òÚX"BRJ°ÕÀ<pøðá;v”¦Nýâé§&¬]»6—Ëär9ǾïSg!Å©<Ð8Îíùü!+MŠœ¤lb×7DÔš8¾ Yø`4Â�Äéz­ù‚¶ ƒ6j£6j£6j£6ú_&­µôüT. ¡sç.+W­jjhìÜ©“š# ˆe ­I tPhÍuø"E�$$ ¥IiâänDù?Mˆ„@@$ˆ�5 Ô$d|îdl9ºÁE…ØžDî’Χ „Â#;Ÿ,ãÀé¤x˜(Ì©f›G±Ï<Ŷ÷ò}”Õ·‚QXA&Vˆ4‚ñ=ÏGŒk" A\RBÆj�°{¿�EF¢(  @Vå$xX¢àDèIá �BPJu©«ëÚ­›B)…šÂ0lnnÖ‘ÒÙ ·Éé(ÛØ��® 9 ìW¤Ii­µ@Ô¬)H:T‚Q‹^؃âÿǃB$f<¤”ìGàyGûçr9r%t@Ò¨""­È‘ž"ÈEQ{gD¡£œ1ÅÛ“ÎÞ˜]ó·Ji­Ø™�ÓU ‘"‰+z aÎŽeà!Ù\*•2a�<(DD€y_kV›¹A¿@ÃÝ�� �IDATFö Iÿƒºd�E‰3ò‚]Nô‘.‰½·Q®­y‰5DIV|ÓÀ~­h¡!"j­ÅEFML>EqîN\ÂEõ èe±R hPJ)'Q;Ò?ÿZÒ§ØÚ›KËÐ’s[ñZzóR’ž«µÖJŸ®è©ÂHkMJÖBWJpÝ”ï‡JÙ#3h†a.—3*¶ü¨|\²N v5мm˜Û*!¢ Þ�:(ë*¢Œ£´�¡å²é93gÔ/Yüç»þ†¡@Gärõõ˺wïšò+š››\®ë†aN–º§h6L6Ú.-†öζ'S":qÚŠ]€xXïÖFmÔFmÔFmÔFmô?IH(E¡p„¨ª¬Ìe³é¦&ˆ 2AËlŸDˆ¬&`/_chb×ü‰ÍJ² �Š||­´�†°Â¹Ð$î¶ß¶A S0‘eá„’“wÒµ<ª ¢H)¹Þ»]z-~®¼mØ_„*11ªˆ¨eèe# Œ-Ïù;ØÝŽm_l+ +·ÇhÞ’.xhü") ‰9Ý~´(É#È׮߸Azn¥ŸÒZ©(ò\·Â¯rS>�T%I׃0lJ7“Ò®ãä‚\† 4'³�GHÇq¢0´ëñ Š8¥‚ÝOh¬}hT6œb“ÑKUUU*•J§ÓQ9®¤ÄýžÑ2£GNaˆˆœ¶Ð !ó*HP·‚õ&q`ì„@…]S#$"%)ÔHû¾†!"’ÒétÚq.ÍPdè¶ÔLr.¬¸m%ž4½â·Œ–µVZkàqYi8‹˜‰%Öo*1ƒor:Š^èLJŸ&ÅJÁR¨™ÑUÆ]×E­¨À­CJ©TÞLÝÔÔä¸.k”Rì“o&‚® ãÍCmaû¦T¤+471^ý6Ѻ6£°°¾ÉæGÊx2›l6Ë4®tXMÖÐÐÀbæy^.— •Ší'K)/;o…¶20·!qì"–í(XºŒ’‹?ÏfÓ¶æˆòÞd¼RbyˆB­ÉHUVV®_¿~îì9Ëã�!=Ï÷]ЊHƒŠ×SQËejjj´R:I‘ –g YYlY«Ë]2(Ìüæ%â�¬XohJääý#�8É(¶æ˜ÐFmÔFmÔFmÔFmô¿CˆèW¤4PuMu„ét¦¦¢Òé(V¯^åEš´@(�"ÁaÄ^BJ6DSbÝfë’9(C *�5‡Ø $NeB ‘Òa�H�4¢@Ùwc².%‘´æ(iNç£q‚5®× ŒÄʘ“Øå”ˆ ]ñm mÇEŸäϦ°ìÏ �4‡LL»ˆÈŠ.¡ÈÞ°±y?y´ ˆi…$DÄ'xˆZ…J)$ŽP(®Üfð˜Í j;¾W`Ç.ÓÙL.â: €˜/B×óו®ƒŽì�JëHå²é0›SQÄO ‚ R ¢B'mŠÀåò³<D¡#IÆ3�ÏÌ ¤d£µÎd2ü &‚±„Aלïo+­2™(È0‡,[%',°%’�D 1""�’“ªƒJkªÓø®Ã ]×Í…ã8¾ëº®k Ø�H“É-ǚ݊„͒炉6ì²Íø¥ß–®¦üd•8ò”Þǰ˾ ÃHæ'ç}0ËǨ6ÌÚ„dÍm _Å 1‰ú)ñŸ‡$Ò$ Cé8¬gál|\&PVˆDKÃR$BE¢õ¨,[ZúŠ©HŸXV ÓŠæ‚7(®eˆˆ¬& ")ß÷«++ÙÍ„WA†®ãÈdgã;#‘�Èe2¹Ø*GQ¤,n˜FãÃ>"Iˆ�–>â)³üšˆ(ñ;ã¹P@ ¢.;õìÞ#Ž+Q ‰c‡©l6í8Âs › ”�‹X=‘÷QŠBEZÈØÁ‡Gʪ®tP:¡d4±$ÿ!$¡pæg+Ò„¨t\�¾‘OÁ‡ï½½ùÛè;¤ï'翟½j£ï?µINýWÒ–`ÿgõö_MDÄ9àHiR* £Š·¶º:“nŽãŠ×Õ—'Ð@:Ôˆˆ!Adû"j!{¨rî~(D† Îùäç;.HJ+­øh_j{,E/1ä ¨ÃçH6#ÛàÄ\(5H²gÛ½BË—;QÛ$!1]Úƒ|¼}G ;Ã9?ˆ5±F@óÇç S_�Jk�!ò6[×uM´p†mseü‚áDŒ ŸËÂ3[߈ Í™´ã85UÕˆ¨•($"R±5>*Ì9Ž#Qi?Ué8jBÄ\.)¥"Wqà¸JÂ*À2?Ú²HTŽ^Ì:ð@ �$FM&}]üï8n:“a›¤"R ¤ãÖR@ˆRv\×DžÏå0uÑ£*ÁÔS4™˜ŸJ)ö‹çœœ§t’¾-qzojÎ(v®®KUúÍÍÍ „¶‚ê ™1šieS¹N òWEÆyD¤ÑEQÄŠ'JÀ° M“aK¨h}A¯¢[Ù¸Ssl…V3Çd(°§ÕŒ’ ß÷sa¨µÎ…AÒsÙá‚ gðFÁ;ŒRJg£víÚ)¥²Ù,˘)fa…–ðóºùöJ²€P}‘Â…—X©œ›íd3žt2A®)Œ´Ö©T*ï òVt$ •r¥ô<ÏàyD ‚ N³Á÷}Ö|qíITÜÔV0XúÖÊÊJž[þ )OiIl2M±€K®+kjªš2i^§AEDßq•RR8‘ y‘e³YtDJ¤š››E\‰ÆÒ!)¥LÑ\Ö™: »o%i/"®,+„ˆC®xç44ÿp)pZÑØ”¡îþ£oк¾#úð½·¿‡œÿ~öª¾ÿÔ&9mô_IÿY‚ýŸÕÛÖéW_úçoBÄ1ÀjÆ :Ržç®\Ò•¸˜ª¬Èf³Ägz)!µÆ(Š”V®ë éh­#H)…Öƒjá ¡Žâ°lˆôÿ±÷ÝqRéßÏSÕ==awÙ%'Ɇ3žéôPÎ3"‚bÎéD=sÆx¢Þyb>sNˆgÎ(Š($)9gØ0;¡»«ž÷ꮩé™ô<ï~÷îóYøÌöVWx*L}Ÿ(AcÜs…Êp‹Å,Î=Ïó\W’dŒ;ñ˜”ÒBú‚3Ƹí åLËUÚ*M–@âXßW‰Œ€ØJaYÚ6["b ž�†@$EàÉ̈„œˆˆA"È 'LNV0OÐWJu»ÔF ¦# ™\L9 Ã0Ú¶”¾TJ{D¦¬'Tà“ˆ8Gàj\È81D‹3?ÀÕœsF %ÆbQàýˆJ›-%!"1� ÅY U >|Ÿ|ˆY±ú\½O^ÒN8Ž#ÌÌ� ïšy‹Ç€@£@ŽhÙ$¤-;•ªÌ`&—ÉÆ-¦ÒxR€±XÌb<çæƒðŽX¡C„ CèCƒ9çRŠ`¦‚žð9 r¾/€J_XŒ3‡I)=/oqDfHÁ™Ê«î#gÈ€@" ‘ù†$dÈY ó2휙]ÁXFxÕÕ-mÛnhhhhhd�€„@IÜB×õ‰De²’ˆ<×D˲„Ov,žÍ»Ù¼O&²ù"Ú–-¥dŒ{^^©Ä}ßO$^¦Q庋Åb*FeY™LF…04…;êO9/£ £R³Û¶íK‰a¬»�½‡‘-QÊ a!êuˆˆaÄ8½<ôªFýÞð ž‘„‚1I¾ïâ'UR%/(h¹m[E‹CÁÎ9w…ëÄíx"† c1‹Ù\J‘s³Ê"Zž¾ï[È'&\cÜ"5S¾�Î8çn6W•ª�€|>ߢ²J±±±BÉ„úpË@¡ßȪS3®júdÁPlI‡aZ.@˜’P¯Åy,NW¡ CH[`s[Ek-LaèºnÜŽår¹L&ƒˆõõõ¾ïwêÔIäW‘ôC�dB‰•Ÿ½”‚#D!„ïºÊm 8‰Â¸�ê‰J4£º�˜É¹�À=W/ &¥Æ$ r„"$Ã+2J¬À9)Høˆ(³¹F›3DBØõ‡'\äÀ¹…ˆ@ ¤ „\�³ÂÌ L òÐBO@”�9[Ô² “Ë9¶ ž jÙk±£S‹ª9·¤’(‘£št)(+ÍÔLÍÔLÍÔLÍÔLÿ¿’RY#b*•”Bºž+³Œ›·$çyÈ…¤ÁM²B|5}»ÕêA(ѹaˆœµ²c™hPèø…º6³N}ÛÖ‘·T¯Xq ÝÓ{\_ßµ‰î§Ö«C9gl*Ni ‡Ð^€ˆ œÎV«¶ÝoQ$‚"ÖYÈ<é1Æã�¤*S·Ve5DP˜.`Êú]ã@Ä‚Œ eàRbv®~5]ÜÍsdɰ ȆYîxÌ.L´1#^àO.Iõ'Åb~EE¶!-¥dˆDäzåódÙjID8™>ÝUÕV°TD`÷c æ‘Å5gÔS^ß<ËDWSÓ…� +€@aÃ…¤žáÄiKOøÕÕÕ*䛲\Èçó±x¼uÛvÀP¨hQݺM;DÜT»aýúõ 'žHU¤R©tº¾!Óh3w'oÈd¥'…”€œ)ñ‚pËk§Ä[8ªðªmÛJQ,C ­7UÖdZ-¡åWà>²CÍu M“ †õê5e z¾´ÜI—ìÏ…Ð>äÚÝ]‘'=e€b3&@J_ í'¯kæœ#©Ã¤ð®ÞØDÇŽ ¤ôòù|>¯Œ=Ìef2¼©ÁBÉ9al½#J·[©ÝDiµ›á³ÞÝz;��Cf9–²˜Èd2™LFI(V¯^]YY©ìÁ¯³,Çžož~¥}6Ç…ˆ‚$éð(T¦?P$ÈP¡þŠø©­“Ìò­OBM‘”’H¥Ä0 K!g$@ YÕfG›g >.”g„¹ô™Y¶_ø*q�I”@ „3Œ3å@·ù½ÐLÍÔLÍÔLÍÔLÍô¿MŒ3ÇŽÙ¶mY6‹1'™ )‰¡Å˜ÅB_}u³W¦Ô6"Rz~T1£@©+uíÁM�Šoõ¥P;,hä  DŒá5™FþX° %YîRÇó<¹µ#‚ŒÈ]0‚Þu1 ÆWÆ‘X1‡Œ MŒT$;€ šp‹ŠÅzh‘A×zƒh¤¢Ž^$""! ö*Õ™4Ôee[±˜¢z:|Å0ªQØC")$�€RJ°0ÀŠžç¹®Ë9Æ,[y+� R”æÐ40‡ïzç\ú”ËåB#st}i#À@@küȈ°XŽc²Z­:íJ`lÆXë¶mZ¶l¹aÆuëÖéÇ!"á )�8ã‚sÎcq§¦U˪ŠJÇŽY ±ÖÛŒ µµõvܱ,KH)t!°3 ElJdF¤lõµ`N/Zµ’ÉpuÑæxM¥ËK) Õ^Ž®üÒÙ/%±`+|*,l£Œ2ËÐXK/HsBÍYfŒ%šÑÆð2L-¡9ç(É÷}Ž��*_à°öÇu]Áñ} t=æ`ÑÈZf¼Å±< <µôIBÅÉ Í}a~éRÜ«K0�¤ë„ç�žï8N¬Âö}ßË»™t£ïûñxœs®òwRëQ‹l4ÿË€TB5ÒýHEŸÏújÊÂ=]`TœïÖì€fcäH,K¥’ 'Ê“¨Xا¥'MWEê»+ÈOÉ™:Í0”ê6S35S35S35S3ýÿIYeªB’DÆ,‹¢çûêêdi52hó`(ܵÓ]``’F5‘›´6…â˜Zû­KB±>Jn¢fô3´X.ºw*pEÅ*#óÞl’~E°…YLçº×ÀK•öaý&ß4¦R5}ŒUe\�!hÔ–ÏA<ï°†Â@ŠïßÊø\ýÅ´07™ ˆžðUz9“!¬8Ûœô…ù.ç-$)už¤#ü'RÉ„gˆù|>›Íj¬Y0E#€Pœ‘L&˜°0$•oQÁõÂŒ‡‘ÞJ±™nBWËÂäŠ-*о¶XV3È9Ïçó7nlhhR* ‹Å”ù:cLÁ’¬›GÄ„kÑ¢E¶1ãúžÀBtŒ˜S(]ySëiUFŸ1F†Ó> ­Áu·UþÍĨZ] �`¸Czøš3›Q¤—¥ÈN4ùiE]^û®°ð ƒŠÀl†zÒõ.ÆPˆ@Êï™eY1‰ÀEi À>É£0xJpX…¾�[9ðÂ7»¤§,ÒÏÈëšK‘#KƒØ²íjD¸ªœMâñ¸ã8ét:—Ë©”D‹Åb±˜(ã#-N2‡PúŠ·Â|5-Ë(<ÇB=áZ 9%"Ó‹EOf(Y“&Ó¶F•9šÌ'¦QCùwAm@IÏ™~ únœ¡™š©™š©™š©™šéÿjH7øžÏ9OV$˜Ã#Ιe^U(B…‹J¥ ’8 (ÓU¦XU¥'¨S—H¥ VJ†QT ]Ì„è_Ý7•D-r9VØLà zOè^ªó•¦îÇÀ˜±ÐÐÄNa),¾s7¡‹D "f¬* ØƒJà4…ºîbˆh¨:Á|™ˆ…™Ìc wh ûMD§´ÂY7Ï(€»B„þÿˆgXçz®K–Å8 $RpPõ’s u(>eÀ‹Å"ðÉ£ùˆ‚íÄ¥�;Æâ‰”ëúÙl£”Ââ¤R¨`ìÀHD~t6#PM7ªÈôWOc=7¿iÓ&Û¶«««UÌ<D¬¯¯oÕ¡Ò>‘°m›1KJ™÷¼œëÚ…ç[Œ ¢œ›BTU€“tÒ錖!¢)&0—…ž1fY–¦£Ó‚-Ó Gáj›� ȧa›iÿYç ·€£"ÔTaó”0A)†°P‡uУÐsM¡¤@A}sb±S¸yImgι,ƺú’0ƸmǸ•Ïæ”!:0Ìd2–eî,§ç/;d*–˜Nôšè·,7"mm¦]Sìbv2‹ !,Æ� fYˆhÛv>Ÿ÷<Ï S"‘cÛdYRJßQšÌÔ' EèÌ6ÅMuÛ”R&Q³æ’–Rrä@@’©.‰¥¨¾Œ­*¥„­°ù7Ûµ,+zê£ ça>“$Aˆ Õž R¯„<ÁŸ,Gk¦fj¦fj¦fj¦fúŸ"I¾p}_ H@@Ž ÀRpÚ„O&V!CëeÞðJoÆ AhЫM‹¡øN¬nóP|1-{{6!_dhá%µy[¿®ïý&,Ã$Á¬GFÄÐ�ŠrèèÿµHÂìÙ³f"⬠‚‹TE¡<EV» t°T<Æ7Ð ý0"‰ £Q‹[Œ1Aÿ•˜ ¨Š�ˆT@sa†Ã-@_’H€”`(“ÉH)ƒl6Ûàz B”kSÍñšV*¨,€KÀúº41î8N<.}dY(…À0he`äQR§IÌ0KÖjy)%a°ê´OB§•••®ë&‰-ZÔÕÕ)X.„Èç³¾$AD¡ˆAez‹WUø¾/HÆb±šššt:Í«Üu¨Ã (͹jËÜGÁ~0LET*ã£è ¡–W²õÙ¶m] ¨Æ@³¥¬(…OzëA9Šìe³¼¹£ l7`¿Þ€ú�1ç(°› B6J@䜫fa†LJÉ1Ƹ͈H�‰�‘†1=‹K!ÛvG§Š ¦½™JÇ‹†8Ãìª^B桱0³ð©tŸªw³Ù¬®Ù²¬ššD\·n¨D¶m‚ÑÐæÈœ¾¦>ë¶‚?Dzkº$¨?ù¢`UdY‹ÍေZLÎèvÍUg JJ©ì7iR««¸Ñ-TUld¡ªÜÊÉj¦fj¦fj¦fj¦fúŸ%)íXŒsas‹!¨äp‚„異a¡á;FÀ&"b!|eÅ4`PõG®k¥wh‡bC7¥1ƒúU_y©X¹g6Õïú~ì4MÍ ±Ü:‚39 …¶ôAUaù(–@DÍâQë†‰Ê Jb…û.ggl `èÁjm-ß› yGÁô7|«0öB(0Vʸc$$D�å:¨b„K!œ$%bŽD�é{B(Ψä|ˆhY ¹0Û¶Œžïæóe᫉-C´„‚ ï @2ïzB)-'f9N6݋Ŝ˜�¾—÷</œˆr† F&!¢N¨¦ú ¡«¾¾žˆ„*œZ±©”‚[*²†ð|ŸsY©„ÓÐÐ��$d‰WWW§R©ÆÆF×u‘˜ã8žï‘­ò¿©iBTœ�é»Ú D…'Ðêwåò­Uµæ@Ì'zÑj6*È_1 ôhÒf°@éÂÿ ÌA€A`ÈBè; Ý(Â夥fz§˜û1æ ¿°‹‘©ÌŠѶme½"I2Æ@uC@� Ä(B×umx¤ "•ZE÷Á<6ƒÞÉhš¿ê˲Îü«*±Ÿ‚-AÐ(b*Q„ÆPRJ§C½¢¤W M*ÌL a…¥û½pÎ03µa�@Ç=)œ±š TH À‹ÂÌáÃÝCí,S ±¡Øz.iR’k -× x.J‰!Sq]‘17T„EbXäRÑLÍÔLÍÔLÍÔLÍôÿ!1Æò™,ç\ÈCNx>·8CFˆŒÂ\ÙúÃkD­þ1™VšO,¹)‚¡6TŠP}Vδ`¸”‚d \…JcfjÉ ¼þjÈdÞïCŒj™æ ®kBm_r·Öý71ƒ©‘&C¬ Ë!¼0›0G' «`f$/ÐåU=¾ïSè*¯SЛ–êu˲´" ¿tÝÍ=•æeÝ„@ªœïz–e9ŽÃ P}+ÈÊUÇ„ID„’@HòŦM›ê7Õ¦Ói7—SÑ6n܈¡r›1–L&+++-ËÊä²zÊ4 5¹M¡…çy®ëçóy æy^]]ýÊ•«V¯^]W×ËåÒé4�ضL&‰„¶½7ÇYjø*ê¾ ø-ËJ$ÊEÿU‘ëºUUU•••œsÇqc¹\NM ©Ì ês>Ÿ¯¯¯GÄd2é8N:ÓXWW—ËåóêêêÊÊJ©^Qñ”é„eYʽ\o(#·ëáè`Ÿf÷4ߨDफ़Ž÷ÎŒ¸�æ6Éf ­õbÓ/‚ad®[×Ð)KÍõ\j wþ“–^™›K*ZŠÚþj%K)>Ib±X]RÕ‡\.§743uWÍ]JæÖ?#‚EÕOó04y^j[ÂÀP"ÉÒœAÄX,&¥Ü°aCmm­)£5|)¥ëº*夹È#c1WŽ9“"=‰lX½fÐIé!GD�šcæÑGÅr “±›™—ÈŒ˜gWðkq~‡Í3YJ Íy€ ‹CR‰=E3ýŸ$Q¿põò �ÔØ°hN]î?6©þãoûÞ²_$Ë&­ÿì·>ó]æ—¨«<å×ÎþrÖš-w¶éžø +Wlôþ ]Û25ÉjÊmX¶:ý{_ûç5}yþç¼*ë—LºðWÿ¯Ü\35Ó¯E…ãñ¿|‘ÿ2I0fÛq˶®—KgÒõ鯆tº¡>°jÖaÀÀ¸Þ‘JMÝJcñxÜ„^ÚÆØÌ%nÂ3…!uâ7Õ¢ëºê⫺¨¡�(Ù„ÂTZ½†¡Í9 ò¹ {ž§„ ú†ªÕ怒+r÷Õ½ED)}ý#„ $cãŒ1’‚…€R€ðQŠ|>+¥¯‚Ü©W¤ôIJ@""©¨öêGß’É£ … ¨„䪓&M5c)ßÔÕ×+!­àUœTÓdÛ¶*Ì9r¾‚ô½¼ðò6G‚Å,fsd …ïfêë6å²Y7ŸÏ6fÒ uõÙÆ $œ8XŒ3@A`yf°7q”æ6†¾�ŽãTTT$“É C´xuË–½¶Ý¶WŸm[·n­Ã¶¹®ÛØØèy Ó”F™ aŒ)`‹Å,ËR sêͼ |êªô"!"Ôƒ*i‚O’Ù–”²±±1ŸÏk‘ŘZÒ*eYÚ;@Õ©¸!ˆU¿rÎÕ.pGJ™ÏçmÛÖŽñD¤8#¥ŒÇãʆBWbî5õDKT—òù<cŒA©_ _ ` õŸôÿ¥?À9³b60TiT¹m©òˆ¨$/*}£ŠX©Rz§«‰a€sob(üR†�®ëær¹|>¯^€ÀlÄ <D<×Í»®ëy�H$6nܨ–ÐòåËÕá£ef…ЇB‹ 4]] k#ÍXuÊ™èËÙD’°‚!1Ôe(´¥2e‚P‚5 6µ–eUTTTWWWWWWTT(é€ÚÑz7år9å2fæÝ4RÎ5zh©œ:Øõ¨µ8Øì¡)v#aÙI³��¡$)ö#rõC„R‚þ€0"•M…mñG•$B"R»WM«Z'zQ…2è"ÏÆçÁ+2ô& †&%•˜Û4Óÿ9ò×¾>êÕN—�þ`ÂM7ü°á?5©ùI÷]ò÷:Ÿð‚¼\¾äª'WŒ»ñ²7V¥œ_ªs%ä};fðA·LÞ"Êo²'rñ}‡öù̪_æšúÓ¨IV»Œ°Çõ_ügd¿‰wÀeo×þŒuLdzßÏ/ý•æäWn®™þ‘ŸÏû[.õ_K…ãñß¹Èiøã·ézÊk?g¯‡UlüEz‚’@’ð…‚pDNˆDHPHín*pÌ_Õ}Z3Øë’`(‹Ì'Zÿ£¯Ñæ}ZÕ©.è]¨+˜÷Q Í;½¦ˆj‹ m'„€A÷_¬<Rr�� �IDAT÷P¿¥ž(À¦ßÕÀÔTk¥Y€Te€D4&°AWU:7dPi™6uz‘2!èáP1i�¦>(qO kSá ,dœó˜e‘çy uõ ù|^Ã-Õ„Öý*ý¼†…™L&Ng2/ïF†©5'BAÍ–-«;tèУG·–-[îµ÷ž·ß1æ¾ûî;øàƒ,ËJ§Ó*Ammí¦M›²Ù¬¹Hôt˜ðF„qଊÇãZÉo Œ”àÉó¼d2iÛ6iéUïI"ð'ØKBj>c˜x¢t‘˜m!í¨_,ÕÒÂ, |ôg…ÍT<HsiiÞ*+ ¥dVï�ÀŠÙ‘E¥? A˽өØ(Cï>sf³Ùl6ëº.*èšZn“9jìJ%ŽaM%1¥xz¿³0‘*{×uC“ÕØ#ÀUï wM&Èb{¥n§rn,‘Ýg¶y׬֜¦Èf/Åy0=P(¢•aæÈˆ ‘ÙDéþÒ @ëðõan"³çš{æBÃÜ)ÒºùYé&3ËÚ¶`âj‚°äÊl1†Å-”NŠ*Wx¢¬Û2ù+>sÂÞ=[&â-:ïrôm×U{‹_½øÀ^5‰T»¾óóÆ!Ý8ëGumyÜ?µvÐ[üú•‡õo›t*:þ樛?*£Ð•k>ºaðvm’ÉV}¹æÝ•ÅÄ’çFtqº]ði)ÊÎ~ò̽»V%*;ïqÊÃÓõÓgÎØ»e"Þ¢ËoO¼÷«º2_DOß®ú¬U«}jp¢ˆ•‰!ÏÔ•¼$Ö|pÙî­v»uŽÞI3Ÿ8g¿5‰DMÏýÎyrVYUxé[�@u3_¾ñ¤wئeÇSßüYjÔÿ<e?{ñ5÷Ð+~ÊKrùƒµ=èÁèT.{õů¶~T¯&S}þZôßÓƒ~«›é•hÝ;çö‹WŽ|µèä(>'½žuÈŽ*§ªÓNGÜðÁêfÑÆ¿‘2¯Ûv—›¦ÿ_–ü*„•?ýôöKýô‹È/LçêbÄU†È�9`€ôµ‹!® ½"8];÷` ý"—ÑÈýÏ|E_RuO 8Ø•)hêzmÂN0nçÊ‘Öfëq™&µHb ňtRABªé RJi©ü’’@HeÔ·™s®Õ¶Àš\ ñ‡9:V÷«tìæm˜‡‘›¨'ñx<•J©k:Z#ˆq+nÇln$/ïfÒ鯆†L:I7f3*2œ²ï°c1˶¹m!/X˜ö}?—É66¤Ý\Š@êec2V‰-²Ù¬·ù\Fzîô©ß޹õ–›F_ÿùçŸ !b1+XœS‰7™ZÉûg“cªÖ•ê5•J%“I�p]WÁT,6¢Ö†-¶m;Ž£Tý®ë¢šlá‘ð‘$# Rð‘@ý€6q E™€šTêàUoµØEï-/0-G*s]7t_LjÔ lÛ¶J¥Ô«ÅϺ9­Oö B’¾‚¤2"`gç¶¥~U+ªçÈY*•RÝÓëM›fh%w"†ÖIj'*c% QS¦fMû™'O0ïj@ªªR1*‘H"í5 “R’¯‹¥¥;È<="ÛPŸ‡e[‘ÅæúP #ËÞĽ‘>DXÏó²Ù¬^ŸX ïK_Ôâ�]b¸¹ÌÌ·Ìbò Âð‹ ó¯‘Á€…Œ2õc3®ŽGýÃÌ_›:Çš¢Ò’zNÍ?EöQ˜˜§`O„?G@ ø\7ýƒé­O}øÓ™Óߺºç7ן8ú“<�ˆÆò«m®ü`ö´ç†®¿mø¨W7�Õ}ÿüUCvxå„uL,Üò‰¯uºñó•æ¿rlÃØã.y=‚ÀåÊgÎ:æAÿÔñßÏ|󼨓#O}dIáN›ùê¦á—Oòì2ýÏ}yãðó¿Øñ¯“çN¹oÏ— ¿fb#�øßÞ2âO_íþÀ´µë¾ô÷?^=ò†Ï]ó­Ìü7n¹Çžg_V¸dWûÂêMŠ6Î}lhÇmŽ9°Ò|É[þñßÏØw—ãùÁ°Åt¿¼ñè ¾Úåž©«×~ÿØÀÙ— ýE1â/û�­}{Ôï»kÕn=>qîÜûþ÷iÐÿÔøÉ oÀá#öKþ´×¤›÷Kä6rÑ+/NýÍð¡Ýþã™8þ{zbÐÏdu3ýO’7÷¾ãÎz­.^|"FÏI«ãöƒG=:qîâ>»ïâ1gÙ²M3ýl"Ïõš=ú¶‚b}G\íQ½í-—ü7…‘쥂«!!A ¬3•ê‘«$+HÍû"HÊYÍ)Êtmdhá"š.0n¢`ÜÑÁˆ¸nvŠo®`\Í+¬þ Á¶i;`V[ «L*0AÆeV®µ”&ø\‚#•ëϺÛús„e¹Ö› M}Р鈃�àyž… |×Ëe²ÙÆÆÆ††Æ††t:­TÄD¤½ÁU`BV‚Ì1ª«8qƬÐÚ92põD+¥”¹\N8X·nÍòåKk7mX»fÕôiS¿˜<yþ¼òùl<W¹[¶lYSS£tÈÒð×ÐÃ×Jo½f´Éƒ!ÇQ@Z†Ñ|ÍçóÚܽÈDJ©%Y&îBSj&7—¨žˆ†Ô“®ÁžöÉ× ]Õ €4ç\ ¼Êln��PÚuÎy*•ª¬¬TÆ&>×ÌcÅU1 æÕˆ”ჹ=e±­„^Š ê«Ö) ,bš˜f²$7$…ñ8"’JÝ¥Èö׋_IÇ!†„PÐûËìªjWKÇÌÚÊRäÄ€pf¡‚Bñ™SöÅÒCЍÈzÐmi9W>Ÿ×ëJ¯%âÑmQñ¹ªV‹¿êîé}9Ÿ#4»¤‹I#ê‡~ˬ^ó¦lH>úOhH”L6šü‰œe ‹ Œ“PËeŒUZXl‘ò²é5ÐdÓ­óÜ]§ гÏ>§Ÿ?¤ãºyók ü™Ï?õÝNÞvÊ®Ý{pÅè‘ö뿹ž�ÄŠ…ûÜññs'w( ,Þ÷s{=¬wuEû=FéŸ^¸pC‘fK®xõ‰Ûvó…ûöì±ç¹7ŸÙí³Ç_^|©¤'>ëåî¹ãЪҮ¹“Ÿ~fÅ +o>j@×í†ÜxÕ!ëŸ{â“,@ãœïu<`Øþ]*+»üþ˜ƒº®]°¨HËk¬í{å;o^ÜÏ*<´“-”à ûú®›&íqë]Ã;2óïûCû®'¼²ž�k,ksƸÿ²WL¿#Oüdiÿ‘çܽEeçý.¹àÀ5ã^úÚ£µ/ŽÜ¦Ã!-”Pö- º·®8÷“Aϼ÷ÀŸÝ¥GÛªøæÔÖޔϮ8àÁ“w½÷ôCÆ=þn­hº$Õ®|ëÊÏßëÞ“ö{êÖ1?¬ÌÕιs¯ÇžüF�¸Ëô&ú��þÊg}èžܦ+Û j˜ðÂ[öàû$�hý¤¿·G—ªx¢uŸA<77 �@ëÞøó¾ý:U'b±T»íõÔ÷ZTâ~vawŽˆ‰áã”XEÌ÷â÷»8r�™ÙόԧuÂŽ¥ZuÝñ Û¦x��þòwF¹SÇŠxªýC®ys‰Â<ÔðÝ#gíß³e"^Ý}ßÓü64q¿~õà:¤bv¢¦sÿý¯|?jQ’óÜmß©*YÝýwç>÷ƒ–îõ$ýýç°mëd<Õ¦ß)/®&�ð¦ß9°sU<ѲÇ~ákbÖ?ŽÛsÛv•N,ÙvÐØEÙ®Òº×.ܧ_ç–ɘhÓÿˆËnúó»´H$[÷9dôGëTçäÚOÆ»gÏÖ©Šö; ý‹6Û)b5ˆ•Þ<|—ΕñD‹mö½å+�hÓkgõm•t*:ì¾Fß»ú º¶LÄœªç¾›m‚Q¹‰£nß­u…c;5½ºñÊ{öh™LTwßÿÂW—ŠÍ2êç‘?}ì‘;÷h[áØñên{{ë+£_ò¹.Ù±cMÒ‰×ôøÝy/Ì÷ÀûâÒÞ©X!�h݇Uõ»òë-imåšo;f·mª;Þ¢CŸ½Îyi¹„¦WKîÇ—.þc¿6ÉxeçÝŽóÉZu:Qí×÷ŸºOš¸íT¶í¹Û1͉î>oÉW±KךdeçÝO|`jCdå¼zÅáÛ·OÅ“-{yàÑDCþ´»Ø¹g»ª¸«ê²Ï™£¯?yÿ>mSñÊÎ{œúÄœ<˜§�€ûý]gÞ]uý'v2Y¥ç$V8ð»öêPS·*{önÿß$øú$ö­»Úˆèpÿ Y²O·¼òM*ÙeMRéÙNž\ÕuÔD� ÿÑ9ÛžôF�À›ôçž­ŽWo¾žùþÑS÷ìÒ‰¥Z÷>þÙ•Ü÷ÏìÐñ¬Ü­8|6ó ò €@Ð ¹àSQæ*ó¦¨þ'†A�ù$}’ˆgÀ™DÐ?ÄPý˜×_]iÌë)߃»aB óú[ª*/…^æ•TñšÞ¹ˆó<…1 4Œ×ÕJ?Ä¿Á[TðŽá�h­,…ùÒ4ð#¢@7ú�c9IAäײ÷æÒ *KØ„­†hYar)¥‡(Ix~.“i¨««Ý¸qÓ†ÙÆŒô…ŸcÇâ1ÇqrOóÊó<ßó„ç“AµÜ²¹…$$q@DzËVŠÄ¦ºJ†ñH0;(¤Š†Y5-*Û¶jÙ¢2e1^Þ.` Ìò%ù’òU6W„è…s®Ø®V‹ïûÊ&B…*ˆ õœˆ²Ùl}}}:ö<1æ8"q +XòLYÎIA"PÑ'8"ÃB^¯C”Xèk 5êPÂEºOY=¨’ �»n“×X¾AJ©ü1WUU9Å RF%”H¥¬XLyB¨®ïç\×—RE×ð¥TË]}VµÑ‚E:S˜â*½†µ�†²�"ʇé0”àFû8ø¾¯fØû@(ÒûÎ÷ýl>—ÉdL¨™©}Ì…§?›ð8²#€‹ƒ¶šçIÙõ¹'‹Ý°XõmbRJä#DÅ^BÆÔB”�žž‚ˆŽñnäøeÅÑd#‡[âDø¼"¤J€Â‘éà# Éæ–zb1nsK}PÎJÚvq#œŠÞé&1Öä5 ·Dž›Ì—Å2覨ô¨üé”›ñÁÄM¿´ok„ìœïÖ  „±þ;m'fÏøÑlùÇ«ï¾ô°ÞæQhïzÈøø¯{}^íªþúè‚§ETµþÜs ßŽ}-��Þ{§Îܳ}�€Ìä›þôÖ>co?¸uÞÉÕ3gnì²}ÿ*�H Ø©WzÖ÷KTîsè¾kŸ¸æŽ ËêÿÛ ÃN;¸…ù^b¯ ï¾nø5åй\ôä_^lqε#Ú#�°ªî»ì¹çÖ€Óÿzë){´5$ À;ôë[9÷­§¬ÉùùÚUõÂ^³t…‡‰Î;ìµçÎݪʾE›ÞxdÜú†wNï]¯èð›ác>[¿™I±úlÞ'Þ?á¤KŽÄ/n™üU}ådÃÄkß|c]·ÓŸ9ù¯ìÞöë ½{…[Õ©ÿ¶Ùù3ê$€X´âÇ ù…36 �¹bÕ›ÚöÿM¬‰º¶Šê>xáÝÔ#öŠƒ\öø)GŽYuÈ?¦ÌŸûÞu=>>{È•¨qá7SS'ýsö¢SÇ]Ôq¹ŸûZ0R{ï;hÌf³µÏu��ÄÜ—_úaÏáGtb@µï^xè9{\ûΜeK¦Ý»ßÆ/g¬”�þ¬¿ ?æ18å…ióg¼rNüù‘ÃÆL÷€6¾9jÈ%Swóñ?Núû^³¯|Þ«ë 7å†Á#_HžóòŒ%Ëf?4~3uq®¨ó™O¯>òœ‰ÛÞôñ‚EGwyÿìÓï›§ `QO޾𰠿ê㻳ÎýìéËöo‹�À»5vÂìE³ßº¨æµ³Ï|™+¾™0‡[¿Y²bÁÛÊwµqñÔi•§½=ÅÒ©ÿØoÞÝwNÛåÎ÷gÍÿöá–Ü~þ]ßú�rÉ£'5¶aij3–Ì|戵wå»é(«AÌýûˆa»Ç=ùõüÓÞºwäv�`j÷‹Æ};Á”‡.¹åä?ÍPýÜÏ&ñ‘ãX¾lÖWïí4Á(Åô¯r‡=5wùòÙ/H?vËû®~mÚüïÇÌ?vÞMf7èŸIrÍÌ/—ïrÛ7óÏùdìÁëïzäí3Š1гÓÙÏ|6wåêy¯Ÿf½pîUã7Y;zp›oßùh@æ‹ _µØÿ€¬òÕ$~;üȱuCûjá²?8·Íô¯æÕS“«%ûùµGœövûËÞš5ÿ›'†ÖÞ}Ô‰/’ W<{êá×þ°÷ß&Î[¶pÊõýæM™½®øŒõ¾¿cøñ/VŒzcî’oïÝcúå'Üúµ9”Ü—£‡œôZͨWf,üqÊË·Þ—oäÚYS–ïþ·KWÌï‚ÊWo}Oî«y?¼QÍ^õòz2N!�oöÝç=Üé–{ëb_M“ùñÇV%ÛlwÔK®ùÛ)ÿU^4ÿƒÄû]>¹!›ÍÖ¿{V'*Ù§[^ù¿&•ì²&¾‚Ê홚}î¸þË/�îg“×lúúËÙ>€X4yòº]îmXáцqWÿù³wO__¿úû×G*’TmñðÙÌ7È/@ˆ*)T4\†yý r †Zn›÷ZýDëÖ"w_(Öucè+ÞÔõ {]�Šq»úU_y˶L˜U™âÝŠ¾šG´L¡C9nê—~ gV]ÕHI7!K zMÒ E8%×ߦH_ŽÕEœš å¿­bÅ%“I!Ć Ö­[·aƆ†/ï" ¯ t¾@Qœ/ ²t‚¹*…d€%d‰í‰2XNOn̶…ç§ñÊT £ªªªD"‘ÉdÇBÔ×××ÖÖ*½±ŽmI¡ºXǼÔë4äV‘ð1•J‘ÊbÀÂwžçéw‹I¨í¡¤!Œ[†‡NÙy)E/æð™7Q3µžB;¥‰�õ¿ïûªK:w©Ù†Q�Ñpé×r+=_zÖÔÕ„¯¶,ˆÇãÊçB‰!)±B2™T“ɤÊ:¡’;è†*ŠeY²8l„†ë™LFI”(A 0ŸÏ›øÖš˜ãcÏB(SPM÷ s™ÇšþU‹ ©Ä)ÆœJ,Võ›³pYìZºIÑ@òP"_ˆì ÅÌ\.§ª¿ê%­md°1#‹¤€Ê Êö_×c®1áë8/º!ÕœJs”ÎE)Ûõ“²ãB#n)‡¡äè6ä¿E] K4™FqëÈ[ôüÇ>ÒæÆœß—eÒ2UQ¡¾Å°¢2éú¦DýØzÈ£÷œÿÐ9ƒ¶íyðƒþÐóïÂhýc‡¦,˲¬Ä·Í­ohŒW¦‚«?¦*“nCÚðgß{é+ÛßtýÀºãÞäKz;–eYv͉¯olh„TeQ'Ò¬ÛI·]Ü}Ê'íÛ£ßÈ×Úž|öþ­¶vàù/î½çûß_|ÞNÊ&Ûò—W_¸tÏTÅ+óô¨ŠçèQUÑnûÁ7OØ`%â*÷¾âÅñ7Ôº‰FýgÌö·?á¡÷~X_»`ÜQëÆŒ¸øõrÑnTWo³M2YUÙgx¿^Þ¦•MxÓÊyŸ|•tén;ö¨h½]ßãÿÔ³ñ­Y³ÜÊ{Ö¬øjy=ѦoWäzµÌ|»|ƒ„†o—­èյ˭dJÙö6½ûÂûÕCGì¹øåÇ&t<÷®«ÿدs×]ŽûëÍGÖ>ûØe¿ÁRí{tí´MŸ}ϸÿîù«¿®nyȬX<;6�ðg¾ôòÂ}F n@µoÿãE÷˜1;~·ní;tëÚFùcøß=óøô/{þïzuî¹÷Ùc¯ÚgîOínzûÑW`äm· Û±K—Cnºó”äk¾¾žòŸ>úøÒGß{ö>=;´ïÒ½}‰lnâ“Ï®=øš[ŽØ¶M»¿â¤žß½;aE{òÖÃ/Ãqcn?f×î·é»k¿v �€Uw°mçÝ÷<ëO‡W|3yZpÓ·[uéÞ¾M‡žÝÚ`Ù®z��˜hÕ±}›ý?ýÈ^"Õõ7}:wêwø)ƒ».1³žä¢Wžøt›³þrþo;¶îvàåý!ýÁ»S½bVƒ?íéG§îrù½зsÇî;ìØ]ÉãbúèÖ±sÿÃ.:y×uS¾\¬<«èУsÛ¶ÛôìTQ[žQ��èÔ´oߦ]Ïg³³ítìÛ¿KÇ^O¾Cý÷3–‹&õ¯Õr›Ûtê¾Óàë»f·™=U®[õÚ¾g»êšmö9çÄߺ?Î]&œ½ŽÖöÓqo¯#p§N˜Ä<tÏÍ{éøÓžzøë—Üwù ¾ÚoÓ£S� &VKnâãO¯>xôßOÙ½{ç>^v÷½>{ì¥ùÞÒqÿx¯å™¿ùˆ¶iß©g—–%ÛÿîÙ§fîvñ˜vh×fÛ!W³û’÷ßÿ± CÉO|쉕ßpÏ{÷ìØ¹÷Î:ÇÊ7¤Þ°ªÚvlÛ¶Ëžgž¸¯mzïØ­c×=Ï8~/æôù¾q ÉåO_ñ@êÒÛ†ã­Òs2 gèsk—óôQk®<êí¦°`3ý"„ȃS7±O7¿òÕ¾–ì²²ÅÊŸí¹. Úv΄‰kI®˜øiºÿvuŸN\*iý§æ 8è÷üñÂ7û¬²ÒÙ4Ú¼²¢C¿^m£ÂþÍ>�Ðä7È/@ê¾'¨èFLD(Àýb³jÐW.ÓKy8=ð½îÕ¯Ì@¨AC¹1Ð7uSߥ5ï¸úžªûVÄÚGb͹•r²ôНï¥Ú¶È½™6‹ÒÍȈšt?·B˜}nªÝ²°Œk±†‚ea<�(œ¬Ô¶Œ±ÚÚÚ+VÔÖÖzù¼ð|èØ1Çq8ç Ix¾2Z.Rƪˆ÷Š&#¡‰ IARBT³š,ŽR©~ub±l6›N× á¹¹lݦn.[]UiYŒ(lnÛv"‘ˆÇ³fÌ·Ât‰ê¯B…KcÚ2B¯54t@ac]Lq@õ�ˆˆÀ.ŠZÈL�&�%†kÃÙQêT(‡EU4`ÖŒ€D"¡sdªnD¦^A=Ñau„9%} Tmz"U^“i¼Ã,O&©¤“ˆ;‰x"•LUV$RÉdE*Y‘J¤’æí8±x\ùDdóy/t·q}ß gVè<DJ”fY–Ú›h‰�Q}˜\R™2áÑ3UùX,¦Aô†‚bèHÅžXbó_:Gú­Òz̶ôþ¬v,–éD&¢ôP‡”!’'|õ¿„`[ ’ú¼ C`VlF#òKäDŽtO=Ô:ÿëÌ_õÊ!C2¥YdŠ/Kg$ò$ÂöRÂròsº#¤ß‚àŸ‘ÊäôÝjò<sâ-9aÜKç÷w��“•¬1¬sÓ PQUÑ.žù×ã¯Ï^>yÁâK&ÝÖý•áÇ>°j†Þÿõ´iÓ¦Mûæ¹Ó»WU¦réÆ`3QcC&VQ£ ¯Ýr?ž}ÍoÛ;]òÖÔiÓ¦M›6ùÖß×T¦ ±¡ÑìDeBfÒ5Ç=Ôaìw‹¯œÿÞ…ÖØ!g¾¼•Ð&ûéÓ/×|â6[Ë%Ö~Ðè׿_ÓèæjçÝ{PÊêݯ÷æõœ��ù\[õÜ®K 'ÞnïQ§ïWÿéÇ3š2@•ë>þüε×ýg4×#ÙÄ@äºô&¨jÛ1¸‡Ù[ÔäÓ°ã>ÝZÍZ2«.;닆gîºÝŠ¥37æf¾¦ý>]Ûü 6È´á­>jsÔð]m�±rùJèÚ³k0ðx·²+VD#Y;½ûv“+——“røÓ_·l¿co‹�rÍ’e~×¾½ÅEÄÊe«’Ý{´ $B5=ºW¯Y¶Ê[»l…ئg·À*ÂêÚ³ ­\¾ZÔ/[V×®OŸê¦f‘V®¬«{帶ÊÔl×›g‰Úe´'K—û{tmÊä‚Õ´ªöK&®|W‹nᬪºd2Y��¬lQ ÙlĪå«ò3oÞ-ÇãñV#ǧk7l”Ŭ±|ÉÊd×î%×íBŸj°1‰.YžQEs¡:Õ˜!�ÀʪJÈe²M2ê—!ÖnÛÞU«—­4™ã-~íÊÁ;wkÓ¢²Õ€Ë>s…�±ÝO<®ûħÆ-ËÏxçƒÌCökJlX¾dEjÛ¾‹¹ÔDÊ m{t‚Cò®=»áªå+½åK–C¯~›ñ¡«–¯Ê}rAÏd<'º_ð©§&LÕ/_Vß¶{·BW©|CÅÜtªZÄs5NU•Ëf¿f'Ýyû¢£GŸÔÕYÙsRƪ:í|ì7 I{ö“l™Íôo -ìÓ²+ÿפr»¬5q¶³~‡Üå›÷&lX7ქ^{Åï}ðÑšM¿ûM¯CþØ£•ùÍ^3äïÿÝç³³wì¼ÝáWŽŸß”ËR¹Ã'Zf3ß ?‹X¹¯R$`–ô…@�dáÞ¦Ñ ÓJ™·ÛÈÍ/(P®›Z÷«c¹+œÀì‚»¯¡üa y²’8çB• eù_d‘J,ŠKïëŠLu®…L�¾̈7¦‘ž‚cÁßU*þ¨F-®aƒ*  €RÐÐbA9`á'4}ß5'¥{ ¢ŠíÇK‚ �I„J¥lÛÎårJŒ!‰b±X»ví¼LŽ„ð=?Ÿq 1f1ÎcP° ª’$H�EL0BT€Å®Ú H) =4:l`è5€vË 1ÃÈùÀPÁx% "×Ï !,Æ5†¤PlẮ6h—FÊC+f+hîÅRÐ�� �IDATªlø3™Œïû*Tz]iõÀV#>œs Q,hÌu¥òbrn)Ž H†¹IŠÊÅ”\åz°,K¡wß÷+++ëêêcUŸ¥”㪵ÈrÒÛGªY` €!G&ôÒ,-,öâV/JßG†Jn¢Õõ È…Ä e"éÄã5ñTÜs·ˆy~ÞI$C !p@I ç\%“IÏó<ßÓáE˜™"8mBÖÅ臈hÙ6"êÀ *uEa±Ù–çz¦TQs�8¢@"ap,²%‹vSÉC½°õóx)¬p$ N§ÒJt TŒêÕÿ"ô«R.$º¨0@b�’$–jv˜u#ˆ*£ ö#)ùug‚•P"h°Âd4¦Œ@‘ Æ©ž(;m¥¢ù£m¬"G±nÚóDYno zpU÷¹ì1k6J’‡Ëc‹Í”k¹öãËüóâãþùî5{…úªD¿í{lúxæ*9°+wÖ´9|»áÛ–GÈâÇ×^š¾ÝéãúÅã¿=÷Š÷šðUæ¼cºnW”±úÁ3æøGïl˜7}f¾ï±ý,÷Ë7ÞYúùs}­KÚöoWûúª'ëß>øÝ0 åÒïg5Ð53§-¨è¿}î}3nܲßÞ9¬‹Ðù€+/>äÞ3?šæ ´e{ï›·ÞoÜÌï+·X²„ü…OÞñÂÚ/Úg‹–½¬S—ŽôñÂ¥ZZ@ù\œD¼‰‰i\ðÂõsø¨#ï:²U"=ç¯}Ûd¥m*jhÙš•ú0�ðVÖnrR5•À[öܵõÌo?œ/tØwÏ®¹Ÿ}õá|û»Ê]Ïiù¯H Ö¾ùâ'†]·“�¼cçôáü%>T[�[¼`U¢cÇj„µæ+bÅҕЦ}²ÈÍ»zÕzß¼8nÍÀч´B�`5mZÁêå«|èiÎïØ¹}fÊ‚5¶a�´iá¢Úvý:Øm3ù²‹]è�ñ‚%Ø¡s;žjÕÊÙ°be |b�¬hÛ¶²õñ/zü°¢˜€Þ—fOZ·o‹Ë. */[O¹‡å»Z¼*!Hcªz@D¬mû¶ÎnçL›tio³0­1X ¬mû6™© ×Jè\vúTJÖ¶s9FW~G„=�h‚Q¿QÝòåéÖ½Úã­íŠSŸ´n}ÞiªêŸ<¢Ë��` 8ý¼½ÇÞûгKßL¾cЖö'kÕ¦evÎÊMíŒ)j‚ ¼²cÇŠµ ¥i÷jKæ/¢;w°ZµiEŸ._% º‰ÍÚ¶o›úãÕ ß8¹>ÇŠví’k.NÃîÁ1‡åŠÌ#"éI(|�ð¦¿ñÖü™ó¿=|2´EÝã‹Îù¤ü91¹ ú¹Ql›iËÄ8‡Â©¶¥}ZfåÿšT~—•¡¦Îv«íÐ!yý•WüY{ž0èéÝ.yeü¸ø¤.GŒîË9/|³@›½þôà„snùæÁ3†žpj»>9·lCeŸè)VøùŨè> €��H_Eq—¾Ò·9:6·0ˆE±Üt÷@yzklÚj€2b,›Y6SŸ¹…1ÇBFq Õÿ1Dz,F(ù¾ô€r |éY1®> ò %³ù¾«â �¢ú_R‰^50‹«ìîJ# mÛ±¬c@eAýpà¸ú(‚[hq@òù‚CDqÎUÆxéÝŠÙȘ'|ß÷=)|’:FƒºÎç0@åÁ®T©¥VHSÊyƒ®©”ã:»8úÙ1à–�€Ä¸Dæ ™u½¼/<_ª2™D&}AÒõ½XÜá¶ÕИ®¨ªìÚ­["™¬¨iQݺU‹V-•ñd2‘JY1[é0³™L&#¥äÈ—92ßõHú*‚’|I¾ž$_qÀ'™÷=Ÿ$ZçdÓ¬ ᎂÁ:PŸ$„ )Á÷¥ç "´mG1Á÷ýÆÆÆl6+„'Zp› ò%]=·™…È!çfùjåp›ÙŽ &äB‚çKfÙã¶/H °x ˆy®¨«m`hY<¦¼ZR€ð ‰1àHþ!"ÏËû¾ Œ˜…ÌBA¾ëç=áúÒäƒH’Y ÃÏ)ŒJÀÐ> is+On.Ï‘$$Èe²ñ˜ãæòÉx¢"™òò®Í-×u}_ªÅ@¤v`ž'<!qä6!¤$€Ìó¥ïû9×u}_¹¸K�Õ¼úà†‘9gxeæó`€ÊçÜbœ#S¾èÊ)]àÈbqÛõÝl>CHVŒKÀ(Y‘´ËvìXܶ›YHHÌÂx"aÇbŽ“ÐÓ À8Z xÌŽ3´8³mËáÌöù‚¹Š¤¨ö‚ÊÜÉKÄã-áI’˜JVrÛ!äéL®²Eµs2¹<ç6c–›umfW&+Õî–ž‰6³É'?ï3bZÊV*OZVÌu}�‹Å}_ A–“”%"·mÇ÷¥ÆóñxœY\ÔgN ç·8ª¸-@¢® …s²$(£"ÇŽ$鋘eÇcŽòßI8qŽ’H$Ò Ébå (´b,íyžô=P99¤Ò—Òçc1˲çÈcÜr,ãÄH¢DF*7…F&ØVN:J¬¦ aòù<·0ïf…ôì·cÁ×$Ÿ@Hò}á"#'n3®—“äëP36‚Üe˜UTõ1’HZê“Dõ\J@àŒÙ@L‰(µì@ é8çv,&¥’¡]:·–ÄÌ{.~¬ú²G.ßÙÉçr¹\Þ“�Ö€cOüÍ´»¯zrêâùßqÃóîàSoÂÊžuì×'5íåG'.mÈlœýêï®ê9 oòbŽ<ùÀ5\wÏç Myðº‡ï{òðžÜ9ô‰¡�8ÿÞºúdMñÝ7¶× Çuüà/×¾:kéœ7Fÿåí–Çž¼x·~Ûâägÿzucãš©O=ý©Ûwû^[Öñˆ…“¿X;`ï= („Ö¾sõQ#ÿ:¥±Éwd¾~ÍüÉ/ÞpÔ ‹¾pý}£úq€ôäÛvÝš`5ï>xø.K¿éÑéê—½÷—{?ï6ôÈ›ìŸZÖ^Þ÷õ--Ç‘kæ®Ï°ã¬Û°x€Ž½÷ß½ñÃ;¿ž±(½~ÎÏÜ»0yHÿþq�Þæ·ƒR3îùrÁN=û§â;ÐþÇ&Ïhß{÷ÿd U¯¿ðY·aG+qÖmØ©W=ðç[Þ›»|ÉÔç.¾æŸ-Fž:PÁJ±ôó·>›³tÙìwn¹êÉMŒ<´-²VÝ»WÌ}{Ü”%+~7}IÎòâøMƒŽ9¸%�`›ƒ†íWûôU7¼õêUó¾øl–²J°~sü);|{û¨{?[°rÑä‡.¸uRŸ“OÜ-VsèiCé¹+®|eƲe³^¿î²Ç³ƒO=¼ &ö;úpçµ.qúŠÕ‹¿ýô»Õ´4™Jxó§Í¨õûŸ0¼büµ?ýåüUkW/þþë¹ Ò“V‡œt˜÷Ìe—<;eáÊ• gL[X¿U{§|W·Ø›÷züsÇ^tç»3—­Y»âǯ§/u£¬{—‘Ç÷ùê¶sÿúþìe«–ÍýzöÖéÖ° FmñÅòŒú׈ÒßøîÔ…ËMyâ’1Ÿt~Ìî6&SI±xúÔµ.H)AæsYW`-ëtìÅÇ4Œ=ÿ¾Ü°“~Ÿ�¹ø…?yÚ#³}t’IXùýw+r¤÷½ËQGvútÌŸ²lͲéµDlŽ ñýN9¡Ý»£/zòëÅ+~üèŽ Ç.Øç”á½c½µýÜû/½ë“EkVΙ8y^>pÑÐÍÙ»;²ç§·Žzð“¹+Ö®Y6{ÊÌUÒèXb¿F´|ëºóúôÇ+—Ìšúã§lC[FŠú²{Ç<?8½o®Ú®Å±ãëÞ:¹ÃaeÏIª2î¹ 3-_6ïËç.¿ñŸÖÁGîÓ”¼«™þe²»õì¼rÂ+æ¯\:ó»ùÙËíÓ2+ÿ?ÔÛr»¬p<96u¶[;ÖὫ¯ÿj¯#Vµ<hèî“G_ý^—£‡õ‹,çüÂ)ŸÍY]Ÿ‹uÚ±_[ª¯ûÉ1Ë|ƒà/”ˆ1Æ@(DƒX1fÙ6)gs›s›s«œÛ³úKH;ácht Àž4ZTFàê6©•Ï ˆHÈð–¦1³¶YÕ¿ê$…ªT_Ð'I,Hó3“Rçþ‚b£b“ꉔR&”R’.°$À†Ñúº©Þ•ªš{¥v3-óɰË5I÷Gá#:FmÛ¬{¢>è`P,ÄQ¥ÍZ”ä‚ ¢Ê«„ˆ®ï¥3�¥•-ªZÔTW¶¨J¤Rñd²¦¦¦¦¦Fi}•Öˆ¤/ì×ùJÍ«´…:Å "Z¶mÇbRÊL& EX¡ùêKU¥P¡tâ4£2Ý=TgÂ×ys¹œ"IBÎPÇÞSùˆXWWW__Ç«««•=‹Åcº!SYjs«°­ WÎ9¢š©"µsdŒ&>dŒI)]×Íd2Ê·ŸÂ¼º¤Š¡ø¬kÐ-ªª”ìI׬MZxè$bòŠn"ûÅìž¹M¶‡ï„q+H�€€ 4�_J_xBõ$ Ÿ ÁAµÌBç`œ1‹«†u'ÍÅ¡‰”2Ng2U¦>ö<sÞÐÐÍfS©T.—[»fÅy6›uóyµ×8çÕÕÕŽãär9ut(geÚ 5ðÒ08R£Vþ,PŒÃõz&"%yÕÿK,00²Úµh5ÂmýÂpñXÌæ\Òœs;´‘‰œ!2/2õÍ4Ëh¯3Ѐí8‰D‚s®-8ÇI&“*QˆŽu¢ |ôÓþJfŒIÝUµûÔ:7®HÏ#OÌ 7»¹zKOln„HToqÆ9×!‘¡Šù“eñ´þ‹I³ë>»t@E¸£ÅàÇ×ð>£žy|Èê›öÝaÄK-/}qì‘M�Àê#îzé’¶¯Ÿ¸}ëêÎû]·`ÿûŸ»|§bXÌ:ÿÐógã?†ôßîà±ÙŸ}ôô®[ÕÏøo¯éîÝ¿½à·Ûîvö¤w¼|Ë~)�¶Íi?s‚xhH¯ššž‡ÞÓ8âÉGÎê¾5ÕùófÏOöÚÖpû•õ ¿ž4iÚ²+îÜ÷ÏßnÛ½O¼óëŽçŽÿêÝ+vM�5.ýîóI_/l2Þ�ßöOO=1¢áo;·íÖ”ÝïyéÚÝ›²tHõvY÷ºÇÆ¿÷ý§ýñÓEZ¶J ðV{×=÷â;/'=·?¨ËÊ'oœ]‡•ûÝ|Øà6‹ÿqÜŸõåÚÝ^raç8��ërX¿ÎYìÿ‡. €Ê}{÷^×Á};þ :¹üŸ/Nî}ôÑÛWBÖåÔ'^½¢ÃÛ§ïÞ³ï Ñ ÿàë·í¨ñ)7çɳöíÓs·SÿÙꢗ>±ˆxÙ˜#êÇêÕ}ça7}°ð³þ™þÈ?„ú'¶Í)<ÿ§VG^ûœóÂßq°\üò‹§Â£ÃwèÞÿÈû²#ŸùŽ6`ËÃïyãΧ^¶_ïÞ{_0©ß­¯ßT¬|×Ë7n7õʽ»ïr܃Ó3sbÖ€‘Þxÿ±×Mò*¼ãû~·ð/CvìÚ©û΃/7χÜçÅ=ÁvG?ôƘß̸îþÝzì|ø%ãm‰pù®n™x¯sž^åø³÷íݹsßßú÷Ï×É(«!¶óU¯¾p2{æä=zvíû»“Æ~uî(OM0jËTŽQÿ*ɵÝ0d§žýþxó’Þq`†œwbåø“/zƒ¾á³_—êÊÝþ<µkßÎA`‰ÔþçŸÑzŒ<q�€\?wòg“f®v¡âgŸßûóQg>±*­÷]bŸ_¾÷÷Ëï8¼o×GŒù¼–'†M2!¹Ïͯ=zȪÛî×c—ǵ¸püÓgvg`õÿósO ÷Ÿ¹C×>ƒ.g9q¡ÐÜjŠírõ«ÏyèøÝ{tê2àç>6=mv¬ò€Ûßxäàµö›îÝ·tÆ3Ýò m™_[:…Ê¿µáÇwî9ó€=zî4ø†Y;ßñæöڗª™~2Y»œ÷—Ó“/Û®[ÿƒÿ<~>/»OKWþ†ªÊí2ãx,,rlêl·¶?á¤6pø *ÀÖ‡ß×ÏìtÒȨ-\3ùÞ³~×£UUËÞÃßì|ùgoÿSí'Ê}ƒ”·û©D�–m[ ]}¥çûÊÃ÷=n�ð02<„¨�Sö`ú–ÅŽ¯PŒ48¿ê0WhXR˜ÍK×/ ¼‹ë2� Âtñú6¬>s,¸!€qé›Ó½’~¡,€"õÈ Œ1`Aå:Úœ‰ Tg´·­x‘m³nšM¬¨l‰ÍžSèQL Afýz,ZUhŽ”½»636m¡I‡ÀÈ_%cÏçóéúuÅgˆŽã$ Dz‰(ŸÍ9Žã(ãs_xž—I7¦ÓiË.X $f[–ei3AÕãB›gÞDÒu!C1"* ºÎ‚òZòÂmKé\.GÂW¶úŽã(ÇËårÙlVyhÇ~Û¶“ɤe'´˜@M¥<*hœ¢M›6®ëª|ÕÕÕ–c‘ádQXW@‘þ›kôd„Ò.ÕÉ0;@°HdAðF„Hµþóù|EEE*•jllTÉ|W�€³…ܲ’ɤ+|"²b6"’:¡#WæèJÂC÷'²Ì"ób>1Ì—²2‘X`…z]­1[A:ló r„ S9�¢‚èº_ 勈j¿khôœ©¨–ùü«æTy2‘HÄ,{Ó¦M™†´Š7±aÆ-Z ¢ëºñTREŽã466‘òŠÒ˜YñPá8Žš2%\#*„?TÑXôþeŒ/б�L”ñ~W‹>ôô`:ïh¹€Ž1¡’g„6M‰ÿ‚ù<zÜ…œ4ѵ™ig 3¡Z€Ê~Gí øC=‘aôõ\J™¬H566*ÁA6›Ure”¤„ÅjaCxHú28üuUæpL)€*f³ G&…aÌ¡#€P² ¤x€`°Õ„D†QÁ_®¼æÈ¡C6wçüðÃ[´hñÛ½·™2Íôo¢/?ÿô¿ó…^ÉEwÿ~ÇûfÒ¥ÛnöÎ'ß½ÿöã‡ÍúxT—¦±Pöý³úœìÞÿã㇅w?ª_öãzÞªU…•Y6ñö“Θ{ÖÔ·Î(oºÛ4eVÍ[)«[WÆò«¿~èìãÞ<pÂWõßü µ´'ÿyÚZVÿWSÑzvß?³ëÉøÔ⇶Â( ÊÔÕ‘XóÞµ#.ZÅW/Ó.úS†Üµ ç+Ú´pĆYÏ]tÌ=ÝŸy×¾?Q )7.þ±!Þ¶: õóߺnäåþÿcïËã%)Šü#2³ªúz×\ 3ÃÀ0 ¬¨,« ²Þ€è‚¢‹¢x +âz-¢+ž ®ˆ«‹‚¨¨ ?¼VEQð¹î{`†7oæ}UUfüþˆªè¬ê~Ã!®(òûUWeeFFfç7ÎoúÆÁcÿÜž$ôè6êÇ ù+=1V6Kóþ‚üü'?Zµíêj­:σ��ÎZGtã 7<ïyÏ+‡xÁùç¿ûǦ.!"c´ÑÆi­"SqÎTƒW¼„?…QûZŽËYìgî•-H\|`\Â!à åÔXBbÊK‘ �Fš¹13F�äEÚÛÜÁõ…Tø/FuÓ,ˆ]åI¡hÞ—g n|ryæ9Ê~ý‡{á•Ï%i¼€£ŠJy—¬9Š�ØdáÄ"êv»lÊŽã¬KÓ ´Ö†=´‘¼�Ö%<6¯%i#çЛ¶„3x A• KWÊèºhoçÜ ”+YX÷ÄWœsÊóO‘6]žP#Žc"âê­VkzzzlB[Kˆ(þÒN‡¡f½^çiµZ “¬µLIÒ\æCbÒtžü”fÊ¿BD ËRJÎ ÊògS†3�t»]Fª~¢>*"À~V;Ï?¥ÔÃþDª}*É¡´CÄq™? íÕ9¨îM«RJkã\o9ðlbQ¡æ¯ʃû×uw1Ûçº Y‘TI’Ô«Õ &×?DD ,ظq#/±ñññ”\²i¯>V$U*Öñ=˜©ç¬¬kÙŽ´ÖÖöDK+£µ.U©ô‡Œˆ ÆúSÌ’#Ò+¼M’ó¯ü\J)ôôŒ4?¸õçúVx» ï„>çy¼ÖÚ@kÖ²1+\îĉ!Ä“‚]x´ÖÊhV¾°¶N&šù)+…sÎ9é¾t¯°öû:_R/Špú+‚ú´´\YQsΑSYõRؼv`HCzXrwœÿ?îvèW—òjÍ_œó÷âÓ÷óyëŠ/üËë¿rãƒÓIeéÎÿxÄé_:òÑê�Ò›¾þ–Ã>}Å};Á¢§ì}èg¾ö·Ñ ìÉ_œWVÿµ’»÷‡íúöKÂ^üžožvØ#Ñ�Ø»/|ßÁÿqÑ“MßöY÷íöyÔŽÊôÐÅŸ8ìØso]?G­v{Á›¾ù¹‡:‚! éo…øˆëœSJ¢³–œC+T€ˆgåsóS|F’F}5HÔ�ÿë%-/ƒÅ]6,Êo�J þù-9�6”#æ%÷œs–ûPrŠ&ÎßFò¥€ƒбœ#RÄÙLЀsªÀN9š÷ÛÿY2ðÔN}>ÞýH2çyÏdÍ–`[ Ú¡§Ía’s·'_Wøˆ¨Ùl2ʪV«Èçþ8IÓ4“V«ÅfyQ:Š"€ Öc¢S!b«Ùä#z„8MÅ’\âŒ?L锓mbæ<OÀÛ)õT�6I9W _%Ž•–µfH¥Rán$I273KRÚÁ ­´ÖºVøž4éA€ ,+ ÏÏßR/ñ>"rðún8ƒT<¾P‰$øÞÑ‚Ää[IÜ ÜÛÎûX"«µN’®RJqh"�9"ç«/¤¾<ûIDœ’Óïj6/È>7™ œ2í På¢ËkŸ†´²A)ň—r‰$n™(S™YkѡϲâÀƒµ”{ý0Î7Æpù•Nw“dãô¦$IÂÅK¢Zõ¡ÉÉZ­V+U­1ÕzýGŒÒ\qsvvV O2gJ¢ë¯ž&ÃIU­…‘/ɽ5;èǹTˆ Õbï?Ù&i­í2×�Øq(û^YD,:†ø$óg Zïd— åš‘”cs´ÒÆp$j…ˆÕjUtdY%£ƒ0ä¯*• ç¤ä9b®².Œo`_ƒ4Mõ@}‡Å× Ø\t©O•Üã-ÿdd‰¯¡âüh¨Øå(û߆ô'Úî]¿™y×#¹q›w\:ûŽÍßSÑé÷ßWº´ÿ‰—ß}âcî��˜=Þ÷óÛÞ÷¨Г¿8=bVÿõPøO§=°öQ=¡V¾ùÇ›ÞüèÞ¢·ã¹7¾ñÑ=S&Üâ§]óŠÓþ´F†4¤Œ½ä?éé‘ü‚üIdt˜RŠ :Gä:ÎøŠÈ úü?å6ÁH>,ïxêÁƒrBußß•Š/ò5~³ÒC†âÌ'Q¥Ÿ¥±ØUheD ½aúfOyD2 ¿Kb8 Â@žõu(¾÷¬ÿHáÈë…èsö¸Ro•R¬àf–:Iž_ƒ?5½Žqֽ̼B†<@É1Ay}{rÇÇÇ+• câ8Þ¸qc»ÙJÓÔ%¸‚ŸGe罌ˆ­V«ÕjùÉÉJ³\’7ùì£2EYåö¶H’’DJocˆ²LéâÝ ”b\Í70Œa=HµZ™™+•Í ‚€ƒÿكݤ1pÑJÙ¢�0 †„"¤,Ýæe ¤*$æcþÌ@‹«”â ð¤ûbP^€žb½¾•¾Èõö'Ÿ88r%¶øËr-›“EË�^Ø?wI)åÈ (-B¬’¬q#b†I’4Ö\“Ü}÷Ý“““K–,Y¸pa³Ù¼÷Þ{—oµblbœ“T«UpT©TˆhnnN’ƒHÞ~¥ˆ[Îã8fýZ³Ù””„4Èk¦¿ÿ".ò—€?ÌNû 7û“2¨¨‹„¢›˜ìi²i—Z“=D{êÌ_Ck¥Ôôô4/î$Gñt»ÝZ­æ ­TK­×ëÊE™!Ë9§Ì€ Rçe¼Çú~ ×e°ßc¦÷Óã¥àUD@YYŠ! iHCÒ†4¤'-EaD 4)­•1FçæO…ʈŸ­$”¦.3[Aˆûõ¯yä¿ú‡UiVZðÝn¹ñ‚!oYP·63P€�DÕwì& È?d_è¼h Êâœã �àA)tŽ‘S¨8»<å( ¤s Þ ŠØFKEŒËÃÑÙî-7ûüc=u.%ƒaiŠÎþéŸØ?¤x%Š\ž@1ó6ÆhÝîv¬³Yª­Ç&&8±ŸQØn·9ŒßZ+ÞÚ™³J^¹€g\â½…¾tùx À"‘+–�0¨9bôBÀ¨ ­çÝn·Æt»]N^À¼j·ÛˆÈŠ€(Šüäsܶsòˆ8¬:+TÁ>Ö¦^ w; ´¦lÞE>•&Cž w6¡ƒl¹²úDxƳó6"r:F6ÉJSìÐ!ïÊ Ú¾*¥ÈH¹­„Eejü>û‚êËR ¡É¬‰ZÍ× òy£µV›Ð‡»â@¤óÌ|6I­³r%G;5aÞÀ8Irÿ(eŒáú#–\ê,—PF«xRg·\¾ìÁ¼÷Þ{ÃJÁÌÜ\•0´6eE€øtðJ”ya݇ºHÎÎ0 %ÏäªFDtX^øà¡qŸ-"0ä-I¹Ó·Õ�� �IDATç×%6E‡ ½M'g)l–úoèßT =/¾•EͶ~Y/B’š”õw¢2nµZav:Þ‘õ)¾ð0“EàK;�xûdI³N;¡Ï´ì)`÷Î[Ìßò‹Ž·\þýÒ†4¤! iHCzÒU1Q ]De”Ž‘œK­ÍJ¶÷À†oVBDÔÊ¥‰Ä$%§@yGþ3H GI_Mà'óç&ÌRµg-ÂìÒšA ›€ËÂK¨�”È¥¦ÈÓ5ø‡Å|ì̧Üsß!¹Ã<’‹|8—‡Ò霉t½šˆ¥±ó¡¶tÂw•à‡Ö„Ÿù#h­M’òà|3‡åËXdŽ4§K°Nª3îÁu»]ž}ÆH¡µF€4MgffX°Â0d3²˜|S€ƒÄ—òZ}¨z ˜,»3° ãgö)Îó·³³³JqØ&/w`§Óá’ŒÏe!ðgßw_ ¤öžHÙÈ߯Åq±b… 3DÐ2äàr¤*¤íû󨃈àH4VÜKçµ&S8Öƒä}^ù|PÐÛÄðNyŒ“,á¶öÄ€õ2ÌäZ­&-+ŽW�´äd8þj€(êávÌ3MQ¥Ráy·ÖnÚ´©V«­^½šUHK—.œœÜ°aC}d¤Z­Öjµõë×WL@DâŒÀfp_mç›Ð Žcö>ÀÌÚ(“YÅ{ëÏ8zZ0YÂ"cþ‘oQ+JÓnÊeL–™’�Ò~U‹A+¦'¢þ"‚¢b×waE ‹ç¶ÕyEnDþäGFFF˜ù²š˜ºÝ.ÿk­­×ëJ)öÐá+¬ðâö%N¼€¼¿ÃþŸþ=ÿlÃ%ò·ˆ€N+­*Dgå×Í{ÝüCÒ†4¤! iHó”¦i܉ãn×Ã.Ÿˆ¨P#¢Iå„R„€ZùÇ&>;:"›Û½ç°ÍAÉ€J‘#rÖ"¸"ßã€Ò4³ë!d6m­”Ú!O†yį]źÇ1Ù] Œ1ûï#[‹¤¡xb+¥ùX©ˆˆ^Ð_�4:Pd¡ïðZª6Þ =@^ÂWT4ß—9Ȫ©[Tè%pÐq›çF€�,¥€�Š0Ã~`‰mÛèÀRö“ê+DòªHz „ÈÂ ãøÆéMsss6I9‚ MS…†!ä *ŸùW”PJB7‰s³ÒtÂü0 …3AÈLùp󀓘Ãl1ò£—ÿœˆ‚ HÉ1á†I’³i’ðãšs"¤)C‡ Ï ˆv›‡ ¹mßäÃá°(r¾ W[k­UZÆ8�¾™ˆ@)…€Îfµ9%C¤sóØFzI’8o\�P­V»Ý.(4AªH@cT­¤iš:Ûîv¢(Ò ã4‘üðÌÞf³9Ž;ç*• û1Àî©®²EžÉ¤Xt…¥àim(WW)Õ«ÁY€÷ytæ ž¸0 EB„¸¥uÊbɉ1¬àÏ ³•R3ÓÓι±±±$M[Íf¥Ze-—pÚTJµÛ]2F+… \jQƒFEŽÚVE”×)èv» Í à+V¬¸ûî»§¦¦V®\ÉJ„™©îÁÊ‚J¥Âê3Æ·,Û,lZëZ­&¡4쥂žªÅÉÞ˜/1k¿oˆÎ´¤>“ý–%ª›Ä‹ÆYk£Z•ņç8µ¡#îO§Ó1*¨V«ÖÚ$IXW’$YAÙÇ�€3˜ö‚†|ø-*YM`аÞfkxi—ß°DD.ÓâqVHÞCªÕ*³Ž½0� Óé "¯­{êZwÍ¢-ò‚2²c€uüsCÎYOÇšsÛqØÊêœË}ÎX29B…CýÀ†4¤! iHCåÙÇQìd|t2l@ó¶³g<(¥Š¦~Áþ½C§t�E\*¨Lî/™.ÅS@ŠºùMAïämÅôÊ׳Àæ¾ÄlTì¹¼Ô9ÇX œ”²îù®Ýò8xÑÒ²¼Ñ烓êhųxƽ«<Ó»¯€0Š(Í¢´ÀóûÅœ~�Íó8U*—f¾ÖZÈ!–R ]ïŒÎí ˜´KêucŒ‚Í¥[÷åÊ÷t`›­pOPSV7!¿M`'c­XïÐétXB´éå™+uƒ<3¾ÿo? eÜâK wÌZëâ‚“¿ƒr/™#¥Ø¬ŽœÏŸ#nŠMÜ QœØÿ°9Zç81Ït àÇå(4”Kž½ÑóÆy+qÃ>Õs@�ÐJ—¸ä?îó“ÿ”%SZ¤<@ÿbVÕOf«-äÕ333cccccc>øàš5kFFF,X@ÎiUHj)Öl‹,¾>66Æñðü —‡¢T£(MÓF£±lÙ²Ö­«×ëK—.]»vmšç>idVOLLpŽ=AQZÏ> ‰MùOÞLˆz_{<(:ÀgléºËBaµRñB“2AŠãN³ÅÅ8ªÕ*R&]¬Êá°üȤˆ3"”·,äì0°{þ·¢ƒðõM˜§ù„¼ò«|æõ.›·ŸÇ4”cÙ ß+DcåòÒ9qPZÑý8b‰ ËPh-! JàÐ0…á†4¤! iHCzÒÓf 'ÆYp¹g©€>šÒ…XÈor^Ýo(žðú 9Þ[þôÀmÏ,ŸEvz׺t„,e�—‘Ó~õAPèlÁÉ–ˆTŽ"Ñ1ÖEçÐ!’sÖq^+‚¼p#ׇ¬JC“þûh¶ÈCîÌ|bmêßããv1ز–Šè«Ÿ¥>óåƒR=goÈœsŽ ùZ†ƒˆ rzíH:=�pÖjfµÖÎ9'6ÑPX­VkQ…;‡!8 ,F ôqH‰{X,è 8\dÖ!�‚ƒ$IÀÙ Lˆ·³ŸKß}é{RäÒ¼¶½SH¨ÈvÙ-ó‚¤È¥i FE )/ÕNαÍÙæŠ!AMR[ÞZ˪—ûü‡a¨òÐwFÑl¾vy†?ß“ByÁ Î+jH`QîÇã�ÀXöÈ¡·²ørûÓÁü/|ɾÁ 2äR*sÈtkˆˆF‹ë8"«3¤�8GÎ�Š\AÀ‚‹ž:…rfzºFi7ž›ž©Õë•êÌÌLP‰| ïM´#âü!¼’Ù# 0ÓÓÓ& ø~cL¥R©U*’Æß³hѢɩ©ééé+VÔjµŽ#‚ðœýz ÷…IÓTÒÆq—/&IÒîv‘Uf™Z¤¨ËCDRÂíLÇDn`ùE…²©ZkTÊZËYû›*@V "Yž}ÊBa¥—Êr”³±b¾K”:ƒ¹§•ÃLX‘Zë2€²HÑ!Íæ¢´$E±(ÒÈÊ[ý›ý/tÆ9U\àýÏöïÕ’°€XwC@ÈI ÌĆ4¤! iHCÒ“ƒ�²!xÇ;�C^Íù5‘2QŸ»ÿf±Wƒ›)÷½Ç^¨s¡P¼ÿ¯r•îù§pß¼‚~o}é÷ÐyÁ|€Î°±+ ÊF*ðÁ¾‚dr§@>ßæì_bäÉî¸>2”l%Äë¿<S!ägbÿ8.'r¥P)¥!³ä ¥Üߨò`c¬?¨¬«@•j5‰cVd°…0Š¢ 6mÚ” Ÿ2( ò‘N–ìäÒkm€Ïí”K«`¡ÔY×IÄeÃ¥ÔŸ™ðp cÂ’Z‡ï´yÁ6±3‹�sìåÚŸÞãºl\¥bBq‘¹ã4¥Ù—Fœ—¡‡²|Pä˪'„ÂŽ¿:$–Û_Ý„àœ³@%NJç®z*fšàë¢ê’—Š•›ß.tÇ1¥vrÆ0 ƒ ˜¯„‘K­L:]Ê“øSÌë…‡ iøb†6IÙÀn”šã¸V«5 6þ¯Zµê¸å–[êõ:Ç}ŸÙ„ˆfggGFFFGG9a'ä&tv%`e±†_êarŸ í§¯|„yÈ!èœÃ)9p€R›:çt¤Ö)‚JŒ…a873Ûn·“Ä*/Kˆ¤ç¸}‘§ÙìïIi“)m¤Â|Ì|—+–s¥TšØ3Erü]—¿•J%¡ò%}r¹F³˜Õ”/稲̅i»TVK×YTèh˜›`HCÒ†4¤!=©‰Ü¼g¡¬–µÀx9ØeÁÛ^L{ÿWp ü©ò[éÄɶDq%øäû “—*¬çeZ$¥)ԀιÔZ�0Yæ-®S�œÛ]#æ…2×nçœôM)J‚òÐÃ�˵ÄÇAeF4ìÙj «—àˆ´w’¦AÊ(:uˉrc{†‡Ó”r/tò´!4HG í—$æ©È2Ô;Æk­óâëä?+|vD©ËRµ‘íµˆ@d³¹ã»ÓJF"’sÍN›RK\}€Û/2ò 2Á’ÏRP YÌèW¡MÝØØXµZm·Ûsss„ •ÒZ[›å)ôŸ7‡Ä8݆s™ÌQÂKÕít:&Mmöp>#D®ð××8w› ´‘Æ%}·Ç1yÉ2O–<˜_äÄŸ2Ÿ¥à9eøl÷o¦Œ{ªp[QŠ¤ÍŒDØG%k³t>OÝ×s ñÑ·*Y0•Ri76Æs6M5  £Ô&­¹f³ÙLãD†&¨U*Ýv{zãÆZ£á<ŸpÑÈ b·›°Ù\}£sF#Žã8ŽYÑtnjr;¼Ôj5¥(\¸x1§˜˜˜˜Þ0ÅŽ¥-ˆ3S��—ý³Öv:çœ1!æ¬,D*�=ÙVËÞØ†K@²õ›UŽ$_7¤+8À:ç)ÅBÖZ¥œ¤¤\‡Ušåâ¢è“oÿÇ¢RÉWo•šU¯¯ÿ•ë¾tù×]žCIÒ ÔÐõy+ö»×ë­—Ô‹l•*«÷‘UÅPˆìõ@D ¦<ÄÃÒïséŸØÂ=19ÿÄìÕžø4”œ!ýMÒ_—`ÿuõöÏM©Mîù ³[—0rÄwy–rðŽ­6µ~Ä|¤ÊWœs¨Ê!£0(}zé-þ‘ -¨hÑÊ‚Z³MÉ?‹säsfd&°rpÌ”FJ ">ô³"Cë@:ïÛHÅb쟉ýÏP|Q PnRöën°š@ŽÑþ\H;þŒúvøâ¡?Óƒ”²â³†ÞA¼„åzP1·±Ë‰_œ–)ïƒê¸Ûm·Û.I14=/€þ)syA5yäàóÚ~0ð¼9²N*”ìì¿0:>¦µŽ;ݹ¹9„‚ •8ïO/çP¤~c¸4ÈSΤSÃõð�€Š3îœ Ãˆ¨œÐAtò.¾è«6xh:¯D PÊ—ùs…-fÓðÇBE­áœ4ND„à»ø³ï3M ¥ïõP’vô|øù«Ì›À:6}çh¿ÛšknÚ´Ik]¯×@ÂnDív[)U«._jâBÂ0ž;À|“s3³–\õj­=;—$I%ŒVl¹Ìa¦š™ššÚðÐCÑQkíºàÌ2)\¨¯ÓéÔëõf³977W­VÃ0l·ÛÝn·R©t»]ÞjÒ4•%zá` ÈÕ2õŠÒDø«Ã_&�� !g]&T@Àe Žãn§™À322’¦njjŠ“f¹B”’D�¥eHEE€¿dümÄåeŠy.íÀ°Ü”Œ«”B¢7¡žRÃ_}þÍ^Q¦C䯥çP\战)E ×7G‡IGN¿÷?<ªû‡ô¸ÐïséóOÌ^ é‰OCÉÒß$ýu ö_Wo7O?ÿÉþôFÄœÜOÆ?;BŸS.n抔À%gJ„¥ç%ü“ûû»$_"¥–¿ð±þÑ!¨ÜÑ”‹`a1:@ޤ�8QŸ nç…‚û÷ Rê!Øâ=~oýÏ2—®ãWH­5Á“ÂOQI”l‰{ý/‚¢{¿oÌ$/3¢ß%âŒý€ó\ëˆb'"d¼—UÃf³Ùj6ã<÷[5ŒÂ0$›áû8ñ‚Šy¾Ø[˜\�ôCñ)+ñ:»ifšŸªÕjˆ˜$I·Û­VB œÓœØÉCåUÕ3\{Âÿ¦¹“³Î +öO¨oØ €û“:›¦)Ú”ó)20CDeLE¦!N ¬u’IäUîC!Ï¢øÀ ñ5ý]’@™Í2)þ�àôË?¨”b?ê[þžàËOI±(,-©%€…½*À"ÚÄq<Ûœé¶ÛHT«T� rNim´!€f³937ד¯M"d^Åq �\¾¥wë##ccczƒ¬å¢¤Õjµ16Ê,­ÕjÛl³M'îv:(â8ÖÆ@®VsyîLVÐcZ­–µvddDj0ü$Ϋz*†ŒÏù6èŠa\XDà›!ôtÖZN™"¼Íâ ˆ4*£t¥Rétb®â‰ˆ¬ ôµ3¥‰CÄ<³DùÛþîõϸ/¢!*ïBÔ9¹M„SË@Η› Ÿâ²…¨bƒƒÔ.à ¼ÿ'Ц’( P/ƒa Ã! iHCÒ†ô$&£µµÀ±Aá+9rùx�z‡<~@ñÃD)¢pÞ¿²Úé ÑB1°™Øg!±Y!=ñNÏO~�}�²ßÃ=#˜K²“¯sœWÌGÚü¢8M¡ˆ)·îÊa‘°9/q�zXÚ‹öõ²vÛž3¿ÿ¢þ”ìü§¼‚}n!wÆ–rwÒ¿Tñ¿èùØ+O7ƒˆ½>û0€±(x'ìv”0yƉ�±EÖZ'ÎZ‹–ˆâv§ÝnkιP›‰EcQ­ŠŽ:NÒÙ!ƒëeU±À¡L¨¤…€$Iâ8N’$IÓñÄZÊãD(Íæ4ÆHDÔiµ»Ý®RjldÔQŠŽ¬g3TAB¤,¹±‚‰²Œeèe  b¥FAA|Oš&“M/´ž§Œ‡–$ Åq§ÓQ�©sJ©°±o³½ßÙ ^åáRpNç¥ésg–¬zÉä1×k¥»ÞŠ~úâí "Z¯T¡/KÎK™; U7Mħ´â¤3¾[X•Ù%ò¬ûr"j@kmÇišVÂ(Žã@kI·Ûš›CD.OÐn·mØbŸ¦i…�t»ä!g2þ³£hÍ5 Q«µææ¦§§Ó8Y¶tKCà 333£££J©|P)µ`Á‚N«-ü1ÆT«U.09;;»Å[ÌÍÍMNNòö’¦iÇah(×y9 ®Ï*-°~ʇ¯ýð»DþCžƒÃåq€h”VF+¥ºÝ®µ.Š"£5'PGaXáu155ÅÅ1œ³ò€˜y”‰xû2ß/<¥Iw®°×QQq“-¢AÐó ²èœ$Fµòþ‚žÔO¯ k�”ò^Ytm+þË*€,7M&ö¹4R„„€C-Á†4¤! iHCz“s ZEG€ÌLZç´Ò*÷ë¦ÜDfÉå6R>j¥ ¢F¤$±JÖ€vâ8Pˆ Ñ¥iœº,y8p>BÝ;²5 ó%(Dä%£€”«¬# NÖZ#*®øMDÎ#^TJe• HkÍ6^†+rèÔ^ò9ñAð=r4‚ã¦.˜$‰uÖÀÌx@Å%Öª qñ1T¡ÊÖÖZç¬ÊÆ›†¥üá)f¯L%ÇÃf%È!‡rÕ—NBî±@„ gϲ‡U‚Ñ¥)»¹‹õ;PZ!¦ÝØ&)8—t»\}-Ð&e”±d@V¤¬³6q6q¡6©MЈ±MPªÓM´ÖA‘K-[Ý“$iµZ­fÇY¨Õjä°ÛI4àÄÄB‡XkÔÛÍÖ¦M›”Rõj¥iê¬kŒÖ«Õªs.Ö]—ZÎ]oÙ‹A!�r²·Ð•jU£š™›Õ¨”ÑÍfÓZ[¯×+a¥Z‹(µí¸Ûív«0E­Cy€wΔ"¢4‰È(ˆ.MŒ1i;ç´RJkDÖi¤À˜n»“@‡•3›6«(çl’¸Ü×£Ûí†aX­V»IJu“Ä3†„˜$ 뼸ív»Ñh4›M£t ³†Q»Û©T*Ö¢&Â$I1bU ÎQB¥,9rôžÉB粨ÿ €±84:wè† k‘rž®$¤´Ñ:µV)­QZ s• BÝí´ªÕªKR ˜™iµZµJevv¶ÅµQéÀhT•0�›¤@T*ä_€#Bàà忢Öè!4J©0ÐQ C£Pëûî¹›ý2âN§ZŒQív7ŽI)°6Q À:­ÑÝn7j�à,†\«cÆq<;;Ûh4XV óçg(‹*±�Œ �h¥3`*Y5r>}•]÷—7H”^ ÐaÐét8Û"_ÔJu;]¥Th€ˆ8ùÕ+Õ$‰kjÇ©MM¨kPAœt•QÖZ“ëXwÐívyO”4GPÔhˆŠ‡ÝpÄCÄOwêOI5Yè•o”eÅ{]©ýlõA®®Bp¹ÖÀõª½��9"’ pŽÄ�±Wø“…”[B�—:…J)´„@”'é!p¨² rHCÒ†4¤! éILÖ"b™!O=xÿ,޹÷l®Z DälÄF‰aЈN)öJ�9Ûqæ-€,#C°Z¡ÍÊdð¸?*¡dìò-«èK—üürƒ(ÄOž_€Øú$zÜ?"“¤Ósì(ÞLœ ßï$›µ(…°šoï�1—F'.Tø¯òp¹âs dÙƒž30@nÌnö˜€˜™÷{9YË`AU­FˆØmwš³s333àæÖÑjµh“$‰L­³ÖNOOãÜ,ë}`lu4.† Hq…ùn· �H™ûCãããN'Žã7ò„VÃH)§‰!uÖ„LÓ)'ŽcgÓ.áÿ„aˆZq7N8§”š%¢”lE /ÒZ·æšë'š­EöÓn·ÛüãÃÒ’9Q¨¹‡ z†t‘VÊhÍW b”‹™ñ\Ììà•ˆG/aG¹‹)IIá)1QIÅ>™}]ˆY(;Á«�8,à¡~õ“/nP~ûÒÙ³�8äAA®o Ã0MS$ ‚�‰Ò8¦ ¬„‘ô\AÏ_†¼¢¾Î‹ ÖùW|·DäT&�€DÕ¼A×$šœœœžž^´hÑÂ… ƒ h·Û³Í9FHÓ”#SX±Èmv:$I*•J«ÕŠã˜ýV´Ö¬?¾•–|y똇ö?Î*ÕJ§ÓaÝPEüFÃ0Û9›þ¨Z­æœk¶Ûi7nµZI’°ØËæã{»¤¹G•¸öˆ€•&ºÄÿÒèЋ¸Y1ÿ…DZäÏþýØçômn�@`è¥#ga3ƒò´Ö!»l UCÒ†4¤! éILJiT ØìÈÈP“# º¯""§4Ÿ÷伕ŸÏ€ØÄ _•R¨”–ÚUFK€79ç°â£a¶]B#ÒýòR9•J˜7deê{uõüCª<å7ÅÓ¹ /EÊZK@¨•ÅNøMÑã&�]‹ý617õËuÉA ”rÔK5‡À“ƒ¬¯ð©Ä üǹwŠ«Bïhœá1@Ô¨E‡Â¹ßÀºn·;;;›¦iÒÛíöØÈˆR*È'®R©°52MÓHWøYKÎZ‹ ŒÒ`VJ°@JkÔŠó±ûÕ!‡(l3g¼mŒ­Ò4¢ªÅŽKmâŸOD:0‘1Êè$IºíŽÖë&1$NJßívGFF@!Ç®c¢jell¬Ói+¥BI@žF©Ÿ”R È‘ƒž(2°DDGDi*ÖÚ¨Z´æ¨Š$IPëNA�låDDŽXHsgŽJà$yÚÃü�Àxs7¹.Š.ØH}Õa®—)–{BQ†j�Àò }«Ã‡Xþuå… ñ¶Â+Y!¶Z­ÈQ1:•¥ xiwŜԧ­(u ¢`½+\W*n=X¨:Nš¦I’ÌÌÌ4Û-Ddÿ#èK\">&%EË3J”z‚Ev•ú¬P ôj·žõd¯ˆãØZ†a¥R‘Œ‰üvÎbÈú—n·Ë*¶¸ÝQJ%IÂQ,Ÿì)Ã=”G¸pt†d¬ >=Q‰íù‡BºYÕ—]U„sA‘Ê2¾ ÀŸDij3À¼M²(´$_C®Äì—–¾‰05YãÃÜCÒ†4¤! éINÖ)¥! Ï"J µ)™zäÜßoÿ)AY´ô—%VCH`½ÖF*½¡k-Çúg‡c/Ý—YgKXÿ< þ9Ïó—›KÓ €<qwé°KDè2#'Cn½r÷ÎK°×ó8èó&ðÓ H=D¤¼FC‰{àå~óÓþ!XÞ"ÑþÐw˜VÅäsBýwf÷8×jµâv'Í«'Æ,\¸0кR©DAØívçææØ”Êyà•R 8<ÄYçÈ¥V 1hµÎiäœe£1[ì‘€]�ÐË\†!–j5Ûn™0��—Zà cL³Ù¬Õja½á WªÍƒ• #7yè#«Z­†:3¨€1fÑ’Å'b”Ũ¡[h¡ˆg˜QÜ8O¡H‹µ–à ! °iB6esûÍfSÄÆ‡4¾³ æI1Øê+öXöÂ0œ™™‘‹Å›æIçæ2—‚­[8/òYzª´ÐT^éÀ¿N¹ÇwÁ«Å‡‹ÅŸóWÌgÊÕ‚²:8E¶‘v¥”âüUF^1…|%f‹ˆ4ç#À<çe»ÝÞ°a‡ÏpîC~£òXʉ¢ˆòƒœ]Ò×HÊv4ß ~ØF?Bíc»ÿl§Ó1ÆT*ßa s½�� �IDAT“$Ù´iS­Vm6›³³³<)õz=Š¢n·Ë2Ì58x„aèç‰àü‘%î3³ÔUÿbiÈý{”Ü/Ò^R€–Ĭü—ßÛßŸÂ ÅÆNAÿcþˆ¨8íЛ`HCÒ†4¤!=‰‰ØÒŸý?��±}”R#0 ð@~nîGLr&ëlV!0¢S 5Òš›EgÓ<ÃH}lô|´ÖÎfV÷Ò±O,“ài4Ä+Û¿?ƒëE8íŸhWøpÝ h-þ ><ã~D Xq€ÑW« ÛS§¹0Fa†`ÕsÌè\­M;{8¢¼°yÖ*`Bi ÿpÌ<eߣ¹”˜×_cÌôôôÆ+•ÊÈȈAEŽ2hDÄØÔe½I’€ÂÔÙ8NYÓ£QÅp䈈�,¹´ÛuÝ…&HÓÔ(­�S²â´bÉ1†1Æ‚sΦ.ÀÀS*ÖÚ8µÎ9 °CA&ºLIÌþA`RkÉf°$޹‚€Î`T«�;“K)~5ÿÙq äØéÀ‹�…ˆ™“*DNuat­R «…FR‘ä à`r—ð“àm‚De j/:‚¹NÁÿ©*äY­8¸€°s}ÆRÁÕ½1"@V!¢œÖ¾$f¾°e‹�$=‹oáv2ߊ™™™n·E²‡Kú›L õÑ| Nr²ˆYÄxžíê)ÐÍ•D,8,H<GJ)­2ÞŠ \åAXar)D©/Hy LéáfÖææÉWçùÿrGš¦SSSµZa?ÅqÜn·“$±Ö6›Í$Iø[—¤I’0Ò4mµZ¬Y¾|9‡Þ4›M‰3âŒ%V“W±‚<Mþ¡ `’)óGíòP"’->— O- E=¦¼Tvæù~wæ#_ýñ'ä1¼eHCÒ†4¤! éoŒrGtèy'#(G”À‹Õt vÀ?á妘‚k´ tùPj¤„W}›<xFÅ ¢(Š¢H¬÷ò ËK”:ÞùRTyæB”†Pê$Ë®¹X¬Pè<Œ$Ñ¿>Ç|ú£“þc1$¡ÔùVØ»yò{Î÷3p^²´&lôY†a£ÑX°`Á‚ 8Z{®ÕœžžNÓtzzzÆ Íf“ÙhÉu“8ŽãÄZ˜J­:666±pÁøøxct�¥uµZm4Õz-ªV¹è½ÉbŒaô…¹G@¥R™˜˜àœmÝn·E•0Œæ¡bŸ�`“éÆgff:qWd†°e˜yÂa­u«Õš››ã|o\÷¾5×ìt:DÄ}��?rŠxØg—Ì 'hÇ]A%ªÔkF£6Òˆ*æ�û2°ùÐ=Ó·È˃ïÞâ#1̽ ªÕªòB÷ef±WONVuŸ„ø@ÔïFéO_`äÙÒ‡~Ù“eËÑ+"ê�ÀÕ؆Ïý—2à«¢üÎûÓQZþìHÈ]NØ=„µ?>ó)ÏŒ(¥4!wÉ'QÇð¼pôJ«Õâü—ümiíø\-]÷»=Jû¤?RDŒã˜k1"b§ÓÙ´i{ðŸJ©F£ˆ333ιf³Çq½^ÑZW«ÕåË—‡aA½^o4µZ­R©”xÞ~UH¿ä0‰~ªt]¾’áËòñ_êoæþ[úuèoÜ'D„¾{°¸ßü …ð&9ßK†ôWEvæŽïÛ@�@ÍÙ;ošîüÅ&6½ùüOžò“{çb{4D“¿:ígý±õx´5˜ºëoüý ë¾³ó÷$]{ÿTògèÚÃÓ¼¬¦Î†{œûë^ÛéÔm—_s_÷±<êfî¾êª;þÏÆÿüº!ý u§˜lÿ¥{1/õ¶Ç'¸?.?7 ´†ÞÁ•RZ+T€€äƒ]æû(øÁ!8 Äÿ9RZñ¾âÿG �5×D0h)ç Il’Ø4u”¥Äù$ÀD¹[²ËùÖ”—ï1îßï]¥åÒ×?£‘@­u8)NæÆa~µFTù«7RÔ°0ß&!ñ¾¢Ä?%—øàîJ4½;)Ocè"TxêÆq<::ºlùò%K–ŒŽŽŽ/^¼x‹-—š0@­�!&Ö6ÛíV§ã�kAaE•z­R¯…ÕŠ §ÐeåL0zd¸Î@†&Ð!6³7 ÆÀÒ¦&7<´nýÔä†n»ŽÀ"FQDÖuZmÎ9_«Õ‚(’<mÅÍà°Z­6jõZ¥Š.µD”$ÉôÆM­¹&8RJ%±R Bã,Ø”œ 5ßšˆKZ €BÔ M½6R©Ö£J-ªÔ‚°‚&°âĶ:m¤ŒF­P«l°Iì€P+e4($ä€Rg1wŸá8ö°ðçˆÓéAkd&Ê+ØA˜¯•9; $_SPl0H°y [º­$½ ­1Iî» \Ô%oœG1áwI’˜Šä°Ë=ó*¯‚aµÖ,.//ByN^¼’ÓD„‰Õ™CJ®Á)9€È(ú‡ßÏ¢þQô_TJ1+¤\K³Ùœžžf÷ÖŸª¼xj·Ûå$ ²§1Ø¡Óé4›M©‰ˆÌy‘Ê“§n^1E•‡<N}šPq‘áøÊÊ~}DÿŒû¯€¾]±$�PéÒƒó mI³0t&øÛ tý÷޹àÿ]ã�`?úá›7ü¥ÎsÝ_áÝŸûc2QÎú0DI§ÛwÔs÷Ÿ÷‘÷~ÿzôxu®’+O:ðùÿíâüy{âîú‹w8ü¬Ÿc꣣yY_tÌ.{~èwÝÅãDöö/¿zÿ÷þhÓccš:ïèç¼ñÛ÷üÍÉÿñë†ô7DÝó_»Ís>yÍu¥ö¶Ç?§Ó†ó^³ÕÖG^øXÖzÞÄÔãÓ“:Î^š@“ŸÀN諘}¬Ìèÿòs›”L—_ÿy)›a9‘ŸÎù ïÛúØ ž@ŽøÒÉö–!”NœÒëU­—¤hbW”AAŸ©ßåáÍrH/TÛg‹Œ‚½… Ò¹§ÄIßæVâxÇhÿ6±ú£vymyì;+Èà­µívÛ%i­V«×ëZëñññ… n±Å .dè†áø‚‰-¶\ºpÉâÚHCk'Iœ?Q¨Œv9^µ¹V$G Jš¦ `Â0Œ¢¨ÝnONNÎÌÌ b½^ Ãó9¸š±7†áèèh£ÑÚuqw:f/» ¸<sÞÓét8!¼€FvA—˜óþ•Ã><ˆU+ãããK–,Á¼ÆgâlJ SŸ1Š#¢V«Å‘ð¾ÂËGe"Š áÁ²3ˆ¿¾¸>¢ÈJž­±ÖÀÇ~>Êê_¿XTÀ ¼Ú¿}xâÝÀñ)²”(G‰,rìhÀ‘ö\¬T„Š>8e¶»áÿ)ËM¼6d9³½]dÿi·ÛQq•ð�O½x»ÈÚ”…fŒ©ÕjìÁþþ -u©Äð~®úTb¾òˆe[­ÖÔÔT§Ó©Õj¼ç°çE§Ó™™™±Ö.Z´hÉ’%«W¯Þf›mFGGkµÚ¢E‹FGGY—$Éììì† Ö­[733㋚µ!LˆeÍÅæ‡Ð/<Ü U'òg¿û�öi Jí÷‹œ/¥ý¼$-ÐGÙ{³œŒå¥)¥÷ÿì¤Ù{õ‚jelÅÿ|â%“¹Jï® Þõ¼í&ªõ-vûçOýfÊAó†Ó^¾õ‚Wÿ?±&w}︗켤5–ýÝË?öó]·îç>ð©‹kµ…;¾è?^[¼ÁÞý­ÃVFÛ¼ãÒþVûƯ½iï­G«#+ö<òË×4åêYoßoûÕÊØÊ¿?âÔ˧Iâ;Ï=ê©ã/øÊzþnÓ׬¦¤zÐYÓ}Ùu½÷Y Ÿù‰›$Lªyý™G?gÛ‰jubõsŽþÚ MáýO1ÑCÿûÖ*#‡_𘬨O�jÿêœ ã¶_ãÑ<äîûÒó—<ÿKå3¨»÷‚s.Ú¡/ßN?ž=|,ôÄé‰G‰ÕCú[¥Á[GqŸLnþö1/ÚmùH.ßýà_ôàPµñ—¦¡?àÈ./=ꨗ<µþ·YX›f‡´^²v`cmæÌ̇`$búNÓ4Iºˆ†&´si’tK•"‹H¬^p.µ6q.E$FV|°vι$M»1:B—³ì€@±íYÑóƧbÁmð»h4üúd˜§ ”?å+AÑ$År­G¦Å�Jeƒ0[Ô³mHp›œó“cžéPúìëDüþ÷˜WΓ󴀜  È:®;¨Q)@p„ü¯4JËçR�Çÿñ¼(rBçR"Ëב’S@JRêè´Û•JŇÖιññq¤SkÔ£je|Á)L\Ž•€MDívriIÕ.šUþ ŒbØ_©T:ΆSs­&(4aP©Uë#ñññZ­hc“´Ýí0Kmœp±@.h·qãF—¦ÕjµZ­rö»8Ž5 RJÔ ”×¶�IYÇaÛ2w,f,¥\-Žåül»Ýn·ÛÊèF£161>:>VÑAÀbcg»qœ¤)OžuN‡âüyÌæjVh3ŸVö gÐyþŽÛã˜aUµZ…bÕj5Ÿg)(ÅÈW=ו\­¦Ö1š‹êy¾-™CwvòÈ—[åÅ&øÊ>cQ‚Èòäx——Þäo]g!µ„!>bàz?¢÷Þg\x/CówÌÕ"ü"IÉš&Ù ø-¢?âýUl¼(¸<!· ªUé­ó— »Pî9bÉYrü™/_á¯,¹Ä¦F‡IlÓÄÙ”’Ø*4‰Ú­.rt`¢Zµ±há’Å‹¶¯×ë¼OÖjµ±±1D\·nݺuëÖ®]Ûl6‰ˆ}vx:$„G4¶Â,*K[¥ÏðT~ÇhˆÌø¿:¬†`æªÍ‹¿ˆÀÈý*Ϥàïüè‡ð *­•½"ê3_÷*ÃAD­•êUÙ�D “ĉ³û C>,Ñô5]³èõ_¾ôúk~xüê?|èˆ~Ù�{ó)¯yÝ‹»èÆ«¿uÈ䉇sÁ éë¾ýþƒž±ßq?ÔÃÄîöÿ~Ý.ÿÈoÖn¸í»¯š=åÕïþ^ »µg½ù•_J_þu×ÿàmá×ýWîîñ¶uùG}߯“`À¹¢óûúößíöéß®¹ì {]ûîC?pI�Ò+?~Ø¿^þ¬/^½þ¡ëNÿÇ[Ž?üÿ‰ý§Z·}ÿã‡ï¹×[ο·wÈÕÙndšZsÆ!˶:øðýFü‡’û~ñ¹7î»Ç«¿r³ç‹ÿþ#ÿüŽË÷øüU®¿îŒýn|÷+Nø]ò|*ûjÍ^ýæ §+ñãÒc¦æ/Ïþ>¼ô°çÔÝc.î¦}ÇewçwϹêï=d›G-Ÿ7=qzâÑcdõþ&iðÖQÞ'Ͳ§xÌé—¬¹ëæKOÙ÷®“Þ|ÒÃûÑ iHn w<ìC|ùöÁ_º||E@¥°w~�Xv7EÏDÆxFŽqü'Ÿû±Ï¸çŸ5Á ræ Þ9pSQI¸¬¯àCç|#)Äú²Ÿ\åèéÛoɳÊú¦]A ”'¢g_6>Ä*Lò"L¢æð#¥­W„Ï× d>å‚?pyª„ßJW”—†�¼“ýÀÖJèÈŸ}c ö@§Ó™œœœšÞ422ÂùáPæ+M»Ýn«Õ€Zu“¸Õj±u—-±I’ ¢d_«×댜ëõúÄÄÄøøxµZuεZ­7r¬µsŽÓ­™<CsÎ&iš¦dgk›žž&"$`Ï@‘¢~´3ß ‘:ÜUcÌÂ… ë#¨ZQFsü…Ê2ûM(­ÙÍ›1¿G%Ù"²•zHžJ óŠãœ‹"oÜCÒ]ÑcÅGÂÌ.†Áäy •RøËúò—¶K*&¼(=EyþZ­�¾ß¾¿Hûù_ºÞ/±b‘ŽXÅUŸEÉq©D~Ër›¨9|ìwxàãÂÒNÒÿ¯ÿgé6ÙúûÉÿ²› äå3³ŒžÆpÕƒ7NNNNMMqTŽäv‘ùòÓ.PîV€¹…x뀷Ë"*m2ý²¡<¤íóÍ—UŸ“|´æ³]¯ú9쿽4²4 ¨©]ƒµŽsÊ" ¹4µ¨” Ìc°gàÂô­Ï¾a¿]Vï°ÏQo?hÙC·Þ¶‰ ½þÛ_ÿãîÇžxä3Vm¿ÿ¿Ÿpxð½¯þ`’�ìýw4÷ù¯_|ëu[ö¸”ÞzÝMÕ½yÉöã¥{~ÐÎswܱ¡ð“çî¿àÌŸmñ†»ïêm÷zëÇ޴ͯ¾zaî×'¼ùÜÝ?ÿ_/íïZüÛoœuÿÇ}ìå»lýÔƒ>òþM~ëÌ_¶š7]wç²ý_ñÜ•##+ÿñ•Ïßzýíw¬ü¸þöõ;÷¿?x×N¦w1¨«+>ûÑ_ïù‰ÏºL½í ÿ´tëùî$nºýÞÅo<ïÿùìPž±w]òË{v>üm/\56²â9ï~ÇóÖ÷+ZÎá[mù¢ÿ¹ÃÁÀ§¸ë×}öM'~øÔ#–? M.ûÕ¿ïÿ¥×=ãÔ£^tÞW¼ÉÎ'mZûÃãÎyû³O}ís¾þ‰“n^ÛÚtÓ§ž}Æ×þà��â{Ï8à´/^’�¤kÏzñÿ|þ¢xþÆÍ^|öƒÛ§ @“¿þô«÷\9Z©.Úá€w|kM�€úþ¿í»ÓòñjÖ·xêóùúu¢*‰uì*ˆÕCÏcµŠ½õ¼s®{Öa/ÛJ@ëÆ³Ž9`‡EÕ ¬/Üz·çŸxY�Þ÷¿'¼l÷eJ}é®}àw3æ¡Ù?~åÍÏ]½ Z_µïQ_º2w‰ïúÞñîºe= ª+v~îq?-{”´oúÖ;žÿ´å£µñUÿðÖoÝ,ÚBOæ®;ómû?eQ­R_¼Ó‘ç<H�\ó©ýVŒVª ¶}Nþ˜½á´Wïõ”-F¢°¶ä€Sn±»J]xì>;­XP ƒêâ~ïGÿí §¯«Öíð¢~þwέÿåI¯Úkõ¢zc鮇ü§¸íX víÏ>vè+F*Õ±­öýøå �ÐÆ ß¼ãÂZÔØr·ü1šúÉñÏß}ëÕ0Ýö­?nÏèÎ%'ì÷´m5¢ šØî€c>rÜa{m» V_õÜc/¸Çn–QÒkNyÙÓ·]Òˆ‚Êø6{¾ê­-çSîüüÝ»-›¨E•‰mÿámgß–@ò»÷l_ßÿ‹÷;��zèÌ—ŒîtÜófaÎÈ­ûʼn¯|æV£QPÛr‡gýûÌ/-[¾ó®ì´¸VYñÌWžôËõ¼;Ѧ+þûõûl;Q ¢‘%«ŸùÊÿ)»Ar÷÷?x­'j#+žuįš-ÉX÷ö þý¥O[Z¯Ô¬>è‹7Ûy^”^}òÁO_½Åh%GWîó¦>ôºçî°¤^Y±çëϼ© þ.0ßÖÑ¿OâÈ.Ï{Á3¶Ûr¢Q1#«·_úDR|ý­Qóì—ì|ü•)�€»ç”çŒüÃÉw9�š½ú+oÜwÕx¥2±ís?qY¦¨yøUð禾U6/õïí´áëŽn}Ì% �@÷çG¯XòÚï·�’_ÿÛê…¯<oƼuÝé¯ßkåXÖmÿšo®uÿôM[.{óEñ#Ø|6ó ò8êAîb� äPETÉç?1A®&`Å“¢Pv0̰µÖl�7JkÌp…JÉ9/¥?ë¦æ Ñ/s”îQ›µùàòƒ¾ÖYT¹29Â7u¡9k­ÙDHÈö}bŸƒ„ÿ”¯ä? ¨Ø·) ÈÿTQjÑX‡Å´äþ±»ùˆú�À:nDø÷°‚¿L½sŽíÉÛ™-‰Â”\X­,Úb cq2wyp «X­P©T¢(‚<A æ®”û_T*•ÆèH¥VeÓb'Ž;qÜM’8MÙ#¬Dìw.ëg¨3nEõz}¤ÞhÔêÕ¨"™Û‘y ¤d' _Iƒ0Ë6ã[ïN§ãœ«ÖkQµ¢Œq�©³=«o¤"ËD@hct€V)ß Zñм)¹”zŽñÒC™vCPJU«U„ìkà÷Ù—FRH*‡gØËhhP)*˜d‘£$zkDeI  @<\ç¯)—(‰¢ôŸE‘U‚ýæC€¥‹›ù³Eƒ§âÜ„%€ Eœß¯ƒ«<oOÛüÛ…-¥”Z+M"zjŽÒW¬ŠÂ<fgrrríÚµ÷ÜsϺuëÖ¯_¿nݺxàÁœššbMœ¯Bù—æ‘rͬÝ_/6¯Ižš†òÈ‘~EL)xJvfå)§ýþøs!;¹ìy5ðŠü$åÝ.4[ܯ²{”B¿‡‰:×^tÉÆ¿;`ßEí›®»cb—]Xî¼ûSí×Þ’.xÁñ'¿ç%Û7|™žñ¢Òçä?¾wë¦~þéÓoßï %SmºæÚ›`§Ýv4��zûÝw‰Ö\{c �ÐúíGÿõ‡ûœòÉ.ðÛæ¼þú©•OÛy�ê»ì¾ÝÜ ×ÝcadŸï»þÌü×Å÷NßyþgÎn¾â /óŸ«>ûØ“ÿãÐ]'Ü_ûÏsÆŽþàaK�Ôèª=öÚk×eU³ËQŸþÄ‘{.ñ4  ·ÜiÇ‘5?<ç²u´»é¬»çþ«+v}ö^OßfaàS�ÜxòÛ¾¼üã§¾zåÃ:¶›žö¶3øï‹_ûî—áï>þÛËgæ¹ÏÍ^òÁ|ÿ¡mŽ:ëuŸþâ³–\qñ§O¾?]¾óSÚ·];í�ì÷ß²¡{ǵSÀÝÿÀ-—ìüwá<m="š¾èì×>ìÙp÷~õÈ—ôÀ‹N»ì¶5?ùmñ–ƒŽ»¤ @Í;þpUýµÿïÆ;o¿ê¼w.»ø­/|ë…Ò öþÔÍÍv»½é›‡D��v͹߹y¯C^®€6ýøØ}ɶüß›î½ûêSŸ3õûk×:€ô†ÏúÊ3àȳ¯¾íÚï]ùöá¯8éšhêÇôî«v?é7ßòëÏ=ûÆ÷ø¶ &  sÙ‡<üìÚÑç^{÷½7~ûŸñWÝÕ)t¾uéñ/;ú’§|ô·ßyÉ +ú–£¾p+CÀBO~tìK޽|çüø†;Öüêï}î�½õËO¹øÆ;oüá;'.|ËÛ¿z¯°÷ÿáâÛvýÄî¾ÿöß}ùU«hpW›w]uõÈ~tÛý÷\uÚsn=ùSWïñ©ŸÞpÛ•_ÞÿîO¾ý³W¦�îîÓ_ûòSfûæµw_ÖÁëÿë5Çýx®Ìj°k>wØ+¾¿úkWÜvûÕ?<õð§�Àú³ÞyÞ•·Ý~Ùÿìw÷Ç_÷‘K»�4³æW¿Ö‡Ÿó}÷ÞpÑñ{Gó0*½ÿšË;/ùúšûî»ñÜÃæÎøøO—áÕ·]wÞáÝ3ÞöÑŸµ7èÇHnÝõ¿¿oÿpÛ]7ýò”N~þ—}òÚ"F‰vËY¿Z³öÁ[¿÷sö[ßþFóô¿pñ•ÿûó �­ß]|ùØs÷ßÕ n>#{ó)‡¾ì”éCθüŽ{o¹è­‹¯¹üÖšWZÚ¿ùàÁoøÑÒ÷þð†Ûþpæ!›N~ù_¾Ó»ÿ›¯éoÞû3—Üzï—}h§[/»ñ¡âæ™\÷_‡¾æœÆ1ß_s÷•§îyÍûþåWøCéüþ„ƒ^{áÄ1ß½öŽ[.;÷ã/]¥¿Üú.»ïYŸ¹öžûoûÉ;F.øÄ·ñ¨o]~ëÍ?}çÄ÷}ÿ¹“äíBómóí“Ýó_5Z[üÔ—gË|æÈ'TÍ“ƒföÞ—½ó²§~ôgkn¿òœc÷ËQÜî‚?7õ­²yð÷ ½½5±ï~»Mþþw·[€tͯ~»nã¿¿1°wþö·í±ßÞžm8ïøûÕ.'_39óàuß;ဂ¦êa7ŸÍü‚<ÄYíòdmL„Ð3•— xyÂúƒ‚ü31z6¢ÒW¥\è‚6ý;Aœæ‡÷Ý ÈCÑü.ö‡GDߨÈ7û§ùÒá• K¿ž³+£Óþs*äuÝ|.·ì{Ïr¹2—gXèáÉ"Ï>¶é·àQÑæï?%wúÚ™¾vrù¦okmâ,)NrÁ’%KjµÃ~¿qT*Š¢Z­&nä\ž�“$i6›­¹¦RŠC3� Z­FQÄê!ŸžmÎuâ®sŽÎeÑa ³@DI’¸$uιԒí¡áŒ/’ÉB˜ãËgÿ<n—æyÙ!?%g=‡á¼ðD+t”&BLßÙ³ K/užD³ã'«§Œ‚ƒŒÊÖ¬ïA ~ò<ÊOvXLù6pø‰×o¯ç¹8ù^%´Üív±Ñh§¢ÈäÂÜ¿±ø»“tC>“wý%£ò|øþ%óâ3¡ôR¿Û¥e[b¦¿/ýëT¦¿¯úo—²MùWDÂëõúØØØ‚ &&&ÆÆÆFFFG"H;Ôç P‹?kþ¸ü}¸¿Û"«ò¯¤¨öõ&PÌIá÷É—™>éX·Üí"ÇD8{¬ÎUTRbz³“9Ž¥ÉŸb³Hîüö_õ•Å9íí;j Ö윫7ÜslŒÔanf>U?.:è#'ìuÛÿ}ÀSV¿ðKé!oéJE“g¼¸ÎyX÷<qÍÌl³2RÏŽþX©Å³s1@zã©ïùîÓ>ú¡ýÆd¢’ß¾{ûÈc‚‰#¾75Û„úH¡³sj›×žø®U—}êµûn»Óá.yÝ[ž»ð‘zöw"Aå�� �IDATwêç¯ûÇw½mwVRâ’ýçg¿g¯ú<·¼ä¤oÓøöÁÛŽ6¶xÚ»xƒ©V4Œìýïçœÿ±ç/šï¥î¾oüûëï9ñļ‡ãã[mU«ŽìpèNÛ%×ÎaLkoýååõÞóÌݶm,zꎯù×ÕÍÞpC<²Ë^÷_~ß ÑÆ+ïïl· uå}Ì^yïýÛm½ó‚‡û¼D|öOÇ9lÏÜ]çžqñ²·~öøì´bë=^ýé½lÓ7ϸ˜ý7T}é¶[/ßj‡}ßøß'¡/øò÷ø”‡Ê„•J¥ � ½þ;çÞ±Ïa.E M?:íœø•'}æ5ÏÜfé–Ûl½˜ó¦<ë«×<ý}§¼ý¶[±zï·œòþ}Öœù+â?:ý»pø‰'¾b·•+w9裟:²váéß›¤î¥§õžçpê[öY½åÒ•«–öÅÁv.ùÚ7׿ð?ø)‹·Øí5ÿþÚÕüñÅQ¹'?üò¹ðê“>ùÊg¬Z¶ÕŽÏØi � ÆWíò”[®ÚëÍÿúÒÆ~{uvº®\µtñ–«·YŒ»š��`uá²¥‹·Üé¥G½l;[ßúïvX±|§—yàÖ÷\{ý ¹;¿{æ¥[½ù?ßþ÷Ëmó¼÷½óŸæ.úñUI‘Õ^ýÓ¯Úã}§»ÿŽ+–­Úu·U¬ ·Üq—m–­Øù%ï|Ý3ºì÷w1’W-·]±dÉV«—76 f��FK—.Þbõ~ozåÓƒhÙŽ;¯\¶Ý~GºëÌu×ÞgçeÔŸBfÁVÛnµ|ÕîþÇxæõg|½�®n÷´Õ[ŒOlµÏÑGü}|Ëš{môìW¾bÉ¥çýè!‚øª‹­Ÿ÷â½6Ÿç2½úë_¾b—wá}ì¸|éVÛ.U��4´t.ùê7|á Ÿ;òY«Vìð¼÷žüŽí~uÆwnKî9ï´Ÿ,xÓç>vð®[-]¾zå‚>Œþñ›_¿þ™ï:é_vÝbñSzÿÑϺû§?½¥§Cé^rÆ™k_øáÏ¿qïÕËVlÿô]V„ƒ_ÄO˜Ñ%Ë–,Y¹×›ŽØ7‚ÅÛï¶Í²­÷zãkž^Ím©· Þ:ì“E‡|kjÓ}øÆË×}ðÀc~4ÒŸ‰:—~ó¼™—|èS¯yÆ6Ë·}æÁûïèi·6¿ þÜÔ¿ÊÞ6xoï¬Üÿ€§Ütñ%ëÉÝÉ¥s;?uúÒKîq4yéÅ7íòüÔ_íý²ß¬FF¢·]}ë”kl¹ÓvKÊ?x›Ý|�`Þ_?Q¯s ¼PÞšBFª¨(”¸}6<JÎv±F‚B–б]= ’G‡Š,Û»R  rxoCù1Qt~��7Bñ< ùA–-Û’¥Ì‡Çþ9Mú'õŒ9r„Å,‡<ŸAý£-q¾�ì3 K”±»¨‘Ö$C€?X¹Y¼¬±ˆôäAvLÄ8×ã€ÏÖª€g<Dˆ“¤ÇÖ¹N·;;7g×yÍ9¥”‹ºµ–‹Y@»ÙjÎÎrº8a² ç\dz³³­V+ÉsÑGµjX‰t`\Þ8ŸT)—¤f«9;×iµÓ8áñ¢§Ÿ ”ŽL SÆ3EÖ±g€âjü‹�¨4uˆZë�@)DÕš2A§›¤Žµ2¡6!(¨‘³V„DÆ„JP5‡ÿ‡""�¥µ ¥5Xç¬s€Eq’µÆ’/Vq"bDdM3Še²G{~ ]>ùÒ¾R*PÿŸ½/³£¨ú>§ªû.sg²’�!’ A@YDAö\^@EÔWA‘D?QAA0Èæ†ÈŽ, aG !!É„„$d’YîíîªóýqºªOwßI¢"ÞóÌ3ϾÝÕU§NÕœßÙJ˜¥½¨8ZÄ‹§2’äß9ë†8�y WJñ–aŽ?^kÍEõ$®+|†<ÙfAþýróX¢0 J?6â° H¢Sa,{[Xtõ칇%’ßäLÃG ˜msÝÝÝ\–\¥R®ÍÁI@¾5þŠD¦9ˆŠ�à@»´Ãêv%Qý·¹mS’¼B%½g‹Ñjþ"ˆÐ€{3ù,Uy·©7\œVºR ƒ0dñ0ÆYD‚@Z¯Í£Ͼò¸½>5ï×ýê”-«�€]=Ýj ¿?Îí_5�Ý#º‡‘Žä‰o¾ÿËCgÞ7{îÂy÷œ?ù×G}ñ}Èš5kÖ¬Y½êÓGô4šý©¦B«+ÝÝZvÃ×~€ýâo‡ÛžqËófÍš5ë¾óÞ5º§«d'zºïùâ±—¬Ñ#/Ìí}þŸ .:ð¤k×Ú ÝõókûÞ{ÜãÖÖ¬ ÖÛûì_<5W<÷½}Á´éÓVï瀡{¾ñõ?ûøIkl_¾ýÞoù““ßñƒ“¹ç™˜ì0±/÷¿#ÆOHÛ 'ŒÝê_¾ 'ì²ñØ'ç=Ù7ôä_VÍ8é­[,|ñ‰åͧî]¼Þ.“Æý1È´ì–«owèo Lï‚^˜4uR:ðÚÆSÖZ¸°Xɺ:mómï‚vVŽäÑ_]7·£ö�vñ¼ùɤͧÕó·˜Þù‹º&OY7µž2yÔâù‹â%óš §nœZdƒIS7¢Þ/™•óç÷­»Ùf£†›EZÕÛÛ×÷ëcÇ×jµZ­ñÖsŸ4+–/·Åž¼¸ ™8eÒp!jôØQñÀ@ÉòÖ¾«9-\5‡��°gd 5Á,Z°¨õĹ;4jµZ­6ö˜™ý+–-·yVƒY0¯·kÒä’ºõi4ô…ĶgTn.¸Sƒ�Ø3¢šƒCÃ2êõ!µî¦ÓF¼4¿W2'ž{ÃYl·ñ¸‘=cg|öîÈ PÙñ¸c'ßù³ëæ·ûÝ­ƒ{¸Ûpf»”Ì‚y ›n>1Ï¥a˜`V-\¸jü”ÉiqH=iêÆ¸hAo¼`ÞØdújr¨Í¢‹šwœ6µ«V«Õê“O»+æ c¢• æ¯?y㬫ÔþEynVGŒ¬5y«#F„Í!y€^Û­£í>é +#6Øîè Ï9°ÿº_ÜñÆ=ŒïMA¥ÀU‹^7iÃzÛ»µ[ÿlj·ÊÚÑ0{»š¾ï{7úëþ¼ìå?ßúâ^ÿû¹w½pëm‹_¹ý÷Ýdß÷L+ÿ³>ðÛ¿9{³»?ºÍÄ-ö?kæóÃ¥,µÛ|Š÷¬æ?Èk"§ !rÿ&ä|5^MôÞö‚@ï¶±º`~¯ÛI]°¬"§ŸUîf‹Õ´yä&(wÃ÷\¢©æ ¡±'VÖв¢pºïpÁ¾ ñŒRÊZû2]Ö劗%•ï”*À'9@i¹(|UPî%Ü’9çÕ³ŠÏ‰�ÀÏ@Ì~†­VË×hô|ˆmZ Òc¢8jš8©Uª=F£^1b�pοRªÕj±ù€M9\Ÿ¢V«Õ+Õ0 C(¥ˆ“ÿÙñ¨Sü–3OXK‰Q²”Ð"%V΋çƒ÷jJáôE%Sq¡xcŒ!Ë1ü2P$Ó(Ø�¸Ã"Äcänû÷àŸŸ#/'ÆùÁCõü9!]×Dî\ ‘,žù×Àvo…{Êö‚'ùE®{1ö70üIl’²ZÚÂÒà.üí—‚°£ù²8_ù)¹{øW Øýl>v½°Û”Iv¸ÜO(ÂüÛåWrEû6e}“(ŠøD˜(ŠXƼûe"‘¹ç€o­°YIù”T?(rñþ€ðÿ{~ʤ†g8‡ˆ÷ÿ•_€^P¡IIÀb¥ƒ€Ê‚ à C­4�¸&Kõz½«««¾†@sZqû™zî±3oúâ;ôªOßjÊ+O<ÁgÃEOÎzZo±Õ¦í²yö†_=ºÅáGO¯A8îmÿÜ‘ëÜÿçõ¨I[̘1cÆŒ-§®ÓØ|ëéðÔcO'��æ¹GŸhm¾õô ºÿ¦ß½xïg6±ºÏͽh÷u¿Y¯·é–3f̘±åô‰#ן1cÌ‹?É)ÁO̚ݽåVéøo×]7ÿmG¶Q�Õ‰{žuú¾ñ]·ÍZ+WMü×[þ8°ûïêYó­EJæ\qáÕK¶9ìÍÖd‡‰½é–çŸøúÛjˆ¾õ¼§ú~yÈÈý._^”=¦ÙWùi}øßºûã—ÎÜu‹á1‹×=šV.v #î]ñJµ1ºôæSߺNïßþôüó×ßöí“¶™±tÖŸžÿë#=oÝcÌ?b%Xró5wlpØáÛ� 'L\Ÿæ>?/Ì͹³Õ'L(bt³ðÅ^·Þ8:(jE~Àñ_¯¹nñGî;@7^Z°¨€¾õ„‰ë ¾0;="ƒ^™óŠu'®ŽŸ8AÏŸ=7­±Ì=ן¸®nŒ[]¶°7Ÿg »ÇïYçý¿^ÞdjECžµ….ôdõÆãü9óVSÀ¡­¢}WóRÁš)/hDä=füzã«;|íñ×§xÅ•Uó¬5~½qƒsç,NUæ•dIµgT^Ü¿ ×C�€aõ:õ-XпÎzãEƒ+oøÜ‰W'ßüܲUKŸ¸pwég|ø;Ϻô’_\wsÿïß{MëS7f¨··à<† ºg„î%³_Hc¡Ì¼ç_ õ'®Œ7–z,¼©ñëo¼çÒCŽ7­ÙßÚÕ¯Pì^wÝ®%sæöûû±ý‹ Ëå 5þ3Àp[ÇKi·O–Q{íÐëAA£¾òò²œ¸`Ï„ Ý‹ŸŸ½rµFê6«àŸMíWYno¶=äÀõï¹ñ׿¾ùÉ·¿wïw¿g‡Ç~;óºîÙè ƒ6×ò?{Ô¸wüÏÿüìì?þOø‹œxñœav®6›O‘iÙׇ’8)máÎùç5HÊ{�ààpÈ0~Lª}Rï÷êcÁ¬ÀˆBÂK©¹z½Œ $U|I^¿g4Òjµ¸óeRù¸�Áe±úzñŒÖŒ8æ-7^ pÜ,* štA»•JpAÙ•ïj‹y Ä×}{ÌŸ³Ø¶ŸJ)T |)rw$;wƒ ¡ññ„ AÙŽàýŒc¹¦:�pyD¬V«|Z�JK/ßÙÕÕU­VŠ�œWÐZk! Ñçfù˜Ì$ŽM’X_Ÿr`ÿÁÏ…Ûáó•HñI)i 4Ãq4„¹ÒnÙ4QúCÂÓ.ñÇkXWÛRâC ð¼StÅŠq õõõ ²‹xÈÒ)!¥A$—+‘õkMBS/Zi#|ŽF;˜ŠÂß+e²,Z¾?QÕëu^DF£V«ù—™_Þ7È¥ÆÈE¥…�yK_aSò‹ÚÏ;–ì†Þ„'÷·òr.ï…7‡vc”_  v)Š~\2»žÃv8=ÇwXnS(ÏvCÏÃ3ýÅ(Ÿ­PØvÊ»Œ2¬S"Â7ˆÎ0ç-M…©ôT0[´Ý�ýº“‚A Ód(KƒÔ´T*![(Úî¥k óÄwOÿɨÏ^vævÕV³Ùl¶b Ì8ú¸·ÌúÎç¯xxîó·_xÎ/£>¸ÿ0QöjÂôͳ®ýñ/®\þÔõ—ÿ~ÑÔ›×rwlpð {-¾ìKß½wÎ üðK—ÌÝõ„#¦êê~—/wö¯Ö>²þƧޱøŠ÷ÉpãÊ;>pì„[ÿï¯òŧo:ûÿ~;æèv¯ƒÞxú¦xß/~úÐK‹þÙÏïŠ6ßj“5úøÀ̹ï/Kfì¼S†BhÉï¾pè1ß|``Øglkåâçï»æœC÷þÔC3¾üýS§k€þû¾~Ôa_ºuY{V‡o»ð9W›5þëç·yô̾[N3l†‚�Š[Iâµ| ªU»ø™¥ƒÂZ�//›»ØÀ„i»ï8ð§o<ôØ ýKŸþû•ߛӵï–[Ö�ô¸·íÝxì»÷ÏÞvê–ÚÖ{®÷ìÅ÷=¶Þ´§üÝxõÝv8gˆ«;qEúkxfÁ¼‡¯:ý‹¿ỷ{pM~óâ½·Üýô‹óŸúÝ×>Å+{³ßxTc'Oî~æ·×=0¯wÎ#ÎkF\3ó•½z/s�ÇísØn+~þùsnùû¢EÏýåî'9*!xËû?¸õß¾~ê÷îžÝûÂ}—œvÞ=›pÜ•Ñû}èºêsgýú±ùóŸ¼ñKŸýéÐ'î?뻾õ†sμæÑ…/ÍýÛ]¼ÄÊ;v5êñó³[‘ÔwÿÀÝ3ÿ÷ôŸßÿü¢%/Í}ü¡g–@¡'c÷=þ}ñ•Ÿ=ãÌéíóج9«×öµïêš {멇¼§g.úÔ7~ÿÄüÅK>ûУ/FEVC¸ý1ïßìÁó?þÍ?>5Ñügzjí|k8 £Öø`{FýcDýÿé÷ÏYð—ŸqÁ8jÇ»]fî£/‰ÀZ ¶ÕŠl¦Ä« Ž>ý¨UòýæaÇ¿«�vîÕÿsð‡.{*ÁjWô>þÈÂ&ùunèÁÜuÁ§üÀüÅó½ýÁyfuL¨íöÁ¬ûû³?uÅCs>{Û…Ÿ¼hö.<bZeÚ‡nõÌ>ó­;^XÜûô÷=×b£Nöºpû£™z×y§þðŽg.Y<ÿ©žXdEÇê»}àÈ1·|é”Kîzvaï¼'~vYµí‹ÖŒý.Ô~ëXÿ}m÷IZñÀuWýù±Ìîþ«ÎüÊo‚÷¼KmïêÐk¤ð-»ì0xã·¿sdz -œÿ2ǃÔv?ñ¸õÿÅ~ïög{÷¾Ð+ꨶYÿÂÞ¶[eÙö˜ 9··ÛyØúøÂ—|ÇÁ{Œ³Ï!;Þwöþ°Ñá‡M/ˆskÎw?ýÒÊfeƒm¦§•}¯ºa›ÿ ¸ú”£µnÙà zÈÌäk$” ¢EKD-¶Ö“–+€" é!¯/´LDD¢&_@KkÍÞcÙ”·«OY}ô*rÁLPP÷ËYë¶p~»%2ÖÄIk¨9Ø?5[ƹÜ`ÌÛ d(aU^‰3êÈU¡·”XJ¤B ùc CŽZªÑr d†6ßÉW†ƒg)7ÆX«­ÃÔi†![I8z|öìÙ}}}cÇŽå0x¯”Q³Ù\±²¯Ùl†aØÑS©T¢¡fß++ú^Y±ªoåÊ}DT©T½³’Þl6ûW®²‰ÑˆðÄÇ­VEQI�øzŠ_qµ'ääÇö;3A[ì7œDYWç µ Ã0¬Tº{zÆŒ[§V«AÀö •1ñ“¨?ØË†Çv2ù8[ˆæÐiAå’ÈKY­Vƒ @‚P¡äú’¯ 0 ÂG±’$ ‘QJ…aÚÈ$ ¥12>H$# áÙˆ%òoG‡½}r„µ‰µ\¼“�ølBŒ“¤ÑhX„(Š==žÕz›ÿÊ(@2b±èƒÌ,(ôD2ó{™ Ò”H³”Ë>@¬.—•´´mG !o×ðThA^—R*¸ àü‚(ŠŒ{T>•8½”úÈrÖy³¥dµ Ì[L(oMð«ÆËLãào´UâJÿ _;Zkopô_!"Û%Ûò ÀúŽ•åG® Êþ#PÅ<Æ0 ªÕÛ‚ ´–âäUg@ÒÒ¿ÜóTßÝŸ™ÑÍeTê#øéËz³S¯üé/»Çæ[ù«1Ÿ¹æ¢ƒ‡+�€£úÖ¯Îãq[­3jân_š½û®:sÛ<lWÞÉ/?Š—¸åï½hè¸_üøÃk‘µ·}ùWßÙño§½mÓ>zÏŒ ¯ýÚn �µá‡~tåÌ%n2zôÔý¾;pä—<ymšKž{êù®M6i¿v圇î¹gÖüR·£è§l±éÎÇ}ã¡ Ÿùàï?÷Ö.� ¹÷ž‡ækë¿jL9쳓û~rÝ);ÿàCï¹ë…õÇŒ­#è±;;¹yÍï®}ÄVß¾Õ>õ^ñ•§ú°g·sßwÀ¸¹—{ùé'ß¿d‡=ÎøäÄ�€Úè}Ó'á–ïÞ¨гë´ÍM<é€Í'ü>»à7×Ü7íðÃ[Ymtâå×nýß~xÇ©›ï}öœwýðÆówO#«©ùô'ïºÙÔNüÍØO]û£ã6P�µ½>{ÁA+/Ú{“ÉÛöÕ[çÜ}õoúß}ä»G¥« ?xÙ/ÿgìï>¶Ó”MvùØÕ³“jµŠ�ÁŒÓ¯½æDøñ[OÞòàïsÕÌ3· ÇìÿÝ›¾±ÝßÝmÚ´O»gúy7þàÐq8ú€o]û•->ki“·?ö‡B¥ZAfsÚþ?8úK÷Ä={]xÓ÷ß9çÿÜfÒ“·;àôëžK yo¾'¸îá—ÜtÁ[ûÒ¾[n<e»ýϘùÂÚ…·ïêšIoò±«f~¢gæGw6qâæï<ñÛ÷¾l‹¬†ÊvŸ¿þêÔ•'ì4uÒæï<þ¢û‹ÉíiF­™Ú1ê%»ä¶sÜvêô÷œ;oÏKfžóö¨ üÄq=3OøÔMú€s¾½ßâ vÛhTÏÈ?ýð¤Í'¦…%»Ÿò‘é4å˜ãvª��Ø¥ÏÜw÷=O¼A÷»?zÊ´{O=éòEý~ÝÕwùʵß{ׂ ÷ß|ÒŒƒ.¸wU«– ]»œ{Ã÷]tþ{§OÙþ¸ëF~ræÏO𬠨òÓW]~Drù1[OÚlï3·ˆªµ*Böº—¨²ý®¿òðÁKÞ¿ã” 6šñîÿäÑ~Ù±ž=¿~Óeï]òíÃÞ2yòV{äò'¢ö/Z3¿Ö´ µjÙ³¿ûîI{Θ2uÛÎyr» o¾ø°µÎ¥êЫ&5ñøï^|HÿÅm5iÃiû^Ö·õ¶7ºv9÷æ+l]vìöo°ñöç<³é›¹(®ò*ø×шv«Ll™ãp{{°ÕŽŸ1¨ö8bï€ëìwÄ®Éà¶ÇSŒ¥³‹ïûÞÉïœ2vĘiGÜ<ñÌo|t«W3Ñî?H÷ë„°R!$µ ¾ã¸C@‘Æ€Ð")B -®zÏ[ÁmB_”ø–ˆ¼w\ÞFDR¶n‡Î%(•]†.þ¥J„°¢8®L~ögÅ¡Àð©Z©2%[)EV*ßH¹¤}Ê•<d”ÂHŒÁ§7°¦Î°ÙY:¢%âJÇE�ÖY€¾E—S ”!L §šKhÁÀóñØÙ+l.ðÛó–㊽gU2PþYF¶ýýýCCCµZmÔ¨Q˜æ÷¦‘dlÔj%­(j¶ ¥•˜Ÿ�Jƒm·A ´&k[Q„îÈ@²¶9I¦ù“ ¤“Y ˜¤ ½`š‘ñ%^¤²úÆÀ�Vë5"êPa0jÔ¨j½ÖŠã ¢$¶î w?¹Ö&’i81¤Ì¹aYZU>CDd1 k��¥¢(êïïO¢X)¥“±”nmÁ!6 >~²Z yòé Zk¥0µ«ò!<^Þøƒrù&î†Ô "™É_IÇ»R ‰HñZEŠ,¡¥Ä 0ÖÚZ¥:44ÔÓèæZ•ý}+M’p4f¾n6›‰«_òÖZTAaùß^È%ÔoµZ===ÖÚ8Ž«ÕªR*2‰ß£RÉÁLZdÒçÉgb£Š| ‚Š_wÖ±…É@ÌŽŠ(Ki¶BÆ<â€@#棊¼AÄW‹Tâ€C­BÊ›q%|L‚ÿÊË­\J)¤¬O˜â—‚ØÆ•‹ Êd¸4@oîIˆ~áÈ7²qƒ†@Ø7yBm’²É= P“Ÿ a’¶&ÖZ+TÆ 7F°qYdIiU«Ö°µZQйg~îàCÒ~"éOúÓÈ‘#ß¶ó;WsO‡þItÿ½w½9ŸõʾðwmsõûþzÏg6]­Îgç~g÷­föäí§n4<úãÉ›ýàÙŸ¾Ïé~´rþ³KõرÝÁàü;¿~üGž9ùá[>2ñUZ5=×kG­ÓSi½ôÐ%=öæ½þü—Ïo¹z µÜ“?­-«ßД“çè'M:6÷’½×:û‰ûúÈ,þÃÿù©¥Ÿ{ðê£Ö] °-™3·Õ=ndÕ,{òªOõÝÉ¿xâ[»¾J'¤]>÷ÙUµñ£º`åó·|é˜3“óŸþùA#×ü\‡þKèµoÔ¯aüÃôÆü·²Zö?Èmøíä)Së]«+ú`±DO=ùä^{íUüñú™3Oÿä©ZkD�R�, Â�5 *Kø·EDÐíu\V •ÊŽHÈk®I[üfJ®Ó‡ ^U"G‚uÖ¶QjÒÞŽàÛ°�€]±Ö"ˆ”l¾8n-(T¾&k¦~Œ^Ãæx¢/‘ŒÙ…߀ÀUÝ›‹^î¶š1‰x߸Ç6àLi íä\q/Oå¾fA)å éqùtžâÁV­Sm^Û °Ñ0QÜ÷Ê c¢0 T­f-…ÕŠ¡ö¶öF£A®pcÇÍ¡!ÆEµ –N™èæÌòƒDe|Å"�åI¿ε@�ˆETêµJ¥‚Zñ錉5ÞG-_íßK"<�Ù/š«ånâ0#Q²` 9w®U™h¡{†œøª@EIŒ®.@’$‰FÞ8ÛÃ:^¼± JçÏIN¥pë"‰ø"ÃNP„A¡B…HDšÏ¶ ¢(‰•RµZmÕŠ>cL€JJà§*[‚ ¦Irð�Þqˆ•J]}>°C6ÛF Ú†”’†¤íRv5-˜* …•›÷üçüîj<&÷¬ˆÞÂ|ý‚tï*9ØÑí/”P¢µ7�� �IDATLö׉(ÐðÖKµÊg 6ò@r,V ieHí îPo|‘Âæ[+· �T²Â‚ò§Õ�Û—$â °DÔj5ã8¶d‡_"êÐZ‘3óWlsÄO_—ãÕn¿æf»ß÷È|ð¡ïàÄËžz©/®­·å»Žûñ?øjm�ÉÓ?ûè‘ß|hÁ+ÍpMw>âÿ]ñ©5ØÚöäßN¯+«ÿSÉÎÿù‘[Ÿrgeú~ŸùÅ¥G®�ÀÌ»á̃¾të KpÔ”<ë—gïòª•éå?Ÿwä'¯}nI?uo¸Í{NúÅ·èØ:Ô¡7 Y"°–¬eå.H½k@©V-•Z t=þ,\”*#¹0ié©þfþàÓ‚™@ª€(r¼;ËßæÛ)|.+ rDd- Rˆ\韟ËúÃÉäŒKÝv롾 :'•i4ë_íÁCa\…ùpW²ñ›!@a>¼¬§3*¼‘2I¡ˆ½”Ί÷/mû,–[öŽt®kÈîSr¶¥T¤t«VL’(‰9ɬµÃ)á+W®ô¦n¡xÒLVc¼ð݆<`€<¨(<˜"% "R¥fùEZëfÔÄ®zw½^· Y¥ŸàÀmE,:–f‹2æLû‰™´8 —âQ9ä”±”­.��k“P­8»2ß>³"[m¢*ôD~L.°üZÃL˜ üGö·û¯(×2¹dŽ,ð‘í<ÞÂëò³ˆ¼�Kk§¼“ðæƒ.¥ˆ+8pÖŒµVWB¿„Ó-Å˲ÖjÅM¤;¢»Gl ¹ô-T˜ZDr=Ǽ—y©ð)\÷Û©oÙæ3 ²Q”ªN–yå'ÂóJ‰øÿˆµ–T. IZ [«¼X&Þ4¤õD=%’VRÑÅây4éÿÈ?ÏÎ0IÙ$RE<‹üFMDÆZÞ°l€­‘ccC@Jý7#޽¤69ýÞ•§¯ÍŸvתÓVOcß/\P¸´çùÎ;ÿ5w��‚íϼíù3_Õ#mzòo§µfõUÞ}é¢ÞWõ„Úèä߯8ùÕ½EOûȵO}äÕ=S$\÷°K=ìÒ¬‘u¨ ½úUð_Ikó䵓C@ Œ5é±4ˆ`ÁZ›ùà @Kây©ú—‰ŸrZfÑ î}²Ay]ÞæÛL}û^‹-éÜ’Ê7Ú°„ܹ÷y¬"ЍÊW{#Èp¯Æ<`€’êŸïUvòj·×û½ÏcéòÉPê‚U¥ÐC¾ž‹»ÎÏäe�1\€Ëúwqu޲&"“˜Ä®PëªQÿÊ•ÍÄÔj5 Øjµ0\Ý”s›[k9¿-Ì.ð¶<´\›˜M·_GDe3Nºv”Réi…&"­4(Dg“È͆„T˘Ü2o J‰äÔj5ÊS­V‰ùÄM!3‰Ñ%ÞûEáí5ΉšzΕâ“KsÀ^*/fIiîÔÃÂr¼Ü [?È êõZ­ÕjÅqlƒÐS Ãþ è~Kæ0êS(¬*R\Qò0R�Ç®³Y'ŽcD  'ŒHü)±ºlßR³ßÍÈ[o€(ð°ÐŠ˜#ù-a~‡, \åóGˆÈr”„Øß¼¸@áˆË<òMùeà ºd®üLµ ä)Œ"7¢væ§Â?™IÂqµÍ³ù)cbó×)D)3Ö’Ò¨P)eÜÁ=qû. ÿ/¥CêP‡:Ô¡uèÍO:ÐJ)Ò:1Œ%k‰uË�,Ö:ÅÍ^ÝOátf)ÀU*²à\ ÖÚ È…ÜûÏRßõZ&:P⡈Çcpòq hß«•Þ³š¿ÓÃ3$L}À�œ P:bÂçv“Àäpl¾»ä æ!½¿GåPV(‘(."1•ïžôøe7L4l¼¬¦î÷>jéHdë@lu';¦õ€M6¬Vª;I’¨Ù4dAk¬@í«ɪf$Nd h·*Œ‚òF™B`Ká·|°Ô”¸’ÄQ¥R©Öj|¾z ub5ÖÇMæZ"¥|>BôfˆT^ågM’‘âƒ] ªµšËβNüÄK-Ôë, ÆI‚:ÍZg÷)äW""´·•…ÓïìžÔégå*²l”Lce0@zî7¨¬$^A’‘ë'Xc”Ëiw6ä· GÞÍÎÖ%"ŠÙc¬5W°WJU*•°RiµZ(×]®†”�ÐJ 28U«Ý†–ž2ÛŽÕe’â]ðÕ)p⃨0Íp)-ÜCBk �­%BË܆¼Ì§I‚çŽó@„nÿÃt7$k ”›&„R›2äf%GÁ"ÍšÜ-N:f)Ÿ8�b´eQuréÉëþ[RJ¾È¨��¬1d-)•$&B"�²k1]êP‡:Ô¡u¨CoZ²@J!*]ÕÚ$ƘÄX«”ViÒß'agú¤Íà„ ð–ª'”šrò~ÿ cŠaö•P(‡Å~îïKôú(¿H« –xW(‘EMÔçp ¸Ø~t1® å¡ …bÞH®$¡¯œGγío)îi·}ûe*¨ïi›%4+…„êå@8x˜ ˜ù)0Æ(•âO­3�( Ã8I ÑÓˆ«V!b½Z³ÃZE‘ŸR®”ÇÿNóo<í¤‚FW;c<cL¥Zíêé•ÕÑH’¤ÑhDQäÇ› •ÖŒÜÊë]µœÖáXÍ|&"0–ˆ*ƒsMûá%®Ä&Xb lMÂ>aN�ñÍJtT~—Œ[$$N’b lZó29œ”Qn•Æ8Ž›Í&w-)®€)ôô÷{c™è•Âv2)/–]âÖUì÷•=s”8§@ÎVYŠr[“XG…?Ë4·eV~_ÍV®·¡�CÙR*~áÕÜfÚWQÉ|ãy“Ù0[tY,µÊN%ô3к±äL‡R C3ÂÜ“òÓeÇøi’Ì´”ÛÁ oîôK¬ “…eÞ"_ 3«ì¾Ù=8ü!©êP‡:Ô¡u¨Coz"€Ä­) ¸Ø¿RJ¡Jº”'¯Æ¡ÀºÒs+¯§À ÕÅó/jþ U^¯²û¦<ŽU˜Z¯ß—ƒÿ½2JΑBwDD¥øÛ4b\)e MµFväI¤GÂ/W�E˜sº¶O<.y?ž´–MLq˜ �KÖPd‡h,ß"´^’àp8’êu±þsBøÂàÎ9 ‚ ÔUpÁ#Œ,‚ª:ŒÈÚjVjÕf³iâ8Ií=Â2@CBePèm™™m™\øÚAâ¬MçR„Z½T+Zk iü J{ö†a�Q@†|V¨dÿvýocDcò†*ÿ¸RŠ”2ÆXc8Nž_ΪeŒÔvƒqJ®ð’)>Δ1X¶<µ Â#ç½,C’¯wÀÒï>Ò„À�¸ õ@¤”J¢¸„ÑP³¿Õ2ÆT*²)o!5©¤ž|× ’~Y¶A¿¼MÐßÀ¢›žþE©-&/ ’Š€ ›~J§X7£˜·r¦¤EÄòÁÌ"éw+*™M –4Câ¿©,‚€·°@¨ÜK¡(i鞌YÇäo¹ðÓ²˜Úåþo­U*Kð×Ûî6^02®º’~Ǽ„`ÞZ!‡€â³¤”3ÖB~ý ¤0 b†CÍ¡ ÂVñ—èâG�ŠsÝ¡u¨CêP‡:ô_E •%K|ô¢VÚjD�C6H» Æ”*VÔ“z§wè õ±½s&WÔJzÂóZ¬P¯sµ¯e—dgÈÙ/dŽ�¸Ü��2Rkä>fw–Yƽ[Vœì]ÀÕR'ö_ymµÀI Küë@è©l𺸌¡ðC QtJ e¸?%ý b;=Û·Ül6 C.OÚÂ㺠3R«ÕÀ’ ´)2‰Öºgäˆx¨ÙjW!,Š"FÔòÌ“=£–¼*ôDí� Ûø·èîéÖaXCD‰+¶Çåú¹{aZW2MkÍr.'¿�ˆrHÌw[9±ä fŒÀ½ÌðüFq¬µæRmÖ.Tªh>±Z­ÆqÌ™ÿàŽ®”ŒJ?ç(mËÙóB>;)­Éóá—¼÷*çjˆ=!‰âz½Î· ¾²|¹µ¶»»;n¶ˆ¨œìãÍ2–‡3ÜtË.‘+)ÊíÄqÌžð$Icêõ:G¯È6Óyá( Z±T]¢-”Ea(,Ip+§¬Àg~Ä—úãžXw ¡g5ˆÜŸüAÖ¸ßgœ¼ùn¤'Y�€X×,²}Ïywlnr‰ÈXAUaŸ‘äÇED©©«]0·`­•]T.Y Ìðò³¼EÊMUÊ 9ëI­–r÷¦7(Q|¡c&èP‡:Ô¡u¨CÿÍDT¿ÕJ!ØÄÙÀ;K E(sS“R9TLQÔÔZspVùø)žíq2·ãú–* WmIø©Ò+6§Yò=|_Q®¶¹ …¼ND� ¢RÆæÂò½{Г·@ øÑÊÓ¶UýWè °c¢(Ò*»3~¦Žh¢ öHÍž„E†åëÆó$í¶à€ï³>R§›ô¡Úà"”R\BÏZËyÝÕj5=ø�ÈÛMØtbŒ‰ã8n¶ü Š@k%IT²¤ Ù+‡œ9ÌK$avY›/` ½xä¯3FÒÆ˜Ø8«‡I”R#GŒ¨uuQ*xù¢*å ó¡R©QE�™œ§9Ø: Ûöï•áîÆ˜8N”R>MÀÃg€+@h­MŒ±Ö2IJ@€€Z‘!kbhÍ%šC�€Ž­! tU yÁzŽÅqÌo‰¯òËS~1µ¾%¹ã0|„|v®†„¬©îîîU«VÕ«5EP­TÐÒˆF·‰bZXŽTÕh ï69ø­µ.çŽû M’Dq>½Ö\iÏ÷œ·…V«A­V‹¢H• y$µ‚t›È–)ÅݤE�ˆ0-/‰h-¤eó¨ KC’¤µ-ø­Dà|ò€„ŠP)ž6BEÆ„ å&#­Ÿ^º|YA^;Zk>ÆÃü,C~ëð úh‹J¥’ I)A.C|`©à3& v û?Z)…n²8þÅoË錸eMy£3ØÜ¿ ¿¦tPADöÙôHó (|148(mØÐ©�ŠbNwrV[o‡:Ô¡u¨CêÐ;<.Õ!•E2Ƭ–y¨YpøKÌã?Ç`“på¥^©¼ë©±ï÷mj‘YÍ”ªž€ò¯»Ëý‹²A òðÌ'-.@YNu»Jàí½—Œ¥å Þ·ìGyÐåa$ ›…*,ç^Ë·{8Wà¤W©åÔ b Tv9.r€Á'nhi7r}ðóú¾¥� ÅE6Š¢ ‡[¯×“(ŽãŒÕZ+ÀJ¥bk);¼]N–¤2·WCˆmœ’3 ¼”oL‹GÔj5•ÄI’Ä& *•Z­Vot%6«ê棬1±F‰Šhb^2�ÆÄÅQÛ�9»8ùÑJ«’ Ô³òC£ü80;—Aòo5¼õ(»,¤x,c]jZÂâŠ(ÌW*BYwÊ ¶M¬Zµjhhˆ!n’$³¤’µ¡¶Â ÂÌäE—÷æ³Üd¬ËäχYrjºL4Ö«r Ÿ=ßÒÜQÈßàí_ži*¿B¥ j­Q̦lÇ?%劄óÜï�r°.$«{â÷j?Æ4ŠÀæ'ÝÃr‹#_µÕ7âxÔ¦lGyÙÊek¿´ßrÛ®ŸéÙ·éHÝ-"Ú¥AСu¨CêP‡: t0ŸÄšê™ˆ»ÏYÍUZkw�›už>ߊ׼)_Rn8”Ôkez0}QöÏ_·Öj”5Ñ‚*Y&ßxœPs¬áHÒ Pq©pÈZBÔ =”cñ0ßï‘¡¿b rK0™“v/bxYà¡-D'6¤ŽGÌ5ëûLŽä úI,8À nLÿ•1&ME‡Té†TãÆÖPS!¨ØõmM’D±ML¨taÛN+µÑû‹3î…ª€¥WÓr¹©$ŽY«õZ­«‹O ȸ«0«3ïXëlMÖPP·é¢<²‰ægÓäçŠ÷ÝÖóçÅrˆ¶B@E<›�`\%NDÈœŸ^� U �Ã~È 8䱨[’¹Òˆ(!UÈ©mÀqIF`è*@Ôji­5ª(1:”fëz 9)HK ˆ3 ³é /çþf9:¹ x¤¸4º»Í¥*({…þ·4(äXŠà+È *àÞ² ¡€î pÑY<‹T–¹�D TàK1r åê;d¡>˜%›b›J§RJ¡"‹&!“¶ß+rEO$È÷ã}Ëmé*lŒÊyüeßäŸJÜ/áÜ 4[­¼ëúž¤;g¾å«ùoR¦ûï½kíoîÐëHoLο1{Õ¡7>u$§CoJúÏìÿ¬Þþ³ ù ð‚Ò„ ”J£‘¥ËݺÂxÖZ†eM·�›åý(|Ñ ”`éw©\æá¨W@˸‘JþLrÞ-"â(z¯ª¦OqÕz�õ´L“(SOl¹Ÿ¾3e .•Qß7·ÑefB& äjÂ1ÃÊèÂK ?<F’óå�%Ÿ•«L¾š;%†‘8¶€!½¨hTZ†¬µlñ\©T¥M ¶Z-ˆM€ÅÃá×HØ /ú™ÅvöÞrËpç£(B­ê®îînC¶ÙlÆÖ( Àù=JBDTYò擽0(•™_¨kÃ×}ŽD¶r\(B²­å2" �37iÁÜPæ¡ü e&‚|ç‰( o!ò) V”-ì’ UI�¬m5›”ŸÓ4IÙË˽„J–9vï—X—S@g³“¾B´Y6wúnXÎù/ó¶0|ræBk_v¬€`AÔ:¡¼Éò†Âl×±]žWl‹!"cÒëü§™P’½�`óÂ#Û”V'‡*I O¬¯ö‚.yÚÉÀpä[FŸ·b 1ËNžW#o«¡l_Ì‚¶\;ÎFü* LoÛù¯ö‘ýãtÿ½w½9ÿÆìU‡ÞøÔ‘œ½)é?K°ÿ³z»zºí¿}ZñÇ@;Ø•RÖ¸†^•OIJ¾Ç¶/j«U£Kj(ÜÀjrùf‰ä‹X­/t’Ÿòyòþ¢ï3¡D¡F"ŠãØÆ *Žg`­5äðeyÏu§T“Õ&åôú;� *äôs@ôEÌÉáU�¨¡œ/å¼zù”t-±Ÿç! ¿±õp€ˆ4*v_»XeËaÛÝõ.›˜hp¨5ÔŒ›-²¶ªƒ0 ›Q"_D. E:©ýE� ¼÷˜†™Ž²�”/Jɱ6MrïjtYk[­ÖÊ•+£$©Ök* Â04¦hØÂ4%³/HþX›IšxAE)Åð‡¥1 äè*køaƒ{sF7�g÷BA� D@Zœ­ òh¶ ±þ]mWYá�°d=-óÙm*�.*€§R£ŠLÄM†iÆAvº§u·ç I$}øJ€»"yË »$¿ÂK²3 ¸Ò©ÕLgq4d3H™r€ë­8ðÉóPظ<—0]ž«|ƒü—Òèo˜Ü?H.¯„gÙ6ÖZ²Æ6û¹!¡µ׿ ƒûÌ'ÈH)åã ¤±•ÉÚlP~?ñ› ”6Ÿ¶¤|ž—3â°éÖÐêŒGåuÁÑ. Ä[Ž®ðÏ�Ò“5Ð_ä@„a—:Ô¡u¨CêP‡þ«ˆ€À%§)ÓisçÏymO¹‰Cƒ9Í[À‘e ïµ1éU“Jy?,€/bŠ™s0X¾7§XçAŽ5"RÒZ•°üT‹ˆLVsAhÆNV¥ZtR±öZiY‘- ÜßìÇ(1›ÿj8µÕHι°ùvž2Ù·àÂCHxªUžá /¯óŠSÁÏ)ªße@W)0nEÍÁÁh°‰–­CTdl+i*xXÍW¯µËž”'z8òây?í¨Q£â8~yÙÒ+Vt۞ƈ)N˜d� P)•XùÙtÌ,Æ¿xÔJÄvžÜ»ç`‰hùßs9û ë‹Èªoåñ¢KhËÀò{}÷¤`ä,äº!9O”¦Eä€dlI·¥ 1IµZõ^vñ{m©À±�¼Z¥9ƒ¼«�¸– “7&ZkµÊ¢¸Ì$aÍ’ ¬Nþý*ƒü¼K‹[Ir憵¡‚¨ ;ú‘ÉÏ©u‹¢?PÆÇ9¸îYÄ£&·“¶V¨‡¯Akóe I—W3–òÜ‘-ÉÒj)q­AlGrÉ`;[Œ1‰¯çQîäÚs¾CêP‡:Ô¡uèML©îä «VÅÔú|•zmA3ƒ’·ß_ô¿¥Ó¯Ð¯——Õܶ·­~T^]–ïB�Rˆ¨PkÔ" —ùoÙ„��‰\¾·ÊJÆå; ñX›XYtþ|™‘‘–…σËíTmUhY†Ë×y§œÔѳ á2Ê—aƒ´àx®rm9(¾ÖvÊ‘½sé³J)0dW®X‘D±2T Â@i26¶ÖS B `Öç„I²·€ å,P¾ÔeÚaD�ˆ¢(Š"PØh4T«ÇñÀÀ� j’¯ÕGÅÊöIØÅ¤¸:3YkPÆ„™)*ãø�d›Vv§‰“ [rKAkm¤1¢]U6D‘ÆRîL¡óˆhLñ€²ù@>ŽA{¬Åˆ=е–PAS¤sfÄÜŠn'ÖùËÛ|‹yo¼'JøEǶNá²�l:[&¤\Þ>Ô§ØžÊ/ç+V—ØèÏ1uW³¥Ž^؆B�ÀIÞ$ÁñCÃâÞBÞG:Rc _ñXÒ¢,ax/É…g… Àj­Ùªà7 Ù=ÙT¡Ã’ï²Öc ¢’-a÷–ÂWé@\½ÏŒ?níX²JGL«¸õâÌÔÜbÛ®v¨CêP‡:Ô¡ý÷ðÙ…iÐ%z3W¬!cY“Ã0f¯€v:,"FQäO)÷šuS�àTï]€á’”8+;ÿ Ónåƒ>~¡0Bà)¬¤�,bÀAÆÎ±fež-bz¼–JùÉËöËo”fº»tUîÇ.£ÖFž,´õ:€uYÇ…øÞ´Ö€%K–c€B¥ƒ ˆMv8¢g V*Žšw0‚Óþ ‘ƾ'¥¤¿1õ+*D´GQsp0j¶4ªª B­1‰bcLF£ÕŠ A»l¸Nr2›åRµì ¶ƒ‹X2]îÉüüJ©——-%¢FOOµ^ëëëk%q£Ñ`~¥¼%‹|àæ1³ôͬnšˆˆ]³Ô` ÐËëKšX … Ë%Qì'1¾B âbø2À'E™bÐ\GP¯6à‚Zн°JÔ¶È:OT@Y䌀À8-@ŇhÀ0 •Ru©Õå(RÈš6 $ y餺<)ßgi, "„€¢("°d FãÄ—“PàëYj×=ô¿R>{Yò2Éaü>$‡ˆ¬÷HK6k€Ð’R‰,‚$…h¹ …h‘ó1|Öƒ“«4ºÇæ³ÀÒ“UÝnéwãf³ÉFÀ@©ÂZ3œÚ¡•HÈòžÎ0,pÖ\5¼¤!"Ò .FÉ7è;S^žž|üù*6T´:…Á}(È'¹™Éó!k‡+øm”ïNíDDY^‡:Ô¡u¨CêÐ9!ªxóŸ©1 ‹Jk¯VQ’‡•µV ÒÝASdm€b_)‡î4€Òœœm€I)D¢Ô¹'×Ü…¨•h­‰IˆHó³¬Êäõ9ÃÉ †1iÍÁÃ� £k­I¬Ö �ßc(±IT Ã$‰Ll +»h­µºcÒãë8Î6ÿ§„ô¬µl%üUµZµÖÆqìoc—>«PÒ’@sΰ5þ)¥�T€Š”×:=…"ŽcÎÁ&kÉ’BP  1‘¼_ÑX²J)@k)êç´m¥”V¨R”h�01 �ajS ¥ÑRjnp¿ÉýVqתUE°lé’UýÕ°R +d­K]Hí#E»¹‰2O9i@KÌH'5ÚÌÎ"%سÅ3Í3¶ ëi–¾Ì@vàÀ"$“&Mjµ Ù8ŽG޹¼oÅÐÐЈ0KÍV³V«U*Áàà RªR©ÄqBÂö¤”(ÇRÈcCÖÒ¨P‘J¡zƒŠ­ ¹Îk­É"¢Eà<p¥µ¶ Ao2#›´2v©,À'=`ÂZpèš‹-`»“2 p@ j¶ 0Þ!OD€ÐU4$"DPJ©@›Èò%�…ˆÀc… ¢VLIa%¨Žpg9ôlÄ\YJKDìV€�dàt&`—}†H@šØ*¥ �¥Qi�0,Y‹*m9\ÃZ+ª³„‘kÉj u¨1Jb"”û�hЈ˜–„œ/²ˆi¬@&Òˆq‚ÅÖ²–µ" Ž€ÒJB" KHˆ6} ¢J}qñ¨A)•Jú£",@byÝ@GQ¥RᙵÂxšÚd5%Ã;%�’"K¶V¯U‚ J›$�YK�¤‚cc-›®t@ÖjP.Ùf˜†få <ª÷%fSfj –Œ1*ÐÒBê“ ¬<n„-4¼R(³Uù „ˆ¬V,æçÄ“°ù…»ÁÝÔȶ§R X‡:Ô¡u¨CêÐY² °ÂO–X“'kÉ ~Ø�� �IDAT÷‘B>ĉk¥‘ö°¦G‚¡pyï"*Ja!¸Hoþì#´=–fòº¸G(*xC;ÄX tI˜Ï¯�­µµÆZ›D1c¬‰ã$IEuF?!ﺗúw•û~zÖeÚp!ӻ쟗*u©œ¾IÈ»I‰,;ñXÉf+X²àÜ­¾ÏUŸT  (»!9\“”Š“8X¹*j¶Œ1=î�•Ižw’6’*詆é í«õuÿã$gSü¥U”$‹†aµZåXV«ÕÕÕU +À&‰@£R Y-R`T™uJ)câÂRbž»†ùe¶;€r¹å~…> ª/ÌŽoøB�WNqmáPYøID@ÈF,“M¹³™°€ÍgµZ­ÑhT‚��¢(j55Þâ#ãØ}· ²—™7JkG’\5Y?m 1!¶I)ЬJO9©E0dccÀðˆH¨ÈÉpnù烛<i·Ü I@!âoÖ� ]0¯ DFüÌg~B‚“°Qúñ¥`ä¤{n(†Ù ‰-ø}5JÒA*‹DD‘I�À&¦^¯×ªµCCCD¤´&<„°%‘YT¾Ã+€yy‘w¯´* ”v)Ͼe¢ôàFžb#HIÈ¿š´Vé¦T*äÑ¡u¨CêP‡:ôßCè¢!¯’!×&`Œ]Hƒ‡œf>lP(¥À�a”Jå[k•Ss³PDŽI.´\VõÊï-wÆSˆVHÑ5Y²ÄÞQ¤8ÕÚI±yCäd� �Ìd­äÂvÈ!Žc®"FDZë Œ1Q¡CPÃÙ ÀyÎ¥‘‚ïO’TK‚ j¶1-[žVóf‡.€8 AöMâI%*Ôô.°ÚÁÀÊU+ûúªaØÕ¨)KÖ’Â,¥‚Á WãbBžöPô6}‘›ƒµ´«'9(ÏDÐa$IApP†|eé²±cǪîî  Y" P¡ÖÆí ôbP@Jž±þžn+¼ú’í*¶q¼™@¾Òøò"¬J1§ˆNÏuÑæSÁôà™CÂöð#c]×£RµA§Ó�>›…¬mµZÖÚ¡Á–Ò\APk] *¹Œ /ËKÞUjDp>|ÙU ân¤ƒå͇ף%>}��|*Oº"@¥dÆ›„´Ò|€½`£ƒ£Ü—Á,�Z9)!¾üájäYŠ3Pnõ;p‘EÑ «›€ZñáiÌ‚ï䂦ޓÜf½^çAk­µðR©T*•Z¥Z©TÂ0l6›Íf“·e­µ¥ÌL)ó¬(Ï¢´¡Ö«í~Ôˆie>1¤°(ü.TˆÒ‚üºk»S‘ÛE(Ý…ÒÙ.F'¹ Ž™ CêP‡:Ô¡uчmr\v†jÀ©¡^ß²”t`ŒñŠ H’„/ú+äBy)Ÿi/Õ8–|Ú¼oœæÇZi9Àò0F~%É£ ‘êþíÖZŽÔõíX!Ö·&õÝBpAA‘õWd'Y)/<.ï—Ëå»0Ÿ!ï¹yL(ûé{eŒI’„“|¼ÒïKK¨|ÑÈë÷=I’0 kµZ†UÇqk­³òûÂùìÿ®8™ 7”y»–T@¼þzÿ²eËV­Z500E"öôôTÊcÉZ°i CEPà<«ÿòÓÍŒeâ;uš.’™Ìüýå™õ-Çq,—ˆé.¿J ¤°ôÚr²<4Ù2"’( Xˆ†‹E’o¿ÖèâáW«UÔŠC6Âj•Äì�€ßCÚv¦°Ø³­@!¨¢|"‰ü“Š$€Èï*Í8J’ÄZ(†aÒ‚†…Â0Òëo+¨|Ñïžüº+€uDÎ@ÉFÆ‚<{9ôW¤XÊýÓßì·GÞÏYÀüƒ­VkÙ²e½½½Ë—/ç5î_*eÃw¯Ì‡Â@ $ÞmY'e^rÌ]îNå¨.O¹).µ&:/†ðš6–½ɬœóÒ‚e�4°ê…§ûšÿ¶yMþ>óëýaþêŽù\k¢¥w_zÞ• ¾mµ§Ö’§îrñš;;|O’U½ —Çÿ„®­™†e55—Í©ÿ?{i'ËŸðÑ­×ò¨]9ïá‡çüËÆÿ/~]‡ÞDD­å‹–ý»{1,eÛã\È_—7�í”p&…`µ«,ÿ�² VmTp‘� ŒÙˆaTÊ´Iù2rÞ' nÓÁå‹‚ðy]òš. ´¨ˆÀðo¾„ óu@K`Ù4Ù”•VNSˆîòì(tÚßC[ò°Ê³H¹cÊÏBÞ@ã;ƒi°±µ6!2< Æ”gò1ºÞ‚ãÕn ¼ ÅÃ>RþÍ?`‰€Œå¯4**Pé jµÚèÑ£{zz� N 4 /Âo$¥"Ö.h 8(K @ ¿òÈVB"ªWk6I¢f³ï•WV,_Þ¿r¥F¬V«\7[ êÀY™úŒ †–/"â?Yü¼ûÙ79;tZ@˜ɵ#»W`uážÂ¸ä»ä¢fûŽr72>—ûYFNOPHÖË­©5ºÆ­·îˆÑ£¡µZ­VEQËÌDLOïkËR…>4€ßîÿô2F eÞºÄ�Àtµ§?@”ΦÉS(äMi™}‡ëã!,‹7»€2@܇”çœ!Ï¿¹H;›‘—Ì‚hù9µæ*(¥P+ÔÊ¿ÝO1hZ) 4/afŽkS#êÔž"fM•J¥^¯W*kíÀÀÀ+¯¼²téÒeË–õõõqmZ®VÈÆÙ1)l ^€‡¼ùÖ ;°¿^Ë¢T¬5É΃3‘p¤•^Ý©œzR²äÆS¯ÿÍ£�úoýóWÏùû²—>׺çûg|û‘xô«*Š›­’ªg^÷•ÏÞ´¨Q}½:W¢øo°Ï×î[#ʶ'vî÷÷Ûì˜+½Njê«¢aYÝzꌾü—íâu"3ûGÇîùÙß®x rL˯ûØnùå‹ÿ¢9ù¿®Co"jÍ<~ãݾþèu¥fÛã?SÈiÙuïßpÒox-kÝ5±üuì”ä‘£ _KˆÎ† † z!æá=ˆ’TåN°Š©Ü d輋LéðK°Ùÿéì]¸³�Â}ƒ.´8#Ê) ¸ÚÿI"J‚ò¦9dÿRsÇ1$ߎˆKíKö’Ã6~ÔT:΃ÿf¸~Ž0o¹oqË>€”ß+†Ó�Ðh4ººA%­Œµ`<jb h¬•ï܇�‹»,kOÃIfATüçŠ#­5¬Zµªï•ý+WJ±‰Dh¥dÕãeç%ÛåçrÇ$3åä,\ñ$ñ$¸%Y^\ä FÒÄ {XžÍ²£í "géûá@i fß:TŒ®–A³Ùj6 V«ÕëõJ½ÖÕÕUëª×»•J…öe®†ŸåW+Ê}+ûƒÎ4þéjØ|áÆ„,—ú³Ö¦–c}ñKoh“s:Ó »ïmÁ–Wø\(óâ`—²½O~`Û„~W<S舿õÑ FcäÈ‘===AÄqÌ›•<-ÕÛäÛÛ ùp3X`£ª0¹þCÁbí¢W< íW$•B¥µÒ¯ÞL,üÓØyê˜zmäÄí?ÿÎ¥iÿâ¹×Ÿ¾×&£ëu·9ü÷.Â2ð䥇Nsìo¼w0ž{ãYïÛr|Wµ{Â[=÷¶6]»ø¶sØb\WרÍ÷ýâï{ó7˜yW¹QuãÓî*kXCO]qÒΓFÔ{&îôÁ=:à¯^yÊÓÆÔk#7zÛqß{°¯ G/\ûá-F½ç²%üÝŠŸP—› Ö¼²¯ôY|ëgw»ÃyO?Ô'.ÿØnSF×룧îö±+žlë /?ÅD/ÿîãÓk=Ç\ÿš¼¨o�ºûš¢ýŽÜ£ûÕ<düpŸñûü°¨ƒÚù×_óàVGº‰nÿØ¿ŽÞ8=ôšXÝ¡7+µß:òûdü÷_žºï6ôT«#6Øö sn}©cÚøwS'œ°gÆþþðû¶h¼:äóÏ áMq¥XU‘ðy‚ÀðÀøŠóxÉ�Jˆ(=T ¡Ëå¦e€lÇÿ)qŽ× ºxA¦••Q+ +øÎ(¥´N‹-:?RÞcOŠ h[kÑZR@ A#h­B…€,)²ÈägD i˜‚bÏ¥¿âªÎkk!Š’(J8±�„)Á÷Yêôo´ÁíhQ‘ÒàÓÔ‡øhÁö‚o™œ? œZïy"q ämóJø[­VÇ 0!k,‘%âæR“µ–2+¹°ßT»M¢0ä×F²)0Cƒ&N4ªžFw¨ªVLœ„Z3h$‡½Éd92iôV*ùÁã.oµñW¼„·…ôRPÔÂ,/i-òíßèï/Ï# ÔZFÙS:u_s ˆ—ÃÄ Y1 ±Ì‰H)²hQh kAPÕ ÈP¢CµÎø±6œ°Þzë3¦ÞÝ�.7€ˆÆÚăPøI#¡"­€ãž€ G?i ¤È*âd"ËöþÀÆ‚”në`ã‹b+¹X�¿E€Í$GNÿL*ËÈ B°D " À%ðÊPŠÓNã#_çwYñC"ž" ©à—*´†Pj Šc:\^†òçÒ¢V|¬@’$Ífshhˆ×/"²i€‡?88800À¥*•Šr§Nú 6]kZ¤Ø±Kfr;C€ÿJò¿|E.ó62ŒE{“\~®ÉرšQ´%ê{ôÖG×9ñGw=ñè-_˜ú×/wö-�0¿èý'\?î¬[ŸšuÕ!KÏ?âÔë—�õ=þËÏøÖ=ÎúóË&¶³pÂq7lð•{{—=ÿë£W]tì7¸í½òä£~˜œ8óñ'nþDåŠcN¼l^ö/rðÁ¯qæ=q؆ÃÍû¿rÄ)Ùæ›÷=óÀ÷ßþØG|ñÎ�Hþöµ#ÿçÁ/žµäåÇü®g¿pÌ9÷Fò©ÁçoúÚ1;½ý£3çgJö¨£¯~é¦åÏüä tÌ=ò¡xÁíßþÈ®Û{ÙßE,ftÿW?íÁí¿ûðKKÿÉOqØÙÉCþ¶O¥_=óýcO¾¡¯öïW—^+ ÜqõM°ÿ‘»u½ºÇlÔJJkɾðëk~ˇlüovyãôDÐkdu‡Þ”Ô~ë(î“Á„­8õÇw>3÷ïw]´ëÜ N¾`Íq4êÐ?›*›ùåÿ=tZøïî�¤ÑàDäK;§X’œ³ZÂ*paÛ7f¨{ô™ðò[ÈãUÎ|0UZ( Ð Xõ2yÍÿdè«D®>Šôûvð&×rYëEᢗo”,ò× Ø �Ø›gùòò°zÈD ”ä_Í3UFR‡.�Hpö‘ÔWJ)ÅuþË.»Â,ôÇÇfÇ&y¥¯oÕ@¿1Æ%„É�Ý¡l,Hk,¹#åÑ×Ã/i„eì±z@‚Ã䳿S6ê]aÚÄðI &N� «Vã8ÔA½ZÕ¨ÐR u u®K†`;Ÿ°”9tQ6( …; pÂ|yj¨d#Cacò×Ënjÿв@’w2+qþ•ªQÚsÌ]ág[­V’$\o(Ò¡²Ör<–%,Å×”¹Q˜nr©O˜Gò�àk0Ôç‹rá?è&Æ 6‡¬µ ‰¹Î"¯Y²±°À½µKξtÎ{=Gñ@;)•;æ7ÉÞ·9ô|+Èä·5î$÷‡‡ÏkŸ­�I’´Z-¿ k­9܆ˆQ5xÖa>ÊFv©Üg9.ûª47È~”", RQæÃ°ä7[TIœXc‰�‹iOk&»ïW}ëC{̘ºÙ.>åÀ /?÷ü ‚ä‰_þì‘m?yþß:yÚžŸ;û˜ðÆŸÞ¼”�ÌÂ9»\xûU'¬Ÿý+Iž{üéú·¼oÚ¨îõv:æÀ-ûçÌY–ólÙ…×_þ§u?tî'w:åí?÷¤ïþéµsR3Cÿ=gŸ|í¶ß½p¿å®E÷ýüÊ…{Ÿuî¡3&mqàW>¿ïÒ«.¿c`àéÇ_˜°ça»oÔӳѻŽÚgÒ’Ù/ä¼ü¸dö’ÍÏúÝͧO²‹a×ÈQ£F5J=ô­¯Þ³Óyß:b‚óü÷ß½Þ¤üz)®˜=ÜG®»ýÿÞQñϘ¹wÞñâ–Ç|⽓GöLÜíŒÓöZ|ݯŠiÉ5Çl¸þ¾—̱Ðö)îúãß:é;#ÎùÞq¬ÆÜý¹=xÂ[¿÷á}¯ûéïW˜á綠·œuÍ)ïøÞñ»ýì¼ þÞÛZñô7Þñ“+þj�¢ù?ÙûÒ‹ïL��’Þ+÷»ä»·FÃ7¶´êÏWßpä.u�ZzÏ7Ýi£µú:›í}ÚUÏ �ÐË7}z×錪W*u·ØçÔŸ=îM%ÑÝŸœ¬±~ÄulV1Ï]wÍã;yð† �ŸºòÔ½7[§Vc'm³ÏùÄ��É‚ß}ð¶ºkõ¶>ð‹7ÏcÌC«¹ìäÝ§Ž©×FMÞõÃ?ü› ‰æÞø…¶^¿Q ë£'n¹ûY,F” =}ÕiûlµÁˆ®Q“ßùñ«þî­;¹žô?~ù'öÜt®ZcÜô^ó@üè7ö˜8¢V3e7÷˜yòÒcß¾éº=ÕJ×ø½/zÖ´í*½|Ã'w™>qLW%¬Ûò Ï~õÓn·ÑÈz×:›í{öm/§¡¬Kî¸àè·O]§Ñ½ÞÖ‡üŸÛɱLïŸÎ=bû‰=µúÈ wýÚƒ1�Ð+7œ¼ùØ®j÷úÛ¸Çhù¾°Ï¶“ÆÔ+ÕS>þû¡aÕ¼óì=¶ÚxîjX½ÉÞ§~å¬#ß>eLW}ÔäÝ?yý‹fµŒzm”<zÑÁÛMß] k£6ÞéèóníM w4o;c› £»ªµÑSÞù‰«Ÿ!þËg¦5ö¼x¡� —/߈ég=T|ªHvñíçµÃ†#ªamäú›½ãc¿Z`axii>û«Óß3}\W­gâG]pÇÞhÅC?8q—)£kaµgüÔŽº¤ñ¼›¾pÐö“FwõLÜñ¸‹^U±Öìë?·ÿVë5j]c¦xñßÍ0/Jf}ç í¦®;¢VFl´ËIgù„Ý7ߨõLÜéÄËŸnÜ…�†Û:Êû$öÌØë=oÝdýÑݵ�¡gê´õÞH†¯7 \}hÏ–_ø[�`_¼h·žw~g® U³.ûÈ®“GÕj£§ì~Þ©¡fÍ«àŸM¥U6,•÷vZö³FL:õÎ� uÛÇ&Ž?þ¦!�€øžOO{Ôu+åãƒÿøÄ·o4²Zi¬3íý¿èµýñ¤õ'œ|k´›Ïjþƒ¼4Ü©OD”朇:@›NGר ‰ˆøŒl\Î9N¡ BŇUX"“$Q’Dü9Ž[‰‰�­P˜V  „Ë ( €ÖRB`ØInlÌ_±çœ”¥A&~{519ç*GÕ�)@2H®^ ÍÄI zA£ÅtpNyõ‘·ÊU[Ôyo3:÷#¸ …D”$‰/îB…å(këJ*únK„/…˜9À á{\Wah­¼zìA¬L†W"+ždŽññ„ìãÀ!_XÎCP¶DQT­V•RqW«Õž‘#k]õ°Z *!!•°{DO÷ˆž®îFX­° ´2@ArÂsIˆfJqPVôsM„+{ÞJ^I”Åü”…6ùs 4[Ñ%¦^©òã8îííýÿì}w˜]EùÿûÎÌ)·ínÊn*©Š4E@¢Ò‹Hï ‚ ‚€X ˆ((MŠ )J醄©¤'$Ùrë9gæýý1çÌ{îÝ$€þ¾¨û>ûì³{î9sfÞygî|ÞúÎ;ï艖RÖjµL&£‘ž™qý‡æ@ º@‚Øõ콕R*{R4'µ"@[}%© ƒ( e¤€€!w„±QsG×áŽ�†žç™ÙדhøiXDIÙKDT Œ³ !éäHHzÙë# hÍ]†”êë·{ž‡ˆq.LÆ€1ä“ìzq ª'ê—Rº®[ jÂu„ëHR•Z]×FÀ»\x*TÀAÿFúo R§a,Þ ã H†ønÌŽLK¿ÁÍÅbQs,”‘æ¼Ö…1¨”ò}_÷vð!�àºn- C)! Ðp¸Ð ™c¼±hw�½´ ÌüÔ«- *€H©(ÙbykòÐÜ‹T=iŠ}ƒãyÂu5{…ë"çDà8.w\( ˜pXJ%Éu<=:DtG‹7w<B®”bŒ1Á¡$ñãŒqΛUšWQ™ÍŠsîû¾Q2ѵ²)}V:ØJI“ªÃÜZ/¶ÞAïNú~݈­±2º*гmÕk0º ;ZM5¸­ÚzXR�„AÑ,l„ª3}rý‡öÝ}(Bå× š6M+Ü©Ûm%çÌ|3ü©o^uÁ“òökœ÷ÿŸþÝ‹î{«{Åß~|Ãü½>›2ÕFsg¾S¶,��ø¤í¦ysgΉ��ÊÏ}ïKìvõöÚ∫VΚµnÌÖSÛ� 7m»Í‹³_[Ba·v_}ó·®xlIÏÂé?¹³tÄg÷k·ŸË|ôœ«.:r›A­œÊÕÂ[~xWû™ß>j8�k¿Ã.»l32ƒ ¦}îÇ?8åÃ]–føˆ)“ s¸ë…UÕ¨Ö½¢W:«Þ^bfô6Ýeûqm-Ÿ€pÎU_üõ¨ï_sܘ:¶‹-·þâÍ'^ûØIç†ÿþs/öösŸê{òÛ¾͸ÏÝvò¹s×KýøªeAÛ¨©[TæÍìQ�rá²7×ÖÌ\'Ô²o®ïšú!·Ÿ¶6‰z½ó¡Ü¡G}Ôµä¦S»|Åþ׿0oîÃMxüŒC¾þd €J ^~5wÒŸæ,œÿê=çŽ|ì û}áÞé8»^ùÏR¥Ré¾ýp�@νû÷ÿÜåÈCG1 î‡Î9àÌ''|û/o,Y<ãš=Ö=?s¹ˆfÿäÈ£o„Sîœ1oæÎôwì—¿­ûóÙ‡œÿêv—?þÏ7ŸùÙGç|ãà/þñ¨¾pÉÁÇÞ™=ó—ÌùÝgðåWU:_~ꛇùäß{|þÂ'/óÈŸûÅ[z-5ôäÁs<çÅ©ß}hö‚¹OßúÕ=»�øØO_ýØœ…s8wнgœuÓ —½üؼm~ðòâeóÿþëcÆSë®–½:£ðÙç-{ûÕë÷xëª+gìpå#³ç½òë½ÿ謟¾¨Å7œôé«ûŽº}æâY·ºúŠã¿þP1ÍjsvÔ¿Ž»å¥yóg<pͱ[ �ÀÜÎçÞóʼù/\·×âïŸüݧj�Ô;÷égø±Óÿ¹tÉìG¿¹«×£¢e¯½X=ð·s—.s÷QÅ¿ÿȨoÞ;cÞë÷[»ñ‹ßûkeŒz¤VÍz~é—½<oÑO\½ß;??ü°ÍlÄ(ÞvgÜöôÜå+ߺï³âÎ/|cúz±ýûu¾ò—¿­%�(ÿý±Û÷Ü{Ѻù˜ä?¯>ò°«{¿ñÅKÞ|ô ¯½øV/õ+-•g¿}ègþÕfÏ{ùæÃ»¯úô‰¿^¨@-»ýÔƒ¾ýÏ]òä[K¼ð)o½0gM£Û~øúGWþìûç.~åš¿vá ?xÉJõù‹9éÞAgÿaæ‚7_¸ûûç­_jõì–îü“™o/›÷ð— üÁïðsw¼øÖ?9wÐýç|ãîwÈÚ…úÛ:úÛ'kÓiËvnõéßøÖONù@EÑüoPß_¿zع/lõ½¿ÎÿÊ]ç|¸=‰0Üè*øwSÓ*ë·ÚÛ˃vßkÛwžÿû| Í}ú¹Uë_z~N >÷ÜšöÚÕò£µ÷|ó+OO»êµwzW¾~ßÅû6hª6ºùlàä_@z.TU·%Ö Ô±Ö¹}.4¸ùMæ`W?“%vT›6lÒÜ�¥‡d`ýÝo»Ø–.;©˜mÅJÙ©RN-+ÌuûEv pm9䔂�¬)°G‘z…}¶V–C» 06§g=^H žQ¦ý”­Úð}? ÃR©†¡ïûíƒå ιãy™\ÖËdÏåŽãeü|[!ö`•Œ’ ðº-#6@©YÖ#Õ¥ ê6dÛx ç[G£9 -ÉÕjuݺu=ë»Q‘"ÕÙk8£1Œ-6—Œ1ÙX˜DZQ³bìé³ß’’Ôt˜U ›2î*Fý¤õ>B[€uƒ¦ø…&[¢ še;¥m1l“|„ºW"aµ-ÏF9 »ûz‡ 2fÜX×÷zzzŠå’”2”QµZ­ÔªµZ­ºc¡”º†¨&ã©dØÊúd2ïÇUÂ`}oΞèû¾N bx^­Vƒ ��ÏóÆ?bÔ¨Aƒ@OOO__Ÿã8íííÏ7«8µíB©ßñ6S³07ljhÒÎØX“U—äÐܨV«D¤¥7Š¢r¹¬ãŒ`S¢(4KF¿H«4ȩ̈RÊjµZ.—µ´Ø{ Y^3¶ &>)Í»½Uš¯‰Ô-?†R½§ä_¤ŸO;\5Vj$)¥{ĸê!RdoÑZõ)U½çÓH¸ðw§ó›Îï^ÖdTî+ª\>¯¿õ1_ÈA±·?U?=ä»ï2ïº3÷Ýbâ~¿Š?ë 1ŒÞ¹ñ€œBˆÌ‡/›ÛÛW¸²?'�� �IDATò ¹dYç Ù ¯�Ds®¹à[ï;{µ‘ Ÿ;’'„Πï[×W‚\¡¡}E6î¤ËÎÿ•'í>aʱ÷v|ÆžC6õË·ö÷k~þúÇÏûâvÚ'»öÿáï¼`—\?·¼üÖ³ó¿;tB[~ØÖ_úØZ‘ñ9výÚ]Ó/ýäÐþ^ª–Þúµ_æ.¸ìˆM1ïaGÇf›e³m…-œ²y¸~y?Æ´ü­'^Ìí{ÁNÛNÈÝjòñ_šXz`öì 0m—AË^\ÚK´þ•eÕÍ—_YºVAß+K–m>vê࿽_¢õÝùHÇáG}صèîù…Ÿ~óSSFÝá¸_zX÷í7>¦ý7Xnø„±£6Ûr÷Ó®½êDþÇ_ß§OyÈ„ëû¾ï9 � šõû»ìvÔÁèûÁëï Ž¾ü'Çï4nøˆqc;uÁè·ÝôÚö^}ÖÇ6=q×3®þÆnso¾õ¥`ýƒ7ü޽ì²#¶3fÚ!ß»ò”ì½7Ü÷Õžºá¦·÷¹øš3v›8bø˜ñÛâ`«OÞrûêý¾õýC·è¶íñ_;iâ?zl ¥{òÀ¯ï†ã.ÿÑÑ;޹Ùä§ Ó¡;ÆOÛbôˆñ»|þKå_~nF¼žœ!cÆï1q\'¶ìj�€™!#‡wŽ˜rÐçÛ\æÆ~hËÑ£¦tÊÁcßž9«—ÔÂ?ÜüÔfŸÿáY9tÜ>žû‰â£½6²¢·Þðê^sÎÞ“G¿Í¶ãµ>Î1yÚ¸‘£§xîÉ;®yáùEú�Äò#&ŒîêÚlâ¨|wkF� 7høðÎa÷:ýèíoää©cFn¾×çŽÜ¦÷õ™Ke¿Œz?$o6a³Qã·;ø¢¿µÓ¬Û�®‡l¾õÄaƒ6ÛíÌ?¼9w‰ô>zô]OÝóà‚àÕÇžáû°Ë†ó\F3~ûë—¦ÿ‹ ÷<jøfFµ1��êGZªOÞtëÊý.þÙ);½å>_½êË›?}ãïç…oßsýÃOÿÙ¥‡n³ÙðQÇ nÂØÑ?nÿí¬Î»ü„m†unqÈ7ÎÜyñ#¼YסԞ¼ñæåû]òóÓv8rô¤í§v[¿H?!ÚºFvuÙåôw÷ sÒ¶ãFŽÝå´ã?Ízm^díB­·ŽûdLÞáw¬ë^úò­Ÿ^õíƒÏ~°?,8@ÿ&ª>uû=½~çÊãw7jÂN‡î=ÙÒnmxü»©y•µ¼­õÞ^³÷¾[¼ñØ“«I-{ò©âÔ­zžzòmEï<õØÓ>ùq~Sý›ýŸ¬PðÖÏ›ñÖ:•1eó®ôÞ7�è÷äßKŒb @E‰ÇÁú’dˆ$µ³.QCÃ8[(Hª¤Ç>Ú ¸)к™ÌYsÚ‚,L޼:Û?éäÿúGwFû6¤¤ÂÄý˜1H»‘×Ï÷Àã"ðÔOxhÄüÍÀœªSíl`Pö -Ý %~2¥‰€ºE-Žõ0z»YjLÄhÖà=€†Òڮ̑™ªúòírRi ÅÁ×6ÕPJ`È„@Î$‘BÎ]ßokkkëh÷³´æm,º‰ÇUÃöóSBeùíQÛ¼ED%¾>Å@:N[)U©Tº»»uM8cMÍ>5*Œše@*[TtÞ¾”üÛ™)ú¦”Zï¢$ÁS«˜@˜’ e…?4wÞü¶sûëAqÇauòt8¢`Œ#êºñüZ9‰HqΑ»Ž›ñ½l&ßVè2¸­£]xn6ŸËäsÙ|.[Èç ù\!Ÿo+dÛ ®çiý£ýV˜àÈ㜠.‡qÎ]G8ŽðÜžR±\« ÇikoÏd³Œ9¾§'Z{i­ÊÚµkµÊÆ÷ý|>ﺮaW©Tb€6C�bßó/cÌÎ,€&ä!©t`×hBƒ„Sb ˆ »^žÚO‡ îúžçyœsàŒ»Žð\Üõü\¾Ée5O0IÈGÈ#ºØ#K"c: À§öò}_kµ¸U$µÑÙ²KBXšVLR'Új­Æj©S°á½½xY£JÚÚôZø 5?¯zÆLÐ5Ö!27%ƒZõ½9 ‡óo;qŸsŸpÏïÏšê�f yV*cïÜb_ òmù~v·hÖÿNåÂçæ/Z¶ø™ËÆÿáÈc~¹€~íK3f̘1ãå;>7¾­«K‰oD©¯ìæó.­½÷û×âß:ÄÆÛÎvç?ðêŒ3fÌxîTÈA©¯dw¢G(?ó­ã®qõ?.Z>ïásÄÕ‡œ~÷&B›ÊS·Þݳ߉‡tnªZ ß÷âû^_U ªÝo]óÉœ˜4eÒ†íœ�PyæÊ-üÌÅ'Ý`µæñg¯<êÆÏôÚÓfnHªŸ¨5ÅõÐÖ52nÓÙ>¨V\ׇ#w7döâÙ=•Ùï›vúŽ[-{{ÖºêœgW ßmlçûðA¦µÜù·ÎO¹£ —/]c'Žî›0¢²lY:“µ7iò8µ|i+-GôÚïïY²ÇÑu!€ZµxI4vò¤Lã-rù’Ùñ†Å¡AÆw¬Z²"\½d™Ülâ¸Ø+BŒ8†–/]){—,é¶å–ýÍ"õ-_ÞÓó‡ãº|ß÷ýÜŽ—ΖÝëÖ©tOÞ^ž0¶?— 6hHGX*5y ·îjÃ)œµu´C¹\�ÀB{*•*ÈKWÔf]ºSÎ÷}ßrìôb÷Úuª‘Õ —.^ž;¾é¸]ïÓ ,Ëi!Q­Õ0ºS¥2�Ú P-WúeÔ¿†Ø°-&µ­\²ÜfN¸èÞ¯¼ý¸ÎöÂi_}:R¸;ŸxÜø'{Ï’ÚÌ¿<ZÞç=úSÛÅ$—.^–ÛbòèF.õÃÙ·lY_ׄñqrH>vâ8\±ty¸tñRØ|Êb¨åŠ¥+ªO|ybÖ÷}?3þËO…zÂ4QïÒ%½]ãÇÕ»J­_ÔÈM¯­Ý¯–õzmmNµbÐk¹u´Ü' ¡Û6jûc®¸äâ=·?ñÁ-Æ÷_AMÄ}+V–;Çn–iywB­VÁ¿›Z­²VÔÏÞΦì¿ß˜—~líšÇ}{Ÿoíã ýÛªõ?ôòæûjÂû›}Ð!?ûÓÅ[>}ƶ£·:èëÓçõwúhµù¤ïÙÀ7È{¥�Ñ„ˆÂ¾Ã�'ëßzð°9™m_wý‡1-BÊyë�ëÀ·î¶¼ˆØ1‚…XZ>ßc#+‹<>¼¦”¾Áœ€m•Ýs5ÿê›Sc4•úÑp«Lš™ ûl£GìŸEæºm«4c±ŸbŒ ÆÍG`…'´<š€vGá €¶m""%U4‘”"¥S çy®QFQDéèý)�0ŠÓX‚ÙZ0Rhúf°Vj:”åcÏ&(B¿1¢zê{Ïó²ù\5úJÅZf“q%É@bk:lþH©Œ9]#[Zì¹0W Ù}f©Œêí+ÕŽñ»¶—†¶´ ÁëÔâ$&ñ)¥c¼.$–¸Æ-4¥N$"f†¦H)¥€ Î-ëA3™L¥Z•RæòùAƒ€Œ"/Ì:B�"CÔ]1®,®ˆ¢dìR UeÓä# Äjæ ù¡C;½l¦…aiŒ*U«Uî:D”Éd¢(Z½z5"¶ jCͶ6-–µZ­\.—Ëål6ÏC@BÐBKq×:«›>%Y Ã[ntØÈXJjp€"$%gŒU«UH¥;i&= kŒ±HÆ*'SÂ@Åõ8ëÎ5z²‚ б?&ËI‚ËëiÌ ˆ’1FQdk @YK¶qIšµ m³’Nß ˆˆ‘’uî56ht^ û²†åœüݼ¾ô "ŠìáØ A_ Â÷NÝ_xØW÷§‡¾õÑÄ^•™²õ„õÏZ¡öË ˜=ã ¾Õ‘[´FÈòÍ{ÿÚVŸ»gŠèä _;êš}{±üÅ£ÇnÕß¡Üm¦À/g¾}f{ò­×fÕ&3EÏßÿ—·Ÿ½c²¸ iiÏaÝ÷­¸åÀ©ÃãÿƒiÓ¿ýúì>ÚfBiÖŒùù©[ááË÷ܳä#W1F�ŒÞûëçíÍ雹ïÆ]ì×x¤´çå/lôÎ&ŠÜrÅ«·½àð-7æÙ¾vÿófÍûˆÿ£äÊáí=7-ÿóɃ[¬Òü;¿ó?û°Ÿ6$S|ãÇŸ|¥¿VYg~-Yµ\Á– �ÂåÝë½Ü ðÁw:땿ΓóGì¾ËØê´§_üë<ç…Ïü~´«ÿ|ףޏh;�|äèô×y‹#è�P]4EfäÈ„Õö#rÙÛË¡sx')µÀHøò]÷¬Úëâý‡ �°AC`åÒL´ç‹=¼üÂüU 6c�´~ÁÂîaSF8]å‘|ÉüELô Z41Ž=Œç† ñÖ.[^h]�ó]]…¡Çß´ð¦r†ÏÛ=:¼ —,X7Þ²V[wµQ*!.tëÞåítæŒg.˜dßL«,VëÞY~uÁj£[N_ìH›¾ÌºF·bTcñ^AI�úaÔ¿ˆ¨géÒâÐÍ»¬ñöÞûµSo?xä­ÏNkë½åÐ1W�€˜ö¹/îzõ5×ÝþöŸ‹_±ïÆÖ'Ò9¸òÆòõì)ê‡ ¼0rd~õü…EÚ¹äây iÄö#ÄÎ!ôÔÒ:úYѬkxWîSß\pÿÉ­ð9æ‡ Ë®^°¨;ÇÛ¶~QjÑDMÛô·u,<ó‰ÖûdÊå‚èÝ'¦ M%‘Ë9ë׬•�õoA,Œ™_5o~/íÜjs©Å*øwSëUÖ‚úÛÛE×ᇌØÿ¾?ü!š½Ë û~¢¸Óù˜~ÿ̘C/žÌ9¯³@çG¿ô«ÇÎüþË¿:íðN¶Ý_hù¢›Oz«ƒüË(9ñ‘¬O@ý"Úg>Œ%§ Œa,IƒN1]ý{þÛ§·æÃh?ÝmS^¦n Ú×u¨œœ)À’$Bÿ rÎÞ­c»M]"4ÕxÜŽõc.’B €Y=iÉ+»·Ð*”à;c|£FcrK2¬SI<³0íŽÙvB°ÎÙÚ NÏ&81Ô¶MÇ÷¸Ä‰!s„r:Yöv6¯`€v2ù LbjB›eK“qDOIZÈ$RJ2Kƒηòmmƒ† nkkcŒÙ>h)‰Ry1S=l6~ê‚ 0¡ÆÝÚ€C´l¡Æ¨›òŒ0ÖZ:n땚y‚U $eÝ55&“´?QßèÖB)C+-ˆ¹.rĤhž$-èœ Æ”¥C>ƒˆ"%%)î?›aœQ(•ò³.wDý·:àðÍFŒ5|Ȩ®Á#;ì2ªkèèa]cF =|ØèᣆuŽÖ9¢+ù6|ô¨¡]]™\V"IRÌ�P*—cY•J¡`ÎX.—Ëf³Üš9‚1†2 3žgæÑØÆcYE4él>kŽÅÀ[»$›Jþ%²E)ý[_DÆ0QMJ¥â"£DÚÉ%^†ÀP•ÄÛ5g(8cÇžÔ ˆB`ÈÂåÜ‘DAQ¤”Òj>!„çy:‹¡Ž4ÑïÝÀ¾ª§“˜“tÀ¨ÃÅDbÈö1ìjìÔ"2éKŒÛ…镽CšS/•RwK*RÊ ÀX§1!¸àõì0ï‚䬟ŸwcÇWsáö^­Z­Vk¡ÓŽ9ñC3®úÆ-¯.š÷ø—ü.8ø”ƒúñ²g#§l™›q÷ O¾ÝW^7ç7?´bâ´É È‹:ìä}Vý梟?»`á ¿ºèºE»Ÿ|äDîpóºd‚jŸ6bÜÙO¬j<ûº=ḑþðÛœýö÷_üÃsòžàã¦lÏÝ~ÓK+K¥U¯þöÖ§‚É[o¾Q?�ÈÏý}õ´]?\G!´ú/ßüô±?~¡Ôï3ªÖ»jÞsw]òé}Ï}iÚw~qöP|îGGqÑ£k[ïóÎG®x+ٰ׿±Uû1Ó{h©#Ð/P�@a-ŠÌ)…ç©Usß)Kp|kÖ.Z%aä¤=w.ýõÊ—f.,¾óÆ?o»fAvÿ©S}�Þù‘}s3þüüí&NÍùÛì=üÍ_>7sø¤'¼È@+î»óéqG|FGˆ³qGœº×Š_~åûÏ]ºøÕ;ÎûÖŸÚ=u/ +åÛÏ>ðôo/™ó—ïã–õ{{@²!ãÇçç>xÏ ‹—/øÇk‹«Á wM_¿ïÑûi`ç'Ø£ûÖo\òÀ?W¬xëïOÏÖ^ âCÇŸ²Í+?:ûš§ç/_øÜu_þÁ3[ž|âNî >{8Ýñµ¯ÿaæ’%³ï»è«7U>õ NÌìñ™ƒ¼{/¹ð®×–­\ôÊSÿX©¿Š0›Ë„ófÌìŽ2{žpd~ú·Ï»õùy+V¯\ôúKs×@ª'Cö?éÀ𶯞û –/_0sÆ‚ÞMúânÝÕ'öæ?þÃs¯>÷ʇf-YµzÙ›/½övf58;{ü–/^ö…?2gÉŠ%s_š³i¶5ì‡Q}°5£ÞQñõ¿>ôê‚¥ _¸ùüËŸ}äÑ;;˜Íeå¢×^]€R T­Z TÖ²QÇœwtßÕgý¢zÄIÏ€Ztç—ûìoæDèe³°üõ,«’YwΟ>lÔS—冖¬ZòÚã/.–b‚¿Ç)' {èâsoyiѲ7ÿvÅ9WÏßí”#'¹“þôÖs¯½à§O,\µü'Ÿ{«¦m õ×9;sìħ~pö¯ž˜»lõª%s^˜µBYËìqÂQƒ¸è¬ëžzsÙòų_}s­×òEG‡fj½uŒ8°å>IÝ/ÜsÇc3.]òÖów\øÝ?‰ýÛ­?}×�½or>´ÛNåû~vÕo.[±lÉíâïyê‰#úÖ×<þæòUË.·ò¨¶Xÿ{Ûj•շǺc{»Øî¨#F<üÍï¼øÑÃöjüÉÃw~îâo><æ3GLI‰smÁ O¿±²·êŽÚvJõö¼ë„-¾ApÃ!Gï‚ÌALf‰´õœê6sdäi`†}JK]Ñdn¶!®9G¬bÙ(¬MU)1²¥²ôö3lL}„‰ÙÜô6…ÇÀçö µ™TSÎ…–ï²{k[#Á:4ÛÚGgûxm_ìïq»6‡ahÊÜÉ­Ê diÛžW*;zŒ”r´S½ncÀ$€a¤”$…ˆÄPÙ=ß°ÿ@«©„&©°)5––dî4°Ÿ1&\Gg­«T*RJ7ã;Ž#›L”iÛn¶ðØìÒh\çÏwÇ÷}ß÷=ÏÓ�-dn &>Û&<!ÁnOе^_:zßf� q£ý"}¬Ürö2ÄD’ÒÄ‘¥[±—×Y9tÿ©(^w¢Q*¶ë€ŽZ0ƘàõD:nT¬ª�Ž#|ÏËfôŸËf ù\[![ÈûmùL[Þ/äê?9¿sX——ñµRC’ e$!N3‰’EDO c, ÞžžJ¥¢1³6›Ûêã.aOqóÒ¨ÏK\Œ ŸenÖ -QúSýv�p§­­M'&dŒéT…z!{ž§Ãìž!|ßGD3¢Z­j€­Q"Dß÷ÇÁ$©éÜ-u¯Ø´Ub«X{tö¶lÿ6í7/ÀÔ6×›Wºc[¤í[ÂYßãIäÓ.êQpö®môÎߟ™ÓóôÓòMíß´†€oyöm7²òÒ½&osÔï_p×Õ‡õ—��;ýéïÏïºïÄ­‡vŒÞã¢ù{^{Ç…Û5Âv6òøë~w^ÈÔ­ö»ºrâí7|n“<òÁÿÈw~Õί|ù#[ìtÆ3Ó®¸ûû{ä�ØfŸýõm'ÈëÙ|Р‰ü¼tÔ-¿ùüøMi.zkμìæ[Xa¿ªwÁKÏ<3cI“wBÁ#gmµÅ®'^ùÒÈ/Lñ¡¯í˜�*½ýgŸyiA:·þ{¡Ü„#¾:¾çÆ{ÎÚõÚÏ~ê©…#É ð!»7¾z×_îþ‡òvÙú“c–ßòÝ9=XØãÒî\týq7Ÿ÷ùçWï´×ùçŒö�ؘ§Œ®àÔOŒÉ�vŸ4Y†cž<ò}ØdÔÒ?ÝõܤÏ|f«X–ؘSoþã×F<ø¹'NÞ÷âÿÕ}—í›ñ©úÆ-Ÿß}ˉ;ú§!çÞýëG1�Ÿ¯^~hïÕûn>~û#¾÷è‚§ïüSñG}"±?±ÍNùÍï¾4ä/g~xÂæ»yçüÈó<Óλû®Sá†#·?õ°_T޽cú…Û:€ƒúùýWnÿêW÷˜4i×/?3å÷]ûéNtðOïþîV¯~}¯Iãw8îW¯•Áõ\1íØ/Tºö˜‹ž û\qÿ/>¶à‡‡l;vÔøí>ïž·"¨>ÛØö™ëî¿üC3/Úê¸ Ûtþô…›æܺ«'¾ù™wLÿbaú»O=zòÇNýÙ³kTšÕànÿ?Þy2»íäO;ùc']ý|:¸£5õèS+F½_R«ÿvÉ!ÛMœò©Kï}ÝôKvñ:ä‹'¦Ÿ|îýüàK~vÀªË÷ÓQh÷•WÇN'–ÈíyÖiSh±'~Ø�PïÌ}îégf­ ÿ‰3ΚôìÙ§ß¼¢hÖ]f·ïÞ}ÍÇ—^qÐä±Ó½üÙnò<ûeBv·Kï½aÿ—í7eÂ'ÞÓ~Îô[OÏ@LýÊ7Ý|ì6c·Ü÷¿¬ Ï÷ê¯[IîßüãmŸ)_wüÎF™ö‰/ÜøZÑîXaïÝÿ›ýVÿ숿õ¾§Ý<+hý¢ókc»Pë§Ö¾ù—ŸŸ¾÷´ ·;ø’ÙÛ_ñç_±É±Tô®‰>éç¿<¼øËC·»Ù¤ýÓ³ÍvãrÙÝ.ýómGÕ~sÜãFÛá’¹[ì´eâÅÕ¼ þÿQ[«Ufmu!Çþöv±õ 'M+³½ŽÜ· pèGî•·;騴/ZõÜ5ŸÿØ„!mƒ'ùçÑ^yÆÖïöüÑꤵØ{#ó“>^á®Gí¿¾|BlUF"ï€IHDLŸe’®?‡œÙ@Ë´€ý`?e�븙x‹§IŸþMÿÍ92î¿®ˆ¦“�gN†¨P‡Ïš#§BPVœ¿L @b†äܬ³.æöèôáÞ0GŸ×$ ’6”…F]�Xˆ”ˆ ÉÆgO�”¬1~\5yÂS’¢ÏÃpÎ!Iü‡VD}Kµ‹n“C캆!pæy †aè8N…™F€J)Θ”Òw\Ér±¨" Š(’¾pÂ04†t•¸Óè?RµëìQ¤º„–ó y³Ì–7eY}•R€ù¾�‘’’ˆ Ž‚3Æbwkdl›J‰‡Eoö<"(ùlülü2RöUf˜_oÄò©¦FpŽPOW–œØæS)#›F>•Rv6„z÷¬Û€1�`zÂhUôS €sÖjŒ1ŽI臎Þg(ÃÈf‚þ[’â(8i™Ñè·Z­ÚšMæqó/°Xq)„ˆÌu�jxÊ ÃÆIÊåK—9\dJJ�Èz~E(¸”²T*u $<EQ©TRJe2�ˆ‚€sNÈ9çLp&8�(ˆèdd§ÄXÞŒà�°$¦�N=6º6™)´Pé‰ÃÐu]] aPGG©TêîéÉçóÕC² ô~+„(ööé&“Éär¹0 £ –Éd´× à<Þ™¥”ŽãèTˆ:1AÌjJCô¸Ÿ‰Ï‹J"´€AcW {Kûq݂鰹¨wc"Òß ¶¨›©kÛ¯“BnT·VÊF.°þ, ‘ÒŸFQTwc¶ÚÒgÅ]ùïvøá°AŸ¦¿þõ¯íííÙõc¸g€þMôü³O}�9_ï•ZxÕÇ·½óÀ—Ÿ¹`‹ žùÔ¢«öÜzú³?{LÿX¨òÈç·<9¸öÍ›LÎ~Ô»äÍwø!yQ^òäN:mîç_}à´Ö.öýSyÅ[ËUÇЂ[[ùÒug÷ç}ûû7¦nø„ÚÜ“ÿ{ÚTV ©AžƒGN{2þvÑu›•{zH®zøÛGûÎ×^¼óèaé³Z V/XTËw¶{ríì;Î=úçãoŸõÓÝߥR­[ôfŸßÕ‘…Þy\tì…ÑeoÜzhûÆŸ ÿzïõ{Xï›>˜_+¤~¿Aþöðƒã'LÌd7”ôAI©ˆæÌž½Ï>û¤?Cüãôé|í|0Ô5 Bcn‚:B¨#L@ ÆUü¸…áµÛ‘>_)^ÏM`“î‡j4Ï"¶�„6¥¡ rìo¬PŠtX&ogŒmQß ��Yhi"Ì+´ Nc$û«¿¬±DBÉèqeI‚8ê™!*�F$ãpxäŒißJ‰Àís²é¿$"$ˆ[2Ê+©ž uŒæ‚c¯±”²$®AO“Qy˜™ÒW+Um†UJIˆ½² L\؈T;'‡AP«ÕtD"Î\tµ]Ô&T¤ÐòziE)™!K5c~ÛV¸¾®”Ö ÕÿÚ ]«Õ¼Lµ¼”’Ý“JÜ•mÐb¹•7ÈZž86î²k@'v¥³“«Ù¸ ›XAD,É¢‡‰ ÚpÀL„Ñh5‘O»?vR6 ¥&0÷ʼn’à–¨‘$ ±c› Dä\¨d5o‘ß~µ1#"!�(!\Îy(¥vñDõJ„vF€H…Œ@¡@ííí2ŒŠ=½ííMòá�� �IDAT:¥?ùèsÎ=á„AP*÷år9Ž(¥4ÊE×u£P�é÷1Æ82ä,Šêó¶\5H «¯—xq’1¼s]õ�  ªwÞ̻֣1Îõrp|»N–bwR±X«U3™lGG{WWW©Tìííãœ9ކàœwvvV«Õr±T«Õ(Ù@)­Pð}¿P(è)(—ËQ!c [$OI)Â$(¥»WŸ)içÔH»ºù1D ""TJ2£OiTÛòfïüÍ ëLŸ&"µ97߀œc¬ÿ¹pc€>¤Lÿý?¶=ò¦IyµÒãwýYpÃ^2/¿ô‹NýÍœ•=¡?|êÇO¼áW§¼[@ôÆoÏ8êÇ/-]_u†n±ë‘?¹åÜèZöäÿœþ¥¬þO%µäÖ£¶9ëIwÊÜ~ýQ›¢#�‹ï½ðЋ]øN ;&ì|È×wñnïÚQ™Ö<öƒ£Î¹û­ÕEÊo¶í§N¿ýgèh€þ[ˆˆxb5PDXÉ8Ýg *¥@Çü"–)[*49ãJ)dŒI¥#‰!ªHš\‹Í‚ÆÇˆº¾:!Q¢½¨Ç£j§å*§nÔ4x%0d:vB†QýŒHÉ A)Ehòw›± !ëá9G2ÒY‰s.Kz"‰±†êÜúàÍcÁâx�ÆôAçAgtf"R‘"R(Ä$IäB ðH’”H 'EDŠ3¡T�ú´Ì9W�D¨€‘’ €�uÒ‚xšãô)_!ïcsÓ ç„a‘žƒÌIÝH’ð\©P)Å„Ë�”…±}˜”$¥dÀã:µp¸ ëaÆÄH)¥ Ô™6ÑäNo…Z5uÇb¨`ymáš®¬I)xSÊ!Ö0RWè�¥H1d‘ «!2ÁjaU#jÎP»ŸH2QP#ÎÇΤ” HQ ‚T9=¢0Š\îrÁ‰H†Š„ë!J¥’vq×NõZÂ#%AE sð‘T±í— GT«UΆL* ¤xÝ´+ªì„,ÉŠÏ’2ºWA8Ž«Ã .c&Ò^s˜RFaù“ëÙ×\"Þ¨‹Ñ0ŒbÎÇ{ÅQöŒ"¥¤x‰«"*)´÷„ñ}ZÇdЬü'RDÚÍ„” PÃÕ…êN‘ît=©ªnVI¥¢ˆ)ÙÑÑÖ×Ó‹Œ*•cÌõ½³©HzŽK\AX ãÂÏpdŠG!"'©â”€J)©H)b‰Õa@fOÑ ™AŒ1¤”b‚C­�(ÂPÖ@¹®[©Ur~†¬–+Žã¸®[¬TÇ ”ô3^$þrÑÏer…\†íííåbYJ¹¾{™+äòm9Îrµ8¤sHµZfÈr…lum…»88¾“!¿R+ûn&—ËÕ*•J„¡”’ÚÛ ¾ï—J¥îîîZ !�‘H%™Ð»A$Ã0”€¾— ‚€±PR„ 9çL ÿgœ1&1 jŽã r-JE@à{^±Xt]«•j>×V­E®ãׂŠã8¥JE)åû>rN‰¢ O£b ©¤”œ; P  ÒBÌ#"WxQEthB("@ˆ¿\ˆÅ ”"!8©«wh€6Llóóží=oSn÷å§ú¾¼á{rûß°liêÒÞ—½¸ø²÷Ü=��;\ø·y¾«GZôäÿœ6™Õÿ9ä~âúËßÕlÌçêþü»{ ŸtÚÝsN{wϤ ‡qýkG\ÿþ jAï~üOÒ¦|ƒ¼Öu<©1É«z®†Nó’²îÒ«Í̂ƀ†L››Z ØoiÙ?Hüáø'Έ è5Ö'»¦óЉt®ÂØŽo^§”âÈ´CAbÐ#ÇqTSFÓÃJ»ó Ú©GIå©kF¤ˆ)%1�bŠDŒ1…À%é+ ¹£U3Y¥ºÀIPÚE$aˆjt(°írИø�Q6¸ چІÆ G!A2+÷ž¾Ù¦¼„= €#㜋$�Xñ”WsâPÑ|^·E¨ùïä_²ï7pÑxeSâÞLDÈ0“ÉD*Œ£÷Q5 0 mmȘÖ]�G¦´: ±V«1m÷FÔAÝFÌÔ‘ã8˜@[AEh¿ÐWt ®ë͈É`çòHEbÛScóÐ.zokRL?1‰3‡DIÔÌjD$+G½ýÑþF¬ççÃÆ{R]¥&5–yoJmË2ñ&ÜÉ–„ºR�jaà ‡ÇBÎu‰ž(Õê:2e8µ&Ë4+I Rq. YGJ)$Òe‘±¬çKˆõ)ˆ=&1 C®P¯¼€ˆAPuüL†Bxȸërá8ÅrŸëúÂsƒjÙó<!D­V‹}y‚ª—q;:: …!2ÁÏeŒ Ï•  IÈP…(¸ãpÆpð²ŠdEÂŽð±§··­­­Z­öõõe³YÏqÉÊL¡E___©TÒN=:ë'cL¯S-„ÐuÝZêÁ:Žãg½,ËÄÙI(dŒD2:;…RÊU«VhkâÂ�d³Y)%C¡£½ôïZqÏ5™,´HÈDàMªÎøÓXñ̈”`{Þ)qlADΰ˜ÿ5G8BpÆ™’ Ýi¤”Œñ$pa€h€h€h€è™¨^Á[Yã„íVJMѡƩÞnL%Xãê5DDˆH»hL¥Í—Ú\õšvæhT¤þ6Ÿ¦þ¶ïÑ‘¬F @�¤cb[®(mú&k )øÑìÜž´Ï‰HÐ’XÀc2Štp}zÕ=‹#r1¿Ív±lPŸa¯B 4êí(Ý«KLÐff h4j¡€ DÍaóÖ†…-ຊ'opfÍZŠ`<AmYjT´ÈÚ`÷–šÔ¦ÁZ­¦óÕ€¥”âDœ±r¹¬("�Çq\ßs3¾ñ¤ � Q )º� PhÇ„(’ˆŽë€”Rg/0W/Ïê‘Øëͼ¥Å°ÙÈêu줭fjf”ÍX[bÍG©õ¢g,n›Ûv#)NÚ”Z˜)„=YÔ¤iªßoåÕ·{›ŠH2È¡ñ½ñ Ö”â¡iA* R'E”ÂáV7yC`ŽÐ¹ˆ€iÕœö}0ªŠx0½WA€‚ÇsÇâ%¦=ùµ‘Ô+ I)¥9)ˆ$2ŠdHÀ|Ïw…†¡ËY„žçé<ÇaÈB.ªÕZ-$¢ï{ÙlÖϸ¡’®ëJ©�H’ ‚ ›Íêì†APCĨÔt®!„NdP,׬Y3a‡×7yH6™¾¾¾b¹¤Sfê\¤ÚM �d’h”ˆ„p•‚(ŠteDt}G„ˆˆ ˆã �X*%e®­À“M uAEDT©TªaОÏ0Á+•J¡P‚Èõ½P…¶×€ã8Úó«Z­Ùh–C}3µZûú "%£H)U«V•«<׌¿”ôëm²¹PÚ� Ð� Ð� Ð� ÐÿŦóØdX?ÆÇ¹ÐNçÖáÌ>kGî¤9²{]3f�ëèoŽƒu$C¨¬Ôßæ6ãØo=¥¥²¯›Ö°•Ú¿ *b#­Tæ­ HÀja˜Šq5½EËÊJVÞ8ÿª”º®ÿåÈ6D“jLëV&H¥”$%ÐÜ_gQ±42ÄDÑ8–ÇPÓXq5$Hñ¶YR½ªÏHãgmO´í ó ��ÂPG”×AÆ1óõzé)i†£ºÿ¶Üš”ô\ëK¾=4¥”ãrDÁ…Ðjb¨” ð±[­ÙÞÌâ›ÕÒ�fÆ ²ûiÂ\×Õ•íL¨‚~Œ¤Ýa±›<䣕àƒã½í9²åÐÈ€ýxKè¥ÉVÕÙóÒì dw›Œü-'Zí-f*¡‰R«µ$Ô¨TÒ=Ôî2”J)—9 Qg Ôz‡¦­‰”TDÄÅžéI70I_BÉ@p'­€Ä£ QÇ_`œR$ÑÈJÊú¹J¥Ò^$¯Z jAÐÛÛ+„ð—ˆ\×g„J+<’Œ²™|©RDárÞž+Ï­V«D ed¤Ng=Ôý×å3²Ù¬#!D6›ÕÞ.ˆëº¨þ(¥jµš©ƒ E”1&„@Š£{\ש¢(BáH‘p¹çyaXÓ+šq--Z¢b áœ<X)ÅH'[!»Áºuë‚ p»ŽãT*Ïó�X¡S¨Â°V.—Ëår…¨”Ò¹`t°€‹öK²eÀ!ãwS—gÆ8ç®ç9"ö‰BEœ3D SM Ð� Ð� Ð� Ð�ý‘ªë’è}Äu¯Î~LÇ6ivò¯7jÝ`Îý6`³¡‹PèˆhH.J¥3&Àiú 1ª5Â@h~Kªß€@@ûiK‚Ÿc<FL&ÀLI54¨;õ.ó¯Ý£  ÄíÜFìi× ´ þ`™Cµ'0g,Îú¶i3Ý´§É¼ŽÿŽf°I_dÚÄ ’J¦â¬ó�ÚAƒÙ@Τf jÞ˜z¯-жðhÒ¼4®2 ÈuµþK‘¬.îN¬#àœ+"Dq]­¡²Iȧq¦³ÇAÆ41œd€È¸‰aQJ)Šì'«Ìa˜h ìŠFaPºjòâ4isŒ,¥V¨í bL­ÝšvššÕïÌ@ ʵ“žBãû�aZågúlúiº«Q”BDÇq²ªã4 êµ €€¢(’aDDÌcÀP)ÚWž1Œélü¤PJéyr.Iê‚ ®ëráèqq�FLéÙg ("(£u=뉱BG;V«íAP-WоãV*5$–ËdÃj ûúJ$°oG@,T*(WjµZ.ÇõˆªÕ*ÜMì¹ö¹\i+•Júº:::zÚÛ9ça¨„|Ï“a¤£`|ß§$þ%Š¢L&Ã�kµšŠd&“áŒGQ¤T¤”R@ºŒ¥©”BÇ€ $#` ´órÃ1óDpT¢„)Qäp‡9‚*EQÄݸܘ/‹VÛŽY Ô˜ÃÐ(ì/ÎãÜp„p\—qFDµZ BEI, cqòË÷*È4@4@4@4@ÿ ” `�HGÔãB›1�Ô¡{rOAâÔý%ûf¸šz“ææ6›¯0+Ó{ê6£bHµãp.Ž1 �œsJ€œ†1¦vëºÍê‰f4k œ 5Z’”~ÄÄè¿Mu:H šÁ� ËŒiJŒíëD„ x0yïmdn¸Z÷fgØò¨l ƒi�™ñ¡€F5j´´é¢íñˆ’TídisR£hZJ`À’Àf 4( ”íBDˆi�-¥Ô>ÇžçqÎkQh·iú W-KZe€ˆQiðozk›gÝ !ñ)ÀÆ J)Á¹]+.õ‡¹Ó<n;Õ§ÆNMÚê'xÄVF4‹tª)ÃÕÔê3ÐüÞÔDØ3˜Èæ›íåLqeÓB½o‰–ƒ•!�¤Öè1dÈ�1"U j•Z5—É"‚B`I‰“¸M`œC9"%Å¡C ‘1&8"Jª¤TœÊs® :²Ø…3@BE ‰)¥(¢J¥1§,ÕÚbIr¾6–/Z¨òímÅJÑw|Æ8'žÏäB7Çe¤j²D$s¹œðÜ #çvÕyRc™LF/çZ­ª³cº®+ƒP³·X,V*Çq …BµZÕ{”RCýØ )K©Éd2ù|žV«ÕRµ'Æèˆ*¥$E�Àx,ÿz1FQD0Æt­Ü!˜ñ|I¼±Z­ !òù¼Þ ‚ *ƒØ›Æó<ív¤WMŽãĺ ½dTÃ'"`u7£ÕÅ$¨‡ˆ0!¸âB0ëf•ÆÐ Ð� Ð� Ð� Ðÿ2)E‚1lD„:»¹0ˆˆ%éÍÀ:Á3LðOÝ#€ø¡Œg®uÊh¥¤°Ÿý‡ùÈÆŸ©Rÿ‚åQoc¯jh11¶NÆ4öEƸUÞɪgÀ­Ýÿ0)M×Üy»Ï1<ÐU”Σ†:—{lÝ‚¸"„�bÈ¡³¸ƒ­h°Ê6gL·MìÎhloaý#@ÀV~¬ .ê·4\h€úÏÁL¿~uÌ¥$6XgD#‹R¸1Åg�š?m‰r TQ½'Ò,�1N3GÊÈ —c¥�TÒ‰ãÏC)“öc/ŠØÅ#î?P\‰ 5A*Κ;xë¿ÑrŽ +mÍ”ŽÉ¸<@SIˆÔ*ÆÄ Ûf¬q40ÒK–ÊÉ4›Z›`‰q3ÃíûÍÍhåPLMPcšò¡ˆ˜ˆR³|ÚA7Ô¤âDÁ”TQéÌF›Cšk/' á8J3– "TÌRJF„ȸ®œ¢yÊ@0bŒ¹®Èçó™L¦F½½½Õ äÀ‘‰ˆH‘ÎK ‚ˆ “Ï­éé+3t `8nçÇÝlåš•D*9ld­T]·|MG~° kÙŒ¨UújµZT3ÙB[ûàR¹O£uÎy†•J…1æº.�KºÇ<7ãy^wiç¼£cp__©§»/ŸÏsî”J•|6GDÕ Ê¸¾BÎTRƒS0!Çi/1¨fŽâ5«4J¦e˜ˆ)¥‹52ÐáfÈ9ç ã„°@À‘)B@Î8*T‘RLˆHÉ ³~&ªÜåœs±¶4Š¢ÞÞÞR©DRj‚ÙÄbù½±…¶L급x\RFQ†a- á褆�È1Û{k@K0@4@4@4@ÿˤñ`ìNõ‹DC&S×=…oSNÈ6�FË’lŽé±$ešÛØ`SYú¬øR#¥ b3l°; Ö9’sǾռ]EÒÜ)•2‰Uc(9€ê#²}Å�°’ÚàV¤¬,tŒ1Í€†7lÑí++ð¤f…-ÿjçÐìMShÙÜ Q'Ò_0C …š–MÞ>²¼Kì4c×ó0!ƘÅidžÄñ•Ædxdk,o[1„V¬¾-™º mïÕ²mêA #ŸM\I<X¬:ZÒ ëº�P­V#RžçiH£9©}¼‰H{0ÆÆbo ��huÎy&,œs.#iüðm/Óa;B=jîTeHVklVEÔÐËŽq0³jªy™›µ”4lT´$ÓO7š§8õR""]CT5¨'ô¼)©jüi²Ï)¥"¦\!rmÎyow¾ƒ!  Ù†¢0âNœÆOJÈcL‘J”h r¹\EzÞ³Ù¬B 0Î9ãs\Žœ Ü!`�ú™‰[LRŒ4‰²bÖ¬Y£‡vn7mêúžõÜwßéŔ•‹V<ùÈãC ƒ»×¬°’iËc¤Š¥ÞL!ŒE’ò~FE!�AX©T I1`ÔOÚG`åÊ•®ëï¦yh9/—Ë:0¡V«yž‰\I)#¥c¾ïû¾Ï9ïíííëé%"×qôÎCDºZšP ÒÚ-Q<RÊ0ŠÏe2I"€Zœ@*†¡N¯¨”Š¢hݺu…B!—ËiW­¼Ó¾~.×ÞÞÞÝÝ­Ó…˜%¦w*¢z•]²t¦tcL®÷\Î9r¦³R()‰X¢mhÈ9@4@4@4@ô?K›«[˜`" .˜äŽÏèÚ d,zúnû5^bIÂ<m¬ÖösÊ7˜D¡£a ²±“�˜ä �IÒSŠ74CR Œo�Þúü&.»œ+¥ÄÙ­ô5!„p]}Ì%«È"6 ÐHLŸYMH�@š¼Ù€( K¼´I(VDèlR_wV/K&„Æ¢A9Žº|‹‹ Æ 0ÎÝÀU¢ãˆµ�VY“¹0Îùo%ÔcAª·iø¦1ÎófÈœs6¤©7ÊFþ=‘¤H;Aõ3QU+U] R^'�D†¡Î‚ÖÛÛ«Û ‚€¬6ž$Ë6n„»Žž©\.)Y«Õ¸#jµZ­Vs8s]$É£’¢B©”Ðy:1FŒ1@f˜ ½VÔ¨R‹+ãI* äû¾žG²œS´0Õšæ!w=)¥N^ÈQ4Ú4}¿.”¨kÑQÔê jR0Ù¶•YºAá:Q¢$…€ÚÝy\²ˆ´3 &"jL¸zt¶^ÉÞ(ñð<Ïþ-/;™lyQdOºµC¥½WÂ0Ô•µ» Ä…­þØ­EQDR !„ç‘Ôáñ¢ÐÑA_Oo>Ÿ‚¢°­P‚@FQ$É‘ ��y5¬2ÁýL¦§¯—ƒÈòÀ¡…Œ1Æ#DEÔ1xв%K…œ;R’ëxaúÙ\$I8> '.?øhË·9mþÐ჻†ž4‘¼ÕÅÒNÛ~hÜèQÝ}Ý+׬Vù‚?dè µ‰J Wö2^Xé-Vûü|Áó3 TGGGTµ2‚jµÆ¹Rùm9“nPË[6›Õ²ã8=ë»Çñ'—ËÕjµ 2™ 肵�ˆDQEmmm…BAG(•JŒ±L&S©TT¨2®†!ÖÂÀu­,#¢HFˆÈ8ª8Ù£¢ˆqÎ¥ ™Ã"ŠHw¹)åû¾pX¹R,ärZ°+•ʰaÃtȃþÊÃ0—ËuwwóLF'e”Rú¾ªB+(x]MÐèíRWEÄâ †Àõ¼0 C!œ0ˆ¤Tœ3θ296®ãªÓóÏ>õ.î }09ÿÁìÕ�}ði@rè¿’þ³û?«·ÿnBÄHJŒMÑ:‘wìb ôû¥¬v6&„F'pQ›G¢HÚ¦uÛØkßf7ú“ê­²ëÙŽ ЭmrÎÛÈY’˜¡É€M°Ç�˜Ô[R0ÞÚØd×ÝDj†Ù”˜Âl¼dþ&«+”X‰µÄR°´SnãÓ /+Å`sß ü3˜P)EÑq»)Ã^ó")¥R9\h ‹Iàgj°µäLìÈ]«™1êtƒ‘eÍÖd‚>4.uGx®1¢ ×€b±X.—=Ï6¬³T.׌ȬsÛæF#ö6:°‚2êÊ �mí7Âf3^9º5³|ŒÂ+5û)ïûY3XHÒTOöy­;°¯˜šå¡åz7"Ѷ7‹Î´Ã,¯›Ãq ­Ö”ÍŸÔŒpΩ¾`ëî6Ô¨Õ2ÏjuXìDCŒtþb¥Ì)™Íçd¨Ê• g …¨Z*9ž;xè/ã+¥"%ƒZªX=„ˆÚ!E¸NÊJ¥¢Ë–J%¥”ŸÉJÀ ”Ìq‘ áùÀ]áùLø}å`øð¶BG—_$²y7ŸÍ ~&ãd/ doµ�fóŠD$¹—mç²$Ã�x…qWx~P)ƒL6_)ƒZE‘ïû™RªZ­jE§RJO´†Ö®ëf³Ùr±DDÚ‘GßqNŠØ¯DkÚÚÚ´^@§fT2$]U$’Fì³Y?“ÉWÇenû|5È B˜Ô�@ìÁ†ˆˆÙlV;FUJe]g�¢(ªT*¹\�´¾IëÂô’72ûÔHÏl]MPw•2"ª¥L?%„£0’‘�ÁxÝ LÊz#ïŠ>²ëÇÞÃSô>éùgŸú�rþƒÙ«úàÓ€ä Ð%ýg öVo7L{øÁI;úˆ®ÿ& Lj‡×SBSø1éØMž%ÇÍ:ZnxT@ŸabõçB˜[(j¨ïg ö.f¬Û ¤&!ȉ;yƒ‹;wËhi )$O�*®¶m޹)8g•1Ì‚…¯ŒF@%‰ñ7Àî~?m× àI¹ñÇžÏqjöÆ€Ò#Ч@)íŸ 6âµ0KT¬äÉʹ¨¯f»‡DÄcË.(¥Â0´mÝØ@ €Š”ö%®V«&¿€ -‰5f×ÓàG{£Ø¯ß¯Á2H�”H@@)ŠB¼³n–RŒ"Ë%42DDV†?Ã+UÏö‰f`⣀„óŒˆ$)�È€3휢-Þ¶ÙÓa{ß4«KRª[$ú[¤”D’£¥V‹ÿ…•0ÃO»ñTŸõ vŠM{*Ë{ÅFk©;[’=›Ø¨¹3içMFy§ "lL«A‰†Ñ Æ„lÈСA­¶få*É\&*é8éâ IÔL&“Íç¢PÕBÊ^IJ)r‡qN2@Çqz‹Åõë×û¾ŸÉdÖ®]+¥ÌÚ¸ë <”8¯„ PHň\¶ÐSíŽ"æˆ,"ƒÈ'æ“•ZÀ~¸ªÉ° qÛ3èJļ0(sá)`ŠÐu<* Êzr+•J­Vó]O“p!„¼R©ø¾A6›íééADíÀOD2öì¨ ¼·Ø'„èèèЮþÚho–?1Q�ƒZ5WÈfó9­‘$­Kb³ˆÅ÷�‚ŠóA2Ôß. texj‚ @Rß©²žï ‡„¤|×­[—ÍfÇÑE©TÒšA3hûgµZ/FÂÍw¾È€1À²˜#° Â9@4@4@4@ô¿@ZG€ç0¤$È€„9f)+0»IY´bµhôöyË `úS§yóKRØŸ¬Œnæä§ÑÕ­lôkÚ§ƒ¢Æü²nu„Æp}óÛàã*Mx)…®ë`¬ŸBqýA›®Ý6ÃÆ:ˆ6Óê °”†6B³ÇÞº‡@ËžV»JyªÞ7}¬×ØpW£VÒNõÀc®ëº®«+@œ¾•Ë唂’|ì-ï×ÎáÄ�� �IDATÏ †&)e­V#"Ï÷uɺl6«£ +•Jooo{{›°ÙÓ‰Öƒ1H ‰|’eÿ‡Äûˆ¤’D¤H(2il†5‡Í^“PÀ¢1óà’õb!–dXHéVLo±Q{“Q-EÂ{Ã%f¥±¥¬"ÊJ|’d°”-ç±ånCDŒ™¨£†ÅHý,%ûE&~�†ë*÷{zzjaຑæ¼ã83¹¬ëº}¥bH*›ÍsGè¼ýÊdßdL¸Âîˆ#Šå’³/ ÕZP B$týŒ"^¬”¸ë’ba„ÜË ?Pœ‹l–ù>s=×åÙl6 kÀã8¤PXV)AธÏäË*”a„Ê÷|dŽ$dž¬Tûj¥ Ë+F¡ ƒ�cw!ªÕj¹\vGáJïQ»ëkfFQäú�!r¹œçyAè@§¾¾>½«H)™"Ƙà\«¥”DQH @'vhž)DÁ""Õu:J‘’Òa<žPI)‹ÅbôÿØûîøºjóýWÒ™wy;^™Î$Rö,4RVÂL @¥Ì² e—U Ph@¡l(B(›B#d/²c;qÇë®3$ýþÐ=ººÃ ¥tüúõûñ'¹>>GÒ‘^éêyÇ#ß7lCTjš¦eY%%%ñîna,SFµµQJƒt™LÅE5‡1Ì“‡/<Ë ïtƒ>é“>é“>é“>é!œs¤|V±a­©±ô Ad ÔÒ�òGÌú!eÊ@^ƲÌE—¥¨ÐE¦/½Lí΃ñ d’¢\#B¦0ÆÖ‘ Ó�…Î=ƒmÄAJþ‚„:2^@bB ád §«“Þ0 Îu,çAôìÉw%|yŒ‹¾Æ£¼!ŒWF¤Tš?„Ðà�Àôœ!„)º¤Á#-›/Hô˜dïÇn¬ë<0;ó)BˆhÚ625|ßO&“8àäÝ.ÕfKSóÅ¡kH‚U8�xžgš¦i[$8ëŽ:uÕ”œŒdÉÏ3NPÆæ(eXÑEFæÔ=„ ¤‰àŒS&â Dƒ…Hd. ÌŽ÷xnØž÷^ÅÞª¨À[՟샵z¨µô¦Ÿ¨À®”Eæ–¯¢…6UýUµw–“oV@€æÙ€&œ·:A9#SFc@ˆqÎ(ΙOMÓllllß´™zŒèZ:íDÌáÜqÃ2u]ïèè¶=‰ÄÄÐ]zBhDÎÇq˜oZpžN;eåååfÛ–öDÒíêêb8®*11Ö ˆQ]G¶Á,“[:'`…¬”çº�QÛÂæ†F 0CfÚ‡ˆkƱiê:ñ)ßÚÑé&S‘°)À<!ÄCݲB¶msNEô„nšaÌ#âûÔ÷©a˜”Ràc œ#„ Ñ(õ=Ï«ªª ‡Ãœó”ëX–å;îæÍ›“ñ„°úyž'ú �Db‚ È„) XfÝ“+0ËZ qŽA !ÖUÆ3ùG†aõüX4…|J㩸mÛ"÷!Nwuu%ãñH$"ò)Ô%}»6¦ì4 ÖL„P:íPJ MWŒ×IGÑÒú¤Oú¤Oú¤Oú¤OþïÑäÃ~MBô<Èšç,Ê”‹g¤Û?€Ðx£ö«2^Àh� 4 Ì)÷…8ˆ æbÓ´D ™Ìy…~_\Ïì5��”dpqgcDl4⟠)içßOÔÇUKZc¶^”9×�ó\ÀBÉ•N“Ûq Ú!nÉ>ÌÛjKƒÊùWù²Y/1Áˆçø!àDlò-$Êå”§Ói긾ïû®§ë:Á¤(7h±a"v@º,SÕÃ,ÈäYÓF �4]7 Ã0MÁñ.­H‚q=ÆL)85ó\âR·E¸„ð9«£¯*¿ì1ƹ¦Æ™j#ƒ@¸bù‚ }SgJºV5Sš`$(VÈÔ-T³<„ß›ÅAm@$ùΆ¨ú£&ž-�¸’¹STçÕwᢀ«ý£.Aj=²R„V43³¶`Ì Y1t×MYºéð´ „0˜è™ÈŸQJ©G©¨±x/Çu}ß7°íº® Lݲe c@t³_¿~ Ç#a¬1D310ŸáHi…o>A€¨Ç=&!(í:Éd²2⎛N%lS'HP*8ºm^ØKw#l–‘H§6nܨ¨ª.KvwBÌP˜s®iæTÇçÀ3ñùÀÏæ ™¦™J¥\´‘„Žx*• …ΜMàK&N×umÓԈ滇àRJ)Åbºiä*yþ2«·ïû€3¹.! ™€¨D"aÛ¶àAàœ–””0'¶ Cœ\ØÝÝíº.¥4Z–%¢¸b  Æ(‡ÜELÞÆ²Ä™’ZhÀ÷}B0B˜"΂ù‚ˆ½°Oú¤Oú¤Oú¤OúäQ¸M€PÀw�4¤@,ÈsÊxU¨Às %Ê’»ÈÝÆA/@G!à¤B È ž‡à˜=(ØâQÊçâ$t„¶Öˆ–ò½lÛF9+€‹™?ÔN( ²Á8ëÏ•¢H �×� ÀZ#oC‚<ˆà@Ù8HSH#¥Pp@ §q@¹›øÂ8sJ€'ßW>¥ÞÀ9O§Pëg°+ÆCTæº.u=ß÷©ï3ÆLÃö}Ñ fŒ1(â—–Bñ<OT!ôA0®ñ‡ø€IFUÔ¼BHIi©8wPÄhš¦˜¦™N§Eãe9âì `ÌqvdÕÇb´p6·_€1æyªæd0?d5V˜–$ŒQ !à_Ì{#(ˆø×™B Thy°J>«BhÈMˆP¯ƒrö§ú'9(…÷jQÞ‹¨¶€<í*lCÞ[gž úSµ0å  õupa$È«Î0 NY2™%xž'òÀ²,ŸQÏóì Àº»»…ÖqÎ)ea eŒ! †²#ÉtÊs<Ó4·lÙê8N]Ã€Ž®nÃŽ`]#€Ri'lDuCw}3Œ²ªªpØf:º¦ØFŽhŒ!ϳ4{<²{z<×£kÔçÀÖtîùºi„0ºnP$޽D===Éd’Qê8Ž@áˆ`Ó4-’ñüóì1Š}0øO% Û …B‚t:Í}*r(„º ƒšÏ÷)ÆØ¶mÃ2uS0ÐÀ¾±L‘b_Ì«@Ù<žç¹®ßÑÑ‘J$Ï5,3™N»®K1M!TÚ¯Ÿµ¼ïiÔ˨R/Hp1Æ"êD<eºˆ.(Ì3È|ö ú¤Oú¤Oú¤Oúäÿ°pžá,Û'AOÅ� Mµ äÙ8ç"¸pÚf÷ZÒÛ/‚¸îûTî å6]ÝÊç!"�á¬!Éî.P„`ÿ'oÆ3˜Ž90ˆqŸ3;8˜Ml*ïç(œZ"WE ²=$—ã ¬¢feËëÂQ´Ç{‹«—Í“…¨µÈw”m(Ë9ç= û‚‚çó<ÉHª(H*a,ÃÅ8{Hžx\=ã0óF,s,"BˆRšN§dJ0qŰ"Š´Ÿô}_'ša&ÂÏîû¾Þ 7H–¶,K€ öòÔIŒ? F!€�€Ö5˲ Ó$‚æM¤XB,ÝèL$òò_D‡xžÇQ¦çUC•`[ÌàÈ&Ôdšá3Ž�ÿ„„_š ,UPaPF Òø’Å´A("G9HIˆ®ëº%í>‚ã@öä‘0Î9ÇHã˜# 1”¹æ$PHÊåìVÁ›:yÕé >ÏSBÙ6¡ãqù/�Cˆ�0ñYŒ6�æÀ8ƈ3Ρì"´Hö!Wì À8 @D48Ó?„ŸRË4ñrÃð\ÚÝÙYY^ÁK'SñdÂ÷}Ó¶’étwgW(¶ ÃgŒÒŒ• � Ã0m‹èš–mÛÑh´¬ºrÐàÆêêš·ß{¯½½½¤cb 4ÓÀ–t¼TÊéŠê„8ÜA^ {ÈÌ O€—†ÌR¢‡9'Ô///Ùšòã[:)ãHÓ±®1ÆB¶Ý¿îûéd£>g 8F a ”s.1Œ!N#KsÎ…yÎ6-ß÷MM'„Pql-¥žï! E£Qq"© Ûô)ìŒ1Æ(!ÄÐŒt:M›¦Ž5bÚ†¦i©Tʧ¾iš&)Jq±$ ÈÐ4Ž&­€‹Dô^*•êô)AHÔÅ®­­õG(5MÓó<A^ˆJ¥Rê åÁqœsLð (³f¢á”ð‚«3/8eVÚ „4pŒ?i¥Oú¤Oú¤Oú¤Oúäÿ”ˆè]@À��ÄÄî†yÖŸÂr]£ò_Ä8Cœû2ˆm>W‚FŒQ #B¤÷5g[†?:£>g‚oL$PιïS]×`?‹Û}Ï÷!.2ÉE À.õB€€{Já,"ˆk€/JÆl=ã—ýìx9Ï£²Á7ª›sÎx†FA2Éñ $ÏÛF h£M¤:ˆÊ$¥$Ñ㙸€L×p�„�1†¦@„ŽÅ "˜–£€±Æ4ƒÂ9Ê„°ÆUú7ÆG9â¾/œ v@Ä1 0ÆŒ3�N©'ÓL4„™®;ét¢»GœÊ®a¢iƈR ŒqŒ(¥:ÁõÝL‡`¬Yš 9SasàýÔtËô9ó´p úàsÂâŒ1@¦”ãXÓ|à2á!–e‰£ã}¢LD~5&œsĸaáp8™L†Ãát:-œ¨ÒˆÀKB@t#¹ÙœsƒÀX†€€ IŒ4Œ`a®2ˆF™PwžQZŒ &œsh”3Ƙ Yã®-cNX /íC8P„Ág @”bD1a™!„p$zcL�J�Ž0g âb()øÔ$ÿœsA%ÁÉãPÂ-³3„%„´‚1�¦¼Kb& FZ…J#„1¦Tè©&Ì”úBÑ™#˜qÆ`‚Ö4 ¥]a@@`¸`„�Ê3i)bì„#Úó<Ïq-Ëò]/ Q‰D°-Ïs9çÕ4McˆUÕÕÄ“‰žT2‰˜V¨»'�4bø5ˆAÆ9åa]×€`Î)`Î9$S) #hØŽšáPÒqK+«Fºi„53Ûá’DÊïé鉙–i‡(‡¶Ö†4T—ÇO—�êH§ˆ†  C¯²ìR@![Ó OQÏ.+£Ôëéî0MS×1õœDO¼$Nû ¨ïSaÂ8DjëëÖÝܼÎÐu�()áe¾ëºÌ§�à{žaè!Ïw… s¦aBŠÅb …’=ñxw0nšçyXÃŽ›ö�é†Î=Æ€sÄÒ^ZãÒA ÆóŒ9BHP<2a_C"ÄâY=…ÝRD¹®.‰pΩ땕•ù>³m3å¦ìpÈó<Ó¶�£D*ItsO&=ša^€LÜg RžSIæ[MLÇIaŒ Cj@)Õ4¢éºçQÆ8£Lãˆ`‘°ö¢Œþò¬–öIŸôIŸôIŸôIŸü‰Â8Œ¢D£fYÁ Àˆ’‡ªÿò€eô.C^â4œðª»¯ÐÍ(>¨±�(È –¨[øô|ßçAÈ1dpt&/qÎr¶¢.‘Ë�'¼ˆWæòD6@m^^ÏHQ³A±佚â=%û#+Ò#*+DÇŠ³�E÷ʧ‰œyÎ9c]“yÔyo- $¹‚ƒ\wÒŒ1"‘Œ#å Ú-  à‹Å¦nèšÆ(õÒ÷)â@؆i™¦ú"BOD¶¿ª'œº§v©PIáæû~:bŒ!”ú€‘Ǩfe•‘’˜Ç¨ã8Ò{dXS ¸ðÆsqZ»<ÐQònª"!"Š[”@að h±@ÕC‚(åÌ‚Be�%� Qg Dô eÔõ8xUÇðL |Wاòæ‘l¹l�sc-Àçù¨WäÈæ„´Ë…2 áÙˆUc Á±Î Ÿ°¸û¾/H35bhš†€0Æ|!”ñç LBµÄ0aŒEf»<GCj‚iš>g©TŠhÑ4 8í:i×úOÑ5M×u‚0cŒy>‚b8ç”3±¼PàÚ6ºÔ7C¶ aÝX²l霹s©TwwwyyÙÀ ÛìêꀚšÛ4C¶ÕÖÚBÓ ·§+„шý™ç&ãq�×Öq)ÑK‰Õ‘¦cÃ2]ƈ¡]#š1ö}?•JqΣÑh"2 »¤´<+Řtuõ$‰h´D¼µm˜ÂEﺮ̵Qs1 ‚ä<ZZÂ8w‡q” Ý–.N@0,ÓgÔñ\Ê}¤eH=¤¯ž ¤£ŸT¤H •®ë†a˜¦IÁXs¯££c]Ks{{GKKËšõëÖ·¶ttt¤R)Ƙ ;,,DŽ©°:É%‚É$2ÑBBt]øß4LC74`¢ZÖrÎÍí“ÿï…v¯ÚØÜ΀'zV/éJ÷šÕö¯ÙôßOýÛú^9zÿá[>yèÖ§¿IþegÓâϵm¿±½·ÄïimÙÚëùÆÿR鵫yº}ýÆøL~ñ·®øb^³ó}eÝkçÌYõo{ÿsu}ò?$ÜÙºaKê?ÝŠ^%»<þ—+ùòuyû| U¹Hç—¿ä™ @¦[ÛD æ%´+|”@e5 å¦3¨[4ÕF Dš Äga&PÃÅ󫓆biÒj @^ŒtÑž‚\<¬ZÔfK@%±´|Mµ z!G >�Fˆ`ÀÈgT^¡œQÎXn€%öÎ Àl TຮàGàJ&|+ü‘*ü“mV‡&{AÆ=X€$2-”Oiš …be¥ºiˆöHˆ‚2ÞcJ]º÷)b\CXÇDÚž‚ a&?�eˆq™Åm˜dYV4-))±m[P²]ã¹®‰DJKK…z†¡ëºã8EUšs.¬‰DB8*…™�çzÕûåÀåõ†ªBêŒP5Y¼…:w€eÓ ¤6Š×D"Ä&(öazÓ0QˆÄï �u~Q©*ÙqÏÊqÎ1Òòà7p„ ›û-Ÿâ¹L…óE5±ÉËë¨Â%"ïWP)ÙÕ<—!O>ÂsÍ"à\žµ!p¯àÓc'.¦R)A˜‡ !Éd�paÈt/͉ª€\£¡ˆØçœ{žW__ŽÆ<JMÓ¬ªªÚ¼yó²eˉDEEEmmmÿþýKKK Œ†ÜЯ*¬ë:çØg6ÒªKË¥¶¦…01Ò�4Œ#‘ˆ˜}ÂÒ!^PÓ´”ë´oݪ:ÞÙÓÝÓÓãy^ggçÚµk›››ÛÛÛ7oÞ¼eË–ÎÎÎtÊá „ çæ‰�€Ï™Ï(±!ã®®®öövÏóD mô}ßs\AŠF$÷§\E‘bÝ+Ô„BDþ}–1–N§S©”°e… …B¶mÇ‘ªòŠêʪ’h )I1”sZ`uU ZR1Pr ÿçMð¼g‹*vŸü$þ¦×.|å¯ó�Äßûà·7.kÿO ª3óO—ÝýWö†¨p/ílõXË´›.}CØü¡W Þ×·?ä–YÛEù½¶„­ùÓá#&?½á‡Ù¦þcÒkW»ï]Ø´çõŸýgl?ЕŸtÐåou~=æ[§{À/ž[÷o“su}ò?$ÎôSðûyÿ­35»<þ+•œ·O;¹ÿÀÓ_ý>s=(bëÕ˜¼}¾Üoãmoß¹›Õp{, X¦ >„ÜÃÔòQ1 …Úâ¼í ŠÙĉÊò¶ÅÒ*ø3ì€"Q¸‹Š„:*6€\¨ ÜÚª[¾pÓ‰Šåî"„�rì&RäNZ­4‹ •~P]ÙÙºPÖ‚ŠÁÂq”¿ŠBTHÆswäyR8F²çMÓ,--å×E #MœsI¨.$s–;Ï4^ÄG@1£’¬N ;á)y|ß÷}Ã0B¡0 ¤ÓiQ¸ú.…nÃP(äy^"‘0M3¯{ ß]ޏª„j±j#{37�€‰ § (cŒ¡¬z(OqÎyÎ3Á;D´‚gÂpò"qdSUGáTÊSQ¤À³¼WVß:OÔ@ €TŸ•m㜋P¦¼S+0¼À@#KJ%mrÍᜠ*:\؀ĔÒp8,4�jjkûÕÕZ!iDÜ,BKD½f£ÀÔ•y/‚…‡ßЭH¬Äq}×õx "Ó²Òž›H$4M…B›Ú¶lÜІ9ŽÓPÅt€°fôtl é¢>P_np@Ž 0âaÛÖ 1æ:~"‘`Œ…B¡ÒÒRáZ/-)·Ã!J)Ǩ¾¾~hã°úúþÕÕÕõõõ%%%cƘˆ?B ¾IuA£@)å4MC�ÉdR"fŸÐ%Çq<ꛦŽE£Ñ¨TƒÂÕLÕT,X”¯â¹!"9¨¦¦¦®®®¼¼´¼¼¼¦¦¦ººº¢¢B×õD"‘H$„yå~AdVÈ‚%Q*RÞ�«¾œwÁÚ()ͱ Wñ[þ~û)û6–ÛVIîÇß6cK¦k¼5¯\zðÐ2;Üoçãïüt«Òa‰E;°ü¤¿J浫Ž]2#u?:öæ÷‹8tYÛû7Žß¡*ªyØµï´æÞ@×>;i€9è¢ wX©ÅOžµïÀ˜mØóô‡ç%äÕ§/;¬Ü¶Jì5å¾/ºŠ ¦»ú¥3w(ýÙ#›Äß:ÿ2ÞÎY8ì Ow<DÛÞ»|ŠÝo]"W÷ÄÂ'Î=`H™m—5pî“‹ŠºÂ ŸÂ7¿}Þ(+:ù•ïåEý/Ô'/¼ê>iläyˆ5ÿùêCþœ¿eë_yá‹';”ìß'ÿ=-Qä{uuŸü¯Jñ¥#wô–=wáa;×GM3V?æ¨ßÛØgÚøOKŸ‘P´éÈ3Ï<b‡ð>²1Œ"[ô¼õo½'5 9¿¢b…@.®(º‰ÌC¡*NàJPº ËsÐTAƒ…Zb ”ëÞç½H¯¨üUB¦BX˜÷Y-VV Àâ‚yPü À C~;�¦iXiÄ(c°[yhBgdžaYVÆ"€"˜r†ÖMC7 ¬Ž@Ä ÈyEF €bz1â 9=Ã9G@€ca3Œgȶå¥åÑh ’L»å€ REBaÛ´4Lqœ§L´™ ¬ÍÔ S74L€‰ˆŠÌèJï:�¸ÔïŠ÷ttw¥\‡§œqŒ Ë,--¥”&“IÑEŽã8Žc|–ªJðÀZ‡5Mkoo]'!nQÉ Ÿ‘“Eª¢ì=PÌaâŠ�'" "'†…qÑA—fóÌbh–Ò $AÀ̆ut€q À4 k }S40?4Fª´ªç?–Hó +ÓPuÕä¤Ú ÕÛ@¾?§­ËL‡àµ9P@Lþ \sƒúYDI×´J¬¢k¶m‹¿¢ Ég~Ñ<]×#Ñh8�†@h{&é†`èÅ‚ÂH1¡d"J9ŽãõôôlÞ¼Y×õŠŠ „ðöoÞ¼9•JQJãqØ´±­$3�¼D²4&žG8ß™¦(eÌ×× ÅbãT*¥k¦e…"žëÛ¶í8FìyžG}Ý4ìH¸¤¤DD7¶¥™F0R0Ÿpà‹„"q@ ¦Ù–¥§„F£QÃ2o¨ C0M³¤¼,‚bPÍmT9 Cú¶ÅqƘH=Ðuâ8)Î)BȲ,]×5L¨ç‹¡ÄŠ™6+µœSy µÉ"GMN=�`œQFËB¾]£hÒÄv„wÍ{o^å¼pÞ›×4~uý”>r�€.›zòi¯T]õÞâ¹Ï³å¶‰¾ÒÎxׂ箞°ÛØ«>ØœÅÄlåý§Myµþ¦O[ÛW¼|bÏÔ“.{-³Ö§Ï>áÏþÓ,|ã|ãÉÉg<²6»§M~ñÛ‰WÌôô"-O~ÓÄ >Ûù³–ÎþÓÞó/›xíŒ�ø_ß2é—_ìñÀÜM›<ú“å×L¾ñSW}*¹âõ[&ï¹÷9Ó×g7Ù¥'>¿±CÈÖ¥S×ÿ¨Éc£êC^ó‡wÿbÿ]Ozd™‹é~~Óñ}±ë½s6nZðØØÅ—wÃg¹¿èS™?-ýÓIg¿Úeýç·KßW=ÿ:9é€Ð?ös¿`çÁV¿üœM<fМ;ã¿§%Š|Ï®î“ÿI)¾t䯓ZÝŽã/|tÆÒ5Ë>žºÿšÛϾ}ûq4}Ò'ÿj1FNºþ7ÇÓÿÓ퀼­—ûȼhuC\¬ 8."äîSuë²ËÃZy°­7dΕðQÎ?Ga*feP[¨'é39ÌHáP؆¨%~µºÞZ’ÓѹÍ+Úɲ| «@Ù«…H ŠûZvÏ’Gæ0¨Õe1âΫK¾,CÙÈuð"„bgŒÅ“Ép8\VVÆëIÄ9çV8dÛ6Êu¼Ëvæ%ùË—B{\íaιišÂeÊ$°ÆHÓu¤‰cBY‹ÜQq¾ðg 'Íju…Ô3‚rÓgd×!V¤RY¾,\bY¢êÒ1Æ2lœ3J©ë Ãpz«‘/j/qž¥0Ǹ H¡ Ô¥PÕ?©å£\Cžþ>¨J^xB^Õ²ñ’ëD,2²–m|€ÀS �¢¤b ÝÐ4ÍÐôt:í1ŠŒ„»Þ´,Çsl%�� �IDATSFƒRêøž`?ɤ”®‹F£ñ®xWG·†5ι®›¥¥¥#FŒˆÅbnÊqÒž›r5M‡Ã¡P¨¬¢|ÐàÆeË–Ç»ºC¦Å€Y) …JlK�ß+‹FL cÄ4Ì÷1ÏIÛ¶-A}ß'„0Æ’Ét8¶mÛ4MLtŒ2Ì ”Ò–æ ]]]®ëB@!p>Ïuà }„b9Ž#THš½Ä\p]×cT˜ ¤É®p¤@Y"Ô—”yúƒ‚•Ü÷ýt:½uëVÇq‡h¤X¥E\ƒˆ‰€^΋)\Tåp«ªžQ „ &#„1Êf@vŒ L½ßEPÅa·?{×ÏÇ65ŽØïÌ &ÔmþvE'ásùfÌÅ·¾Ûàa]yÃdýµÇߨÂh˪Ä~w|øìiµÙŠüo,±÷=æˆa¥‘š='O_µª=çMYË+Oü½ßÏo¾xÿÆ!{ŸwóYƒ>yü¥U™/×øÌÎ~i̽w+lš;ë©§[Æ]uó±Mw˜pÓÕ‡myö‰R�‰% V×tÜ¢Ñ?9á›V®Îñò£M+7¼êí7.¥œ?£‡JJKKKKKñ—wývæž·Þ5±]ñ§ŸÖ <åå-PçÊõU¿˜öáïö1ä3t͌֞|þ¡ƒK¢ \vÑÁmÓ^üÒã›^˜Ü¿ö°W1(ú”hú‚»Îº'vã}Sê·3 ÞìO®<èϧívß™‡M{üÎmœXÁ;[ß¼ê… ö¹ïÔþrëíËZàKîÜç±'¿b��îúÇÆ=ôÀ �Ào}úðï}Ïí½°ï =<ÿ¦>~Ò~6�ß2ó'í9 fÙ•#Æ]ôìÒ��ßüú¯öU_jF¸ß‡\ø—ÒTâ~rñ`‚²'Nfúí´ì1éèþ�’‹Ÿ¾p܈J[7Âw>ä¶Ù�€ßüö G©‹Xáš&\ûÆZyxÏ7œ}`c¹m•ÞÿÌ?ޏk^»füNµaC·ËFxÕ»ù%©%Ï^tÈŽõ±PéàŸ÷ì2iÝÉiI|Áç4¼2d…«FþÂF�Þ¼;Ç6Ä,»|ÈÁctÑC'í=¼_Ô4BÕã¦.§E›Ê7¿zñ~£ÊC†nW>êòßþjÂ.JìPåˆÃnx³hÛôÑí'îÝXŽÔìtÌïdØNNWmýûÍwmˆZvIÿýoùÂ�ÞñêÙ#+Bf¤vçà1¾õo×2f`¹m˜±!罓ꥣÒ3n»ã Êˆ©›eCÇ]xÓU“öR²Kxñ+ëè6;êû‰?oêÑ» ©Ž˜ºU:hÏo}¯ÕÏ»#ýþe;ו…L«lÈÏ~…Þg¿>è�À7?qDlÔU_æ?•/¬íÃÛNؽÌÔ­’Úûœûb3ƒÞµ%½üÅK6ª*dEv?áö6e_;¿¼ÿŒý†”Yº­nÜý„óÃÀ[ûú5Gí:°,mØcÊszòtÌYùÊ•GîX¶BåXF{©ÈŸ{ÏQ»4ö‹Yº°ßY7\Ú#ªÃV´aÏ3žX› ô¶t®“(ÚtðÏvZ[±4ÑÆa5ÿM†¯ÿ5I<ltô5_û��lÝÔ¢?¾g à=sùÅþƒK-«lÈ·ÎÎj¶? þÕR0Ëz•µ·ÿe|là…3<��çýsªO}=�àÍüUcÅ ÓºÕÇ“ =cï%¦®vò3­ ÜwϪ­;û=÷;,>Ûøù‰£¤s=ˆb'•Sª‚¢ïR¨Zœ|6¯´<€‡.ò,ò¢¸! Àr7©™òFŸAV”qŸæ"UÛVTòª–ÍËëµñ\‰…V[«‚êÌg‚Ez¿JRÀPÎäʙϨÏ(ƒŒÓLÊ4UÎdŒy”z ÞæAò…4ŽdáhnEv –c¾‘ùÀ9{},S‚ ”á@ç8f€9¾Ï<ÊÖBá¨e‡9Â>ã£îxO*•Üã²^Œ± *àœSJÏu<×gT(‚²ÑG¡€uRÓ4˶3lˆš‰ti¤ €"ÜËêÐó€c, B¡Pww7 |ãyZªêªª{ªVƒÃP`Ý`‚€!yz»)S È5FäO""#CŽróð´/4G LÈ´›qN3<ÁïEÞN)H³º1GHxù)çÒã/>Ë+â4^Ú¤d·eE!™ÚE‘gÞã ñ­LÅ£ ,ÂT¤i�†a`ŒC¡Ö5bè ¸a™†ap„xMÀ02L9!¤!ŒÊ,,'{âºnî¶Û£G5‰ÃüB¡ëº‰DÊ÷ýP(‹ERéd"‘زi3c´´¤ÄK$ÒÝÝæ:QÓÐ�§˜ �‡MÓD˜§Ói×÷�#¢iöwwwÇãq9ÇE⌰Ò†¥iF®m�€ οà2Á©T*NSJ]×õWtWx YÀû€¬“œg>ˆx¢¢?(ëûÏF‰> ‡Ã†a` š†EZ`q‡z¾¨QÌ&Ñ<Ä9Ê¥¤UX�òH1+HËuÞDŸ¹B= �(±ô=$=ÿ½?·%‚Ô’«Êšš„1À=fºxþrPùÏ®¹ç×G ‹¨Õè»öS2ý¦ë^û¶sÃûxtåØŸç¹jý¥ó—À¨Gj��dؘ&séüÅ>�@rÖoùæ~She‘¹Ä6.\¸uÀŽ£c� Ü4fh|Ñ‚u¢û¾ÿ¦'®½ãƒõ]«§ÿñùÄq??´D}ÎÞçâ{®›¸SY± r¶úÉß½Prîo&Õ �À±Á»î½÷Nu6­éÌ?ÜzúžÕêɶ¤vÔÈèÒ7_˜Ý–öÎ ÝTo[×â!»a§}öÞeP Aѧ�À[|Ïù×ßrßI¶Ø®Øñü'¦ÜÿÁ©—>»eÖݽÜÇzfüæ×7:óéÓþðÀÕ_~ð‡{ZÜXýèá©ó»�]ݲ¼ÝY5+`-–wTþ‘ÑKYßIºÞ{þðQ“ö±€­üô£oßpØC³W,ýÛuC><gÂU3�<±ê«9áSÿºxõÊ9Ó.©ûà¼CÏ{5ƒtô}ï\–H¥RÏc�Ð¥/½¸lï‰GÕcàï\|ø¹3†üæí%ë×νŸÏoe�þ¢?N<á18ýù¹+æ¿|®õÜäãnŸçßúÆ….›3æö—-Ÿy÷>‹¯þ+[8@zöã'?:÷¥ùk×/~îxôÕœ5éœÆ'?¾æèsg ÿí‡+WϸaÀ»çœù§oÌiÉ[qñ£ozgѪ¥Ÿ<uùÕ�ÈÀc§~°xõâ7/){õœ _Ï�hËW¬ØéÖ¯Ö¶¬üìáóâMM¬™37úó·V´¬›óÐßÞsçÜ]ï|wÑŠ¯>híï/¸ëk€­}ôÔc§öLzfþÚ…OµéŽ“¯z'žßÕ@—Þ=鸇ݓžürÅʹoÞ7y �PxK¦}½båìÇ®½å´›>v�x÷ÒOf’ÉÓ—5¯_ôÞ5ûš½t”ß2ï‹ôYÚܼø¥IñÇny·þšWç®X0m²óØù¿ý{jõ=…µ-ü¼y×Û¾Z±fÉGSÝrï1Gÿ~~.F1Çœóô'K[7~ûÚϵçÏ»zz‡¶Ëá‡V}ýöûí�’Ÿ}ðEÉíTü€é@販žÚuÌc_¬Z¿ü½óªæ}ñm7ïU[RŸþ樟¿Usù›‹V|õÄ1÷;åáÕ XË3gù›eûþqÆ·ëW;~Ô·³oÎ5ãz î˜xò ‘ __ºöëûöœwÅ)·~©¾Júó&œújÙ…/Ï_µ|öK·9˜¯ئE³›÷øãüu-+þvQô•[ŸCg>ûÅ·ËÞ½¤ìõ‹¯~i WV¡Þ–ŽÞÖIgú‰±PÕǾX{íOÿ¯Ê¢ù¿!=¿üèKfïðÛ¿/]ùõ ïYÀ­í΂µ̲^ðw±µ=Y¶ÿØ·|þÙJ à/ýdV[Ç—Ÿ/öèêY³6ï:v_% ·O»æWŸ4Ý3oK÷ƯÝ0.ÇRµÝÅgß ?€ qF•hgvsð£¼-WÞ¾ ÜéÂmË•\b ÃòêæA”W2–Q®?X%Pnº¾š"›gYPƒNå òA`„ŸMòÿV§Ö›÷Êâºê”Ë{\­Z-' Ê n^ÇÊûåÑ( |{yqÒ1¨6UÝF³€ˆAö›œäÊ *–"  …ƒ)1½Â±Ï�Âшëº.õce¥•••ŒóŽÎήžn×É�6µ%œsŽÖ5]×£@ø¦iÊžÉhEpìe:Ö £¬¬¬¢ª²¼¢Bd2 X•N§Å醂”.N‹˜sõuP�OI§Ó„X,ÖÝÝ-ÿÄsEŽ‹4ÁrX  DÖ¥¢zϨn[UµãP  ÙÙÇ8cL˜Hð.†¢òPI9ƒÔW`J<Nay0^þ)OÃ{ë©o, %Á +<9"ïz›w(  _aŠñRç‹ZŽ AaŒéº.NC*$.EQ¢Á¾ïS׋Çオ §§'•J9¾Ç9ïIÄ­­›&qFÀÁš¡–)˜ä¼ÿkGÃ1Û§SŽçѰ‰ÅbŽã$“ÉÒÒÒöööD"À0Æáp477/Y²dÀ€Ôu]Ãói:ž°4]CÈs cæ¹¾ïb „àH$üÓC~)‰uvv ]ݸqS[ÛæH$jY!Bˆ`â�À=ñdwWOw"©:ç\LÆ2óT„zIcLœ©É97 Ãqœöööd2iY–0@ „„mEÓ´’’’X,&âòBýÉÞ«wFdqÎu]·,‹RšN§%0Xð€aTÄ5Hëz¡œ«œÐOȉ†1Âáe”ñ†u.`Œ³gáþÃâ­~î'>RuÓCŒ$À“=qŽDD H4 ñîÞLý¨rÂM7ì½âÁsÇ o<ôÏþ19�ó-Ö4MÓì=o[ÚÝ“°¢áàX’p4äöÄ]�ñ}¿~yÇß^?¶DÎ$oÖeÃLMÓ4½lÊk[{Žæ4¢'Î:õ¶KϾóÔý‡ŒšüjõiçXñ]#Îg÷Ý»à'—ž?FÄD¢êÃ~÷Êó¿Þ;ÜËíÑ#nêÂÈsG ‰Eúí8þæÚ5Û"Ý÷ʦß|Heo•²æ§®| üëÛŽû.î=TZÚ¿(‹Ž˜8j¨×ÑÚK†1oýö£/Âã~½ûÎC"•;Œ<ù—‰7-r£M{—µ|ÑÜÍyÇ×-é¡åɯ›Ûô|½¾eèÀÑåÛ¯½Wáï<ÿné1“ö4€­yé±êλ뚟j¸ëI¸ùèÎgû@ÄoàpÍõýGìÿ‹ûï™B^yø5±ËCX3,˲L�ø _|iÕ~“Æ× ào=ô‚{Âí<y÷A5µƒV‰ïHÿ›§Ÿ·ËS/øñІÆ}Ï™zõ~KŸxêK·ã­G_†É·ÝvÜÎ4Møí§‡^}ôµ-ÜùøÑÇ×|Ã}çì×X[3`pMAlzÆ“Ïl:ôÚ[Ž^Õo瓯<µñ›w>ØÌó[òæÃ/ÁI·ÿþ„Ý×õ¹Û¨~��—nÞP;xï³ydä«Ys3»{½bÀàšªÚÆAU¨hS=��dWÔÕTÕŽ:òÌ£‡ÒðÀh¨uäéã®›¿°›³Õ/?ñqÿ³wÁ^u•ƒ¾â’ŸÆß{gŽ—ÛÕàÏ}êÑ9»^qßÅl¨¼Ó΃…=ΨÙ4¨®aô—œ¶ÛæÙŸ¯HGj‡4TW÷o¬tï(��d–ÕÔTõk{Ö »èfÝÈÑꆎ=sâNÝ æ7Ó^;꟭¼ÿþõƒÇŒ¿î±kw_øØ_rÀ5 Š¡;6ö+-ë¿ß¹Sör—/]OÍ}N8®úãiomæàÎù`&9øð½·ÍséÏýËÃ_6]ö§+Ƭ¯é?¤>†�x/Ú’žñøS½áîÓ÷Ü0âàËï¹hè'½¸Â[7í¡¿•Ÿu÷ÍGíÔ¿¦¾q@yÆö¿yæ/ w¿ôöSvêW5|ÂÕçî±öÝw—gm(ΌǞh=ôÆ{±oc]ð]šŒâ‰'´Xu]uõ€½Ïš²¿ UÃvT7pï_œ¼¿pÞ _Y…Š/EÖÉŒ˜Ç<»µ³ù«§ŽmûÍø ßê öÉ¿HÒ?3­ûˆëï<y·AõCv?ê ‘ŠukÛ³à_-…³¬èmÅ×öô€ƒÆ _òÁŒMœµÌø8>z‡®g¬c|ËÇ,i:ä'äñì7û2š+æ~»•EjG ­ÎÿÂÛæâ�½~ƒü]P@/ wGYš.ñ» 3òœÒ\ÁíL /‚‚ ™Ø·IL[ =±*`W"™Q,ب¥œ1¬°0Æ Ä2ÛDÕ›$Ú)±œtÿʦÊÍ4(ª+ÑÇ.ýl¨È~“­åÊYê+Ë^’]ªÆŸóÜ D ÊQÞfÙ9š¦q„(ç>cTiƒ@\røJ,yN¡¼˜y€rŽÑ ]×u‘¿ t¼SapΩ””H9i ¸¢ºª¤¤$NcŒ±F’É$çk„r¦ºë{¦mQÎÒ®ãs¦™1t—ú=ÉD"™D#¿nŽïõ$ ‘ŠÊJÝ4Ä飔ë¸Ô'†.³5M“‡`Œ#Ð"ÕÛÌs]W…(0’�*Žã¨ýH"•_í[Ɉ)ÕU¨V†VÀ§ÌóEÿ‹àq dF¯|*tàœ Û•j\ê”Ò„¡‚1æ¹>PŽ0Æãˆ#�Ä9PÏÃ�„`�ÎàbN3î‹lÙ8PÌp�À9¢” Ó‡ª(`—” \@/©<‚*)<¦ T¸¤ b!Hr¡ƒeš¦¬T¸î岺t:-´�¤YÁqD0Öˆã¹D×!"Ø„2-ƒhÉdÒ÷}ÍЭO%MÓ´BvM]­uÇã¥D׋CæE‚¶Ë šÌT*Ç“ÉäÆã][;æÎ[VVæzéd2ÙÕÕ•Hôèºîû>cE„ K¼oÄ1ŸûžW_]›JÄ©ï†m!žHöÔÔÔ ŒB„3�€hIŒrîRß°CžG9ç¡PÈ÷}ÇqlÛö9D4b`Œ9F@4˜2@]ÓMÐ ¢iZ"ÇLMç”™ºahõ¼Ì <‹ ND@ˆ2&MÀ(0¶Rå¤U\í/fŠøSÀXAÄú'g“ë¥uƒ¤¤iéžçÙ¶-SØlÛFœû®+4A¬´†¦sÊßõ�@¬ÿª5 c,ÈME{(eÔ÷cC7&"B*çm’ôŠ·òé)_²ö”i/^0Ú�ŠFp"ÏDçÆ{‰EzÁÅþÂ?œ|}êŠY+×´¬yÛà—'žøÀ*^vÌý_Î;wîܯž=sp,NÇA_¢'iD"oõ–ûÑ9×NPñ¶>æ²7çÌ;wîÜY·þ¤,†DOBmD4‚ 9óÚ“¬úÍê5­+þv±6uÂY/}Gh“úø©—º2¡ê»šp͸^[ЖpÓßÞwHX6jضýœ�šyçïWé¿K 0Ûüá§wNzìì}î?똙K=Îzy¶9Þ±êºL™z]I™ßÚƒêöT±hí¢®Ô¢ÏzšÎÚm‡–u ·¦ÚV³ßÀª"™·¿ùüûUÇNÜM ­Í­0°q`æÅ­ACjS--ùLÖæ°‘ƒXks1+‡?ïÅië8áÈjÀÚÖ®÷ŽfçÞB[×o Ò/c*2¸´mýoÓúÚ¿qP&*BØ8€·6o¤Ýë×wõ1¢´·Qä=­­]]/ŸTmY–e…w»yíܺ•å·d]³ß0d`o!¸¬¢ÔK$ ¢…‹75gŽc¥%L¦��P´$ ©Tè†æ Λw[–eY“§Ç;Û·²Ü®Ú¼¶54ppÁv;Û¦2”ˆ'ó•„靖±J$9� h, édª×Žúa÷>,¶q}«Ú9ÞšW¯¿Ë ª’hEÓ埸”2�c)' žñ—iëùo¿—<x½™í2B›×¶„‡lÈí¥^:ö´´ôTœ!‡$¡ Í­^óÚf:j9ÔtCó†ôG5†,˲ìÁ}쉻›×wW”m*/^Qnoš±+hÆbz:¥ Wté(ºNJAF¬~—ï¸qB|Ú3ý÷Æ÷?!ùÓŽ÷lؘ¬Øß.zw ÅfÁ¿ZŠÍ²bÒËÚŽGv耯þöAûæÞ[wðo®üÉê÷Þoëøð¯†ö³!ê7{Ù„»ÿzÈOÎÙ¹a‡#¯š¾¢·”¥b‹Oþ=Ûøù^"�6Aú” `Ù}K"ä._Ùfå\ɳ @.¯»,­°À†"Å­­þ›wƒZ”Z¯¼¨~ˆH4IRš« “ï¥V¤ŠlŒ ¢äEéU0RDÕ‚²™ÎEe9ñÒ*‘×9<÷|/‰cU#‹x¾ ðm»0X€ÄÈ—eA„6œ·Q˸‰¼¡A‰nœw@0Æ0/LtͰÌp,ZZ^PJc¥%š¡»®Ë8¾§™FÒIûÀA#ŒóD2™J§uÃ(¯¨ˆ••¶åsO%{’ Ü…JËËÊ*ÊÍíø^ggçÖ­[ãñ¸ ´/ÔžTœ¿5MБRjÛ¶@¡†aä… @.1DžºÊΑ³@þµÐú#„*ç>pÅTD Ë€À@&ç£4oi„d+âù”~LuX95@ÎDõC¡V‹+Òü!;)åéÊÊ¢Po~f¥…S4U>(à¨èXÓ4et€Ä!M?Dâˆ_Åy{, ýWM?š¦Y–¥išX@‡¢‘Xi©ã¹¡H¸²ºÊñÜD"¡ëºlž|uNE" aÆX"‘hkkÛ´iÓš5kÖ®]»×^{ýlÜOxCmmYYYÛ¦MeeeÄÐ[6nhiiI$¦ŽGÁ€P{G{,sÆãñi5·¬{ÿýO=—VVVrÎ]×-//¯««CS#!!O%“É4G˜` �û¾ïúgAÆ!spĈ«/†^P rÎC‡<šÄq·—£ ®Øy_êJ•·çI(x^ŒK:–gRˆÏóD¤8aA؃ä[`ŒEBfŽÇÖˆq7MÓ0 ±H M䜋8”91Ÿ«U|n»Í½iqç‡Wý«5'MýÚ}èeÚqHÇÂ…âl8wÑÜ%d‡‡GÈtù«/ÎÛáøGY WíuÞ•“*?ÿà‹$)¸CSSSSÓèÆÊðÈFÁâùK|��úí¼…ÎÈFiî篿½îÓ_ÔBæ!oX3õÀ~§¾Aj†njjj=ª¡¤¶©©|Ý‚E"%8±pîÊÈèïëiÓÖïuâq40ºêÒüߟû\5ÞWo¾›8püO¢Û¿5_üUOÞñü¦;fÄö"{½y¯¿¹báï÷²Bún·.îzߨZ|I¬|þú%äø w}rÞCÓ÷ß¡wÌ‚«"e¼»-�^kg‡.‹Ù¸[eë×_1ge혽îÜ´eîßW|õMt·±åÿŒ•`Ó/|TÜñc4� u µ|ÍŠµÀœ^³rƒ]W—Ñi˺V¨ª©Â@4»Ž+_Øûê…imc'V��—UUÀÆæ yè›Ô5Ô$W¯Ì‘Á;V­îì×P«W7Ô‘õ+×d8ü5+×¢Ú†~$\Qa¶·´ææ(‚"ÕÕÑÊ“_Þ*â’ÒŽ›úâªH^K*kªÑúUk·AàPÔ Q¼©¹Z `•E™\=\]Smî~Ë‚DÐ&¯óé£ÌÜ®\]S•\³jSo³X´¨@—pñŽÊÕ€` ´� —Žú„w57Ç+kª•»_½òŒ'µ³ßø¶½gËÂ; }Ó™çï;÷¡Ÿ™öF|üÉã¶7?qEUyªµ5ÏyÞK'h]]dÓÊÕ™X(ºvÅj^ÛP«UTUðÖæ ½ƒ7\]SþÙCÍ© oœ•wí/g(ŠôëÚ´jM\ÞŠW”7 BÀå d?ô¶tlü¬Ø:YÈ8ÿ§2Îúd›¢…ÃzÇæöuAѺºHÛŠ•ÝÛ4R™ÿj)>ËŠHok»6æ˜ µ3_{ùå7í}踟þl÷ùoMŸöêÌG5’¨ßì&àª}~ùç–¯|÷—ú3§œñÀª^V®"‹O~§e¿A~0Élö@$Žf1TÁÊ!T湡ÔHñ<Ë+µªÐT•7‹ÛÀ8 ßS‘ž¬±ÐF iáÕ’9ϤgK°§þ[ØxTÌL ‚=Y8Ïõ6ËBÔ¿ÊÔ†<øÄ9Gœ!΀Qùƒ¸¸.ÿÊ©Œ2Ÿ OšH8 \14¨h‚ìq±5F„�Æ,8~\þIü0%åZgd?c5²# !NHæ¸ ”åˆ1 "íž#&."Ä KE">¥]ñѤ“N:i@Èq]Ï÷9dž"„MÝÛѲ’H,ŽFìpˆèîQ¿'™hÙ¸aãÆííí=ñ¸ë{â°hçT˜-¬fj¨@x˜u]§”†B!ÆX2™”¬ørÈ$ì̳ਠ]Ùé¤(Œ`Ê?°îûœ1¤¤ncŒ`@‘IŒçLžCŠIv¦pÀJ*¥¶(˜ªb"H ³FuJ ’ùŽDUóõ3rj¶i)Ž6tZ»ó9§êE�&ø _‚d"ÕÖ&n¿¨±“�� �IDATi;™‘¾O¸+Q¥Ï˜Ï˜806m»¬¢< ûŒ:ž‹æs§T€Ìü ºx<ÞÕÕ …ÂáðÊ•+÷êGŽ‹ÅF=|ÄÐD2Þ“H8®WZZ>|øpÛ¶@Ye… SšixŒ¦©—ò]ù–m˜¦™ˆ§Ò)DzlË ºéº~wwœú™ð(Æ"šÈÞ眆�ÂÂlbX¦išë¢Á›fÆE׉è“T*%´‚)L®Â.ƒ�^Ää­¨rM«™º fm…œb]¢Ô4MŒ5ϣ‚#ŒÂBG²MqŽD˜Of–!LPÞ€h'–èìJ•“à“5Åz¾'g(WN@ä÷¶ÐzºðÞK+½ü‘+v1t:v< 58åGsï¹úÉ9kV|xÇϹãO?²—({\7jDxîKÎXדܺø•'ÞÙÐØ4Òʹ£þèÓn{äº{?]µzöŸ¯{pÍþ§Ml$æáOl :ÜùÛ/j]øQÛ“G¨áÆÆ>§œT÷Þï~óÊ¢uK^¿áwo•ŸxÚ6A£†£YÏ<þåÆD¢mÎ_žúعãÐíúø€®šõÙ¦¦}÷Ì¢¾éíkŽü‡Ù‰^ŸaNwÛŠY/Üxì¸K¾lºþOŽ"�ñY¿?á¸ëÞk/¾,è{Ýñ­Ÿy-ï««w(9qz×›§•÷š¡À�€{ŽïË]>ÒL“µ-Ý’¤ [ln_ÓF¡nØ{$þ~ç—óWÇ·,Yöô}«B‡mª½Æ…çßûùÊ1£ÃÖNÕ,`Öüša{ ù' ßðÚóŸ :îx‘!ŽwÆØ üê–¿-m^;çÙK¯ýkÉä3Æ N~ºîÓ7?Y²nýâ·o¹úÉŽƒ&^pÅàÁ‘¥oM›½¶uÕ7óÖ¦ÝÙ/Lïw¡¢PÕ!ÇÐùÔÕ7¾¹lÆo?ûd‘ˆJÐ~tòé;}ýû ïûdeëêY^tȩ̈MÙÝ(;üçÇðg¯¼êåùë×/zíºËO?ãÈ*dpü‘æ«7^ñ¼–k¾þø›bóŽBaÛ[1w~§oxÊÄÈôß\úÔç+6lÚ¸fÁ—KÛ9@^K*;õïéË/{föªÖÖUóç®Úön?âMÝ>±7i<æä=—N½äÎw®oÛÔ²üËyëÜü®}×É'øâ¶óþðîâõÖ/ýrñwó­¡^:j»ï¨Nx|Áßß™³ªyõì'.»ý£†‰'ì¡£P8D×Ì›³ÉÆ `N:å²,¬Åõ'^zBÏÔ þ”>îÔŸ„€­yþ—Gÿü‘Å>2C!h]ðMKšËy§ïzìÑõßþ«Gg¯o[?ïÃ/ÖÒmu‚uÀé§ô{ç†KžürMËò÷ï¸xêÊýNŸ8Ì6þØ—Þÿë»>ZÝÖºdƬoLІ¬NßõÄÉßzáŸ?ZÚ²©mýâÙ 70¥aö§L*óº üxyKëÚEs–·›E+Ú>:”«Pñ¥£öˆ¢ë$ïœ=íÙæ¯n^ÿíçÏ^qÓ_µCÞÏÚn]}ò=EÿÑ~»'_»ûž–·lhY¿YăXž1¥ökϹïÃå­m­«[Õ"³àߨÚb³,»<f•õ¶¶kc&Wû·k®ÿbŸ£ÇÆÊ9fY7\ó·Ç7*OU³?Y²±;mÔï<ªšwwýÄE¾AжS޾«HÞ´‚èK®TP,?AîûU$=ä*†W‘»°5êýêÕ Þ\†‘bªX=¯(ù¬ÜߣÀëHT¯l®�r·Å*¦R__V!á¨Äª·V-¹bÉ¢ò Ž*²ýªoPðÿ«Ú¸!Ê~êèdð0Ëv—’7X²$Š€ sdð)õõ9C×ÖוV”kºŠFªjúõ8`ààA%•åUu5åU•%åeýªkêëêëcåeºm1�ÐH¸$V^]UYÓ¯_mme¿êXIIiiiEeeu¿~õõõuuu•••ÑhT„%óà¤C© yà 9`© Ž<p]ó–«*¡ê'Rp¸ªlyú#õV†rð o%ûe+Ê«¥°jÆ*ÐÕ¼G8çœ2¦PÈØɯA‚ó ŸÆ‚¼¹ {Lí=µyWxî’’'(W�r–$”Ýà8ŽÈ8 ž PÕy!o© bˆ#‘ˆ€ÙžçF–e ¿tgwwOO²1Æ€qmmm8I¥Rr1Q[(/ÚcF4­¬¬4 Ãó¼~ýúÅb±¯¿þúÃ?ܸ¡eÝú5N2F…ÏÜ0mL ;™Ü¼µÃe,ÍXO*•J;@ðò5«V¯_×ÑÑi[ážd�ìp„RÚ ¦�aY!è™<‚B†a†á8FD×õp8‹•Šó„²©3!. œsÇq�@P9$“ISPZZFB"FIMÎÊå¼Ñßî ‹Îçœ#Ä)¥‚gAän´µµµµµmÞ¼yëÖ­©T*™L¶¶¶& Õè,>ˆÄ Hk¬¨]RϨ÷ M ,s‘q(R†L}7²Þ<á[>›¹¸ë“_7EÄ¡vÉøÇ7s #.|úñ o;r§I/–ÿú…©G÷F�€JºëÅ˪_›²ceiÃ×­<ðþg¯“ ÛqÝÉ>wzhÂèššòÌ£g~§ˆ|°öºþÅ{öøú¢½†ï~Î̦;^ºå€0�îÿó‡Ÿ>…>8ahYYãá÷&&=ùÈÙƒ¿Kqþ·‹W„†WÒ~Y÷ª/gΜ»¾ Š;÷Ý v¾ï”;¿¬;oúï\¹[�xbÝ7ŸÎürU>·þ÷‘ðã.ÜõØ´ ö½ÿç?ûxumy…€Tì{Òàô o¿ô 3÷Þñ­OÞ´¸ E¸ùˆñUk:é‰KÏþ|Óîc/»¸Á�ÀŽÕB£:Àˆî?l$õŽY÷OødXó__˜5ìøã·2pƯ\YûÖ™{4ŽwêŸüùµÛÌDVóô’'ÏÞDãîgüµâ’—žR¬ƒ/¿ý¨î©ã†Þå¸ß¾·ê“çÿÿ餟–f ÇýOä¹_V¼}îžC†îwîó+}Ó4€ÖtéK/œNÜiðè£ÿ”šüìô+vÖ•yïëwî2çò† Û÷¢™£n}íþc« ²ñw½tÓs®;lð®'ýy^ Ó@ 5M¾èÈÄý'^7Ó‹|ÇëúñªßMØy`ýà]Æ_:í[ÒŸæ¶õ;þÁ×oÿÑüë=hÈ.G^6}õw .ÞÔí zî³ÓÏN?gÿa #|ÆÝŸnfù] Æ.W¿òüiøéÓöl8òǧNý<?¹£¸ôÒQÛ—bõÏ ÛôþÆ4ŽúÙÍkzpú{[€ë'œ?%:ý´K^'ão¼ûð¶ÛP-ô«9G6dˆ%Â^ð‹Q|Èä){��lËÒYŸÌ\¸Ñ…ÈOϹ`اžõƸœwö~7½tßOšï8räÀ¦£nÿ´“›¦zí„Ð~7¿úèan;tÔ]§L+¹xúSg Æ þÕ³OLôŸ˜¼ÓÀã®x{7-A¶ºÜØõšWž>>ùàÉ{ ©ÐôÓó›W=è÷¯?r覻ûÑàÁ;ŽûÅ Ýâm¿¿¶· ª}ùÛ÷žuPÓÆ1ão\´Ëo<pÜwÎ¥ê“Xpé÷>pLü£vØØatí4fPAh¿›ßxz’óÈI»ª´ëK‡ï>"ˆâ*œÿ>‰›eÊò˜UrÔÛÚ®íxÊ©MI<v⸠ÊÃ'îï'Çœ:9?–ŽµÍºï쩈•›øFÃwž³ã?3Qì$òuC†Ñ9‹tDüçhŸc–{bVà]ÁgEŸgݧÏŒ²}Ä1…;Pz²¨2ðeIÿHƒ2íá¹|×êž(¯„T×.Ù6ÀYiæ88AEærpŽ!Édº/÷BùúˆäÇ\ðÜРظ”s  Gc¦"ÜÁÿcïËã/9ªzÏ9UÝwùm3“I2 !! C¢¨¼�dyÈ" ú„'›,‚O@ÅQD} ²)Šˆš§Y‚AVQ$B²Ïd–ßr—î®:çýqªªëöýÍd%{r?“û»·»ºêÔ©ºõ=«÷^Á¼Â¼`U£9_Ù3o È£„2á�P ©Ç/"Æ™Dd˜qI-Bƒ35ÕųÜØØØ<´^úÃápiyy08p`8V.@SXí˜x.˾ztsô(F´Fœ¯}Š~Ž!Ñ’M@QÇh£$Ç-ÚIñ†¯C0Æ\ýõKKK«««~6¿#dð5ŸÇ4¹·½ž²œ ^ºŽB³¨QD|¨ÓQ si'°0Uy¥ù�¸•ÌJ!"‘),¡õžUNÒÒChý,âzO`‰!UFHŒÊ•‰?:|ŠŽI©'¹fa^;¢ÞŠ ¶*‰¤^‘ÆKn.NëE¯×èÅíêi_ôʺ®«¦Aƒ1½‚îQ•ka0X¢ÉdRUU¿è///«¼õËÞÖÖÖÁýû€€Ô®žž¨ìõb–W×8t㛎=ö¸å••Éd²sçÎI]Zß –{¦¿ºzÔÑv¸vìI' wu§“ö»{pÓÕ—Ÿ~ì±;†ýƒëN<z×q‰ý%W\;aYÚ½óÆ©ÿò“õ‰¸aµ1ÚwÝ7Ö–|5ÚØ¿<èWãFãɨ( b/Î‚ÑæÖ ,D¤×/ƒA=­:ÐT“¢(¬Afï™Y]®ò=Ù’ADvÎ7n ÀKC"ª›¦r "öláKò¯sGÒîá‰3i×f¶d¢«^ªišƒ7íßÜÜ<ú¨Ý馪˜ª'ʲ¯‰K¢H"6MS«Nº´5¹Ãd2 ²,±©kÑ÷Î9¢6½!ˆNXÝxA�T‰Š¿M"(¢^ý’_û±'<ލæøçþçµµµÿö€:Â5 úO¢O}âã·Cη½â+^ûÃ÷~×c>wá/Ýíˆg>¾òµ¾ç{~âË}þ‰‡ÇB“zÎÝŸV¿á«ñ˜xö“«¿z“9ê¨e;¾ú‚ß{ê³¾òœÏðY'ÜJ­Æøú¯]Ç;v¯”Õ Ÿ}óÏ<ùûÈ'íŒ#ŸPç{òí§[ÊêÛ5ÍÈsýOÏ>éiøö+ßüð[\fCÆëëâoüÐoœûó7ýêgÞõÄcoØ­÷^~eµ|ôZÏïÿò;þ‰|ò_éξ•FH>påW7ûÇìÂÆe|é“~Ž꒿züÚÍß· ïºíõmXß4Ý>VŽH‡ýùð‡þßɧœ:)é{Ï"ùË{ØÃºß!¾÷=ïù¥_úDaÀö .ÌÒ1D¶q¶YÈàRòÞ„ˆOQ"ŠÎ’d¶ôü«ÔHŽ""xWK¤´ë'„­y<Gé)†CD ™d×kTõ0ïæš‚nчC̵ÖÂ3ÏØ£ Çû™Œq9àÌ9À1¢ÓŸœ!̬õ¤Om²fÕd.öKƒí°2GøW„Dù–´4QŸ’w†™uJŒ5m�‚c < ½Å;vÔuMÆ1•kz8è BhËB¯WÙ²°dšÚ7À�â„%Œ…™Y#Æ1ä-#fÖœ”¾j8F­C†ØÕ™ä–™ Ðèt5/k¼étº´´Ô‘Ø\sùIP9ÿ7'ÊÊÎÏf§Y` ¼Mê6ì¤äHßζ–?eæCïÙ‚a¡œc�ÊW@ë°®S ªLlu y‡s¶$V§zr‡ãF‡¶U ¢& Ì‚þÙïÒLÊJ"j|HW)ÑAcöJGB­s(j„Ǹ*™yB•>¥ßï‡ËÕ´R—Mþ¿gÏžn¸A{’ï9Úϲ(o¼þº]GsôžcoØ»osssmmíÀk»vîÚµ À¬íÚshT5u=X1À²4n&¶h<•eÍ2©‰ì _omwÒ‰7mmƒÞäà¾åååëÝ$�ÖÚ¥¥¥zºé§£ªªêɨוŸ45¾©w²e¿×¸YêºvÎ5UÝ4žÈZk˜fË|¦Eœ,šªÞØØ(­][[Û±cÕ{¯æz@TýKÃÞÄ]+Í8Ä].0ÌRãvH“ŒBÐ?ÖóÂZËêÑ I‰‚ ©;s}®Â{ˆ D¢ßVG´¼÷" ˆDT¥þ™º·t`aýÍ8²Ä.hAG&¾ü=ûï÷>ç/¾%åÕF}÷øÑo}H†ÌÇŸý“ŸzÆŸ]|ÃzÓßsÆ?å­ozú­Õ�¸KÞþ3ç¾æ³×œ»ïö€sþð/þftÛöäÛNßRVßQ‰¯þ«sïõs”§?ú—þú-çÞ€¿ê}¿òø—žÅM#ÜqÊ>îÅó²ÞjGeÙ÷‘Wžû¿ûÚÞ-Y¾ó½äÙý¿»Ð,hAßA²ij”€¨Á%¨ 8Ë\¨ŽéôID9°L†z¥t/Ç0é„ëÁ#¥óZxθÊo'¢ ›a�D¿+xj ²o[¬5whÔkRlBÈá}D×ɣўbzü4pÑCnîÎ ��†(ÙA3µ‚÷mJ „X=´3ÞÚ©o8k}MÇzÌ(4bŒ Hk¤EÔ!ÄØìv%¶ÖñÅеÔÍfd€¢,‡KK“ÉDãñ¸è•ãñXK'@DòÆ&®gŒée¯,PÀZ+àëºöÞ†Š›ìœâ"�(É&Ñ ãâ0_±ê{„.2Ñ­ëzeee}}½®ë”É?™[?óáä·Ÿ¸ÚªÏôEm<T‘ÍÚCD>5‰IIàÙu$9 [PÄT„*BQg$™"�4‘¸M\.Qƒ•RäMdýÖ“ÂU:+ë”÷’Î2fIÌ·‹m#AÒ]­_ˆÞ “jê˜-‘ºå§˜‹Ñx,„Æ#+— û†=Š1€‚ÁUg8Ž6GÌl‰ÂêÐÎ4MsõÕWŸv·ÓŸ÷sÿëCçÿÓÛßþö^yÊêÚ²„²‚nss“l8ìÚµcuueß¾ý{†{¨(ÁغiÄ@¸okóø‚�¤nXHdRWSЬ~£ä¨RE5iVwîØ¿_¯×ó>L÷^8ðʃøi-" Uu"º (ÆXÕQuÉÕ¡âp8Ô¢„]#"ÆZ‹Ä¤‰�¶)p+™B-½Ñ<æV²8Ï^5tÌÜëõTßÙÚ$¢ååeñ¬éu³¶J2¢G‰p.<ÌU`­7"IX#R|´ ��ºë/~bãoÉ…wyÁÇ7_päk–õÖk¯é|ôÐW}æªWÝæî�€½ï¯|ø²_¹U·lÓ“o;ÝbVßq¨üïo¹þº[uøœ<ôœ[÷sÚ³þîâgݺ{º„ÇþÄ[¾ðoùæYЂ¶¡[¿ ¾+é–ü‚ÜvŠ~º€ØÚËàÚÙëfìNzO'õpÞ²¡Ò5äˆjÛ±E‘@uº��: ·[XžÙN9;ô+)&`ZÇLWsD—;Žª®‚çZ#dN©qÌŒðéYIý·Sø´:~‹ˆºè'Ü’éÜæIs)–!Çœy¯Zo%:QQ´•&sàÜm;q�Z2ê j'(9DIñÞ£ÉÔ OIÄ'¨–Nð…-‚Ó~ËjTľ¹¹‰Ö ‡Ã‡½²è•MÓx‘ÂZM1Ð4Mã}9èƒg/̵cæ‚ k0é’4×;ï½×â\7s|© ì‡ÌçÅÑ2~ª˜FkkkãÑh:™”1qZ.6MAZ¹µ9éÅpÖ;&H&K>‰éÊ @$$uÖ , ‡½u%âr ±ˆ’¯])�.ÕÅ BCÌìØZ�@éÀí êRN�Œh�Ä»&­Ùe¥0uÅ™ §@²ÈVôüúJ ÄLɘ/º4ƒ‰‡D¤55óÄaê‰xïk×ìØ±£vNbEUï}Ó4€XúˆèX¨°¶?@CÕdêêf:®–——sMÓ¬¬¬ˆó›£ÑêêêæÆVn° ¬ Ó+ÊïròUW]uÍU߸ÓqÇïܹ“™«I=ÚÉtº¼¶[È µÌïß»—–§ÓéÆHØ7Sï¥)­·Æ ï\ÙuéWnÔÍñ«««+;¾|ýÃÁòxÿ†Aé÷Ë­‰¥,ïÜWM«ªê÷‡Æ`UU€Ò3vTWUUÚ6}@˜vÎ1 # .s�ää÷1 ¼÷›››…±ÇsÌp8ôÞ§5’”eÙæ$°H­D9êlSú9r«ÿŽfè•¥æ>‡uí˜Y•teYj,U¿ß?pÓþõõuD\]]µÖVU%"ÖZæð¬ügL\ ŠB2TE¥úDcÈ’Cç):¸ -hA ZЂôÝIÁ¦áüÑ•�Chw¯×kš†O} �Ò1çÈÙ»¦ö©‹óŒ@DØ4�Ä#½O‡~c4à<˜"#pjPÊOü|˜D˜C€~Y¢óžfë,3V¸¥µ‹*2TƒîŒÿ‚ˆ°óTØL"I âuä�ÌÎ�2–XÄ‹![©^7"S:ç¼ó‚ Žñì½k«Aì³gPk­¥bÉ7Ö¸ñP”ÞFTÆÊÚ!"4¾ÎTÂì5žÖ$Ì.Å #‚hæöàÂÝútä&tÅ B�ÖZc4—W(ž‚½ "†¨­*@F ŒÆ¨A@< hBz $@H èœC2(,“騶ìZqmeeÅ9ÖZôžI¼1Æ¢BfFB@D2Èè„‘MQöµÿì¼÷Lhƒ¡÷Ò4•Í l!ÏÌ\–"8×`P‚€�;ÏjšO÷, †Õdº4îß¿8–E1W×Ö¶¶¶öˆÐ+zÑ8™»Ì˜ÄHDLs�dU/Ã:!D" ™/td*¹ÿÂÌáÃßi2�ˆE+#èÒ Q (ÎÕªCP£1šN§ÖZP§tffo0æ­P“xZ2 ”ž¥ßhÛ˜sÕ°$›6J"‚ˆÎÎç ­tÍzåÊ †_IÊ—L;Ê"&‰Õ땵^Ôþ¯™4úÔ,­,{ï'ãŠÐNÆUQ  ‘íjŸ®,:´±²²R†ª‘quS³48çDعº7¨² è•Îy�pÎõz=�™6M OÐï :tÞyçíÚµkçÚŽªjz½3¶WU)¬Ç ¿µ¹N}Îå œ4uƒRÊ­ieêÊÓòÈ×{'ã£jyuâ< +£C•ACûö“oÈÀÆ¡u"2€q2©˜™Œ&v¶–È ùñ1½…ò¿(ŠÑÖÆò`h‹Byt;h¼ïÈš¢,z}4Ö55"ŠºK;`ˆt/È@êå¤ -i¸ÒBê­ ›­�´FmôZ‹ARn Q‘õ§Ä ‹0´À¸qhs4šôûâ(˜Á7L`€?µÆ� ´3Æ€{ÍÑ´{EQyõ­@ƒVż©"²¦h|ø¥S)XЂ´ -hA ún%Mé,‚ çl…×ÌÞ&³L²QcŠGJŸÌGíj)ä<Z�2Óbn9L v¢ÊµEŒ¸�gc¤“F2”’+r(’®Ñ–óȈNßò÷U‚Ä ˆ'rFŠPÇsî‘åJé»!sµP›|Ñ:°m²ñΈfGÑò$¼Á6„!4žz(R5u2ábtIÍvȘ’ÿ%µNâ*3c–A56IœBA@öÆkêGÕÊ`› ö‚3Aøú¬ªªTaÁÌ@„¨%âwMgâÃprã|º†ó¼Î3€4Æ.--·F½^¯×ëmmmAÌò˜Û«ó.%¶¤ùêÈ^¾æ%!Ód!d•H “dˆj…ÄpnØ”V8„*¤±€ E݃�¡úÕ3s�öˆÉ¡@r¡LFú¼ç©‡Úöœ’æ+r>Þ<×ã ÿ3/›Ä@æPƒ&ç§RzPpYaÜä¼£¦ætgÕ:¼÷š‹dyy�F£‘ˆô}Í«W×5p›Oj®CÇœaDC† 4T×ui¸ÛÚ‹.ºhçÎeYŽÇãåÕ•¦rT–hŒZ4,emÇji‹¢WÖMY ûcWoUµµ$¶˜Ö~TûJ„É0Óx4­&UiìõöKS¹ñh³¸nÄ’Pˆ1AFÔ9«Í,�–t—÷,ÌšÂCüLB^¯7™LDdeeeeeišÆ˜BÔðlùUA“ÂÕ0I£d1S¹Ür&ZAæ5G€-F�êD€6xœ©—J§MýEXY Eø’gdÒŠÀ¡� ¤£vÁp¯è;t i9³§}©#· ZЂ´ -hA RšÉdž³ñÀ9€ç.Îjó4Pg1í|û‰ôÊrä@4z~ „YÒ^sÌ/`r°.ó¬§á®Ødhµ^½ñÐÀL·’�jE‡“çäOÚ–4À™#¸†ˆG?-ù "†È£) ´}}éŸZŽƒB�š~ À�>‹6GD²&yˆ$]�²`Û©–íå«ÇÌÁfõ/¡%‹Øgf-Ü ™òÂ{¯Öiµ~§¹Hß‹´SeGŒu:@4¿Kf~º2©�ÊZZZšN§UUi6�yf£9ò0‹ã€L¡„¹£M㘮2_qá_ ñ申`ƒ„ÊfÏú>p„ABÖú8"Íód&ÓÅþ´s‰*$œ8EaçOÌ(…¤V�� �IDATä¤ñnjòuÔáÌüç3‹.N½Î¬Zªõ¡ >‹¢ÈÜ”˜cx¿ö¤×ë%' 1ὟN§Ê1u%€¨ËÐÄœeÇÔéè÷û›[ƒÁàä“O^[[«ªê„N8å”SF£�"I._WÀâëf}}]k’1^¸¨Y©?n*4DTW;â‡ýr<ßínw}ä#yÌ1ÇÔu]…:zØT®%ÆäûàyÔ–½L|SÖoÓ{眈hAÇápˆˆ^âÜ@P\>8GùÞ³Û´{^»Šó5¸@3¨3Hº>Ý«Mj¨óç&ÉîgNi­A%ÌÌa[3›UU\Ђ´ -hA ZPNmn<Œ]%?ùé'Æ„�ïô¤“â,"Mÿr–g.}."Ma¦zÐ8ÿÀòÅã¦D;ç|?;ï;C˜…+í£ã‡˜Æ©WªO¸bݼaD„sþ¹×@ꢈxÏ aæ£îŒ%=ÅØmƸí¤Ì_“7;ï&÷6°šCO¨­ÔÐNb`] ñ,B2í@þ ¾ƒMD³¬¥‰HÈ–A4fX2}“ÄØø¤˜La¤Q©K̬A ÅÌ3'M7Æ“ÚNY–ËËË:×ÐÇ–sQ—dÆœ¯ÎuÞùü‚ÎûyÊ…6µ†1¯! €€µ¹' ºØ�‚¨á€�“‡�Φ®B`$h+†Ê¬†+g—òj[8Ïäùñæ ™Ÿ‘Î5ÆJ6õ¹ZC¨ŽAĺnÑZ«:¸¤a {‘ˆL&“²,ûý¾~¥Ë³,KW7Љ BÓ¶:P…Kì²÷¾®Æ2Xko¸á†•••§=íiW]ýOþëg++X°ˆ·ˆž]Ó çæÀÁ›JjÖÜŽÚ5wš‰Ô#LÁ¢¬šºv qãÄ»ñæ°ÔÓéÙgŸ}â Ç}âÂ1; Ò‰caf蔸­ëT€³di½äÜ›TÕÚÚÚêê*¥¬¢º–3·¼.mPI·•äùVÝX˜Yå3ø j0ˆˆè¤ßm%µ .:YªÓϽÌT)WÑ”L )"˜Épã\ã\Ú©B$ÕávÝ-hA ZЂ´ ï*:ÂI(äÇ,È\‹9šÜÓW‡;ZÅgþ„YØ3wñ ÊMض«*>`)|”z¯fÕB™é@{<eADíW8œˆQH3ÌeÚ`Q”–rƒ cuq‘ ƒ%ÕC‡þ§ëÃŒ¸Ð·y—õm !Ö0GV�)²‹Å‡#‘^Cì€=€ "KëÖk‰¼"Å' ¢¢ Ýþç0žö#hvÎ%Å‹µÖÃÞ!¢z4 !Jë`‚³¦iÓJÅ#$³ñ–e!™‰"˜IÆm˜•jŠª�@«á6ÌÞ0kkkW_{Ít:]^ZšV•jæ½ës)ÅL畞žÒ‘ðüÊÄ7uNÁ€õ·µÁ&˜i£UYƒ¹¿€6ªÁÖÄ„A²hŽà%‚ ¢t­`Ÿòà [g8‘&æç’Öu{%/ ‡!ÌÒ Š‘IÔ .=Mmûái"Ã䢪Õ2 ½Å�:ç˜lj¿0£0)l&¢åÕÕéÖDµ¼¼|ÒI'Ýt`jS…R"â|å«BØš²A£°¼®ÝaËó°ìƒ1ãz2ʺ®½c`ÙÚÚÚ½{÷`ØûÌg?õµ¯}íÎÇíq¾Fc$¬æÈvJVzfvÊ�pÞC´™#÷¾žV@8 –WWzƒþt:uMÓëõŒÅºn´àHV¨`›éŒòùíHBú“ãrÓÌ…É‘j:Öu~qâMë¶ÓõĉMÙÃeÆÃÌZÊ‘…“²I‘X&$G5Æ� ó‰`A ZЂ´ -¨kàÌÉrÌÙžŸÃò<d'o™Íñž7ª:i3öo«Pè�§ThPÿœ÷A…ÌA ‡Ês(’›€«HD¼´ ïç8“@}Î;Ia†4,jÁÑjýÖG±p,>ḭ̀Í`;LŽÆ™µ-i7rkv~½g&ÛuÎÏô3^��Ôß~˜9ÕëósïñäâÑQ*%¢<¢°Ö\ –ÌppwÁÒë½W¨j¢,I–wQaFh–ˆïÈh!ŒLV#µ$çŒÛHgømgD$:½cÌÖÖÖQGešFICaæ\êh��iæúôôœÉ]Žeu1 FޤH æ áÑIhŸ•¤$µLDAÖbŒA§'4±@ÄÿóÕsÙn7„,Ú(­}ÉjštÆ;Ï«#l 2§&k¹‘é)´KÆÆuÅWaÐäÊC Ð"¢ºkœsÃáP} œs¦°iteYб7Æ0 8ö ±®k‰Žý†-‘%3­›o¼ÑíÞ½û˜c޹îºë^ûÚ×Ú²èiïòÞ[*Ñfç¼W?04M–——'ã Uh{PHYz‘Êû’°©jqX7X[YùØÇ>vùe—./ lA"Eã½¢å|Ƈ™½†P)Î7ÆxßfŒ1>ý^×îÝ‚PU�h †V»H¢¤ f)Lk¶QF™o»A²:ÙI•ª"€º®ÅsÓ4Z½BÓCv–L^FG;ÛQÖí™E„3hB0OºØ{YXÙ‚´ -hA ZЗÃÁ7ÙÂEV¤­2Oÿ ‰]Ú~ 1ßÍ>iþ8K1ñÄS úªw°Ætë Täp+ÿóÈc€ÂñÉ��°€çK5ía[a‘XA} Qíûýx‰HœÅ3;jâÀ¨`@DHš�d@� ͪ•W|n ÃÐÏ`ÂOüLp(Ýü[‰,£¤›H¼Šµ Ú¦ TíFÂ~t–¢´ïí™ùŸ &E$„‚ Ïrž3`œMu¢½Ò¢ÆEˆ6CtΩ#ÛÑ;�À¶ÿª f™W+ITÙøh0×iÂÚ5»víÚÚÚª&S­Á–Ôg¹€ÚÛs�œƒÛHfDzn9@ÎçLo™NJb¦IHª:3ÛŸ¨ãbÈt|�ÂüÉÕ‹�Ú°.¨ª ÕOñA<›ú£zç¤bž¥¡a¦ÉJwåçMå¡ó|ƒ(ðÔ¦¥0Uó¯ÕR\ÔA©÷ÞZK…eæ¢(Ûðxkµ‡D$ž­µŽ=Ä`~"ÒHÈÜõ)vx0ôz½Â–£ÑˆˆVVV.¾øâ•µÕ••²€¹i[ ’k'P'[[ã^¿WWN†8øb¹Üª™ú2v50!“©ê J9ÚÜè‘ÙÜܼúŠËŒ4»w·Š¢P†¸èc/€Hœn$º‡ÖŠ é?Œޱfee¥×ëMëJË…’EÖ‚Êd 8›Â0ßü³byÉÛ|0û6Ø!ÀuÐ}†2JJ,ûBDª÷‰ Äà �c_:’¦}cïÕσ6�±]tm‘]ý—E@Ëj„•ukôŸúÄÇoùÅ úÒí“ó·Ï^-èöO ÉYÐw$ݱûŽÕÛo#Y…ëšk@néˆ?�àˆ~Ô�Z±¬µæ‡È΃â‚í ‡t<É£oªä€ê0ÍæýÑá@Ô8@Ìhefs@vÀlÕÑ2ËÌZNb0íŒa6ÆxaM¿§Å–>ºÔªIg†ÖaѶ¼Êpï6s¤‹%Ä·`;"ijÀø¥ˆxµNGoí6ý[TF$˜JYZ8ñ3Íb4;+[ß1ÜÖ"P¯ø˜‹®¬å!?&eÐvj&"ÀvÞŽãc¶2"bîÄD`B-!"kq2™ìرãàÁƒ£ÑhçÎSϦ(€»K ³4:8gÂ-Û¥Ò@ Æü„ÄÒ,ke‡ä:!³ú²Ð‚� @ÿNÆ[aACˆhÉ„ü…Y¦™ü™Ìní'ñØ,ÓUåò™úìœË[j­ã’ZÓ›Õ qðÞEQXk­eýSw-ï½)¬J`'3¥‚Oaæétº´´ìçYæÑªnlYµ*œEQà´[kUÕ‚,NX«‘z2õÂv©@ĦiÖvíÔ®nü`yÀëŠöÅ5u¯?`ßTU³²´Ìèí´æØýµËÖÁñ´aö`öõd\¸²´TyçÖÖÖªÑz]×Kƒ¡÷Þ‰ñ $ÀB ¨JÑ�ª‚RÔiÉ3xí53¡Q=HÑëMª©rfçS˜ea›úó² þù@DñNDL¬‰ö¿º®õkm¯ìµ ¾Ñ¼†EQ0³jè´…ˆPžžsVñÇÌÞKÓ4½^ˆrMkîp“©^ƒÞ¶ ƒÿö€º w-蛤O}âã·CÎß>{µ Û?-$gAß‘tÇì;VoLþÐÿûæÉÞ µ»R^½<˜dUÜ$fnS˜<i3à¤*†ÚϤ˜ÊÁyj0Ç��mÏ:N ù£Sƒ<›=.§üÀš_޳éC 2¡h™0ÇÄZ Ð4 ™ö¢6Á�‚@Ê‚Ž�ÂŒDÂì"ÈIØ,°°Ìv0¢eÎb§µóz&NcÌ-uj¸NØ8Ý«iÀreª‰Õÿć�Ã0Ùä0ÄxÇyI†}±Éš‡¡d!°¨D;ãx¦oùd1³xV_b�`$¬Ó§¨Â¥jš�oL¡†_AÏ"Ò°�HHbPÁ†Aª]eÔýYH@2§wDÑÓ£÷G’´ 0HD¾®[C·N‡ˆÄŒë®nV—W꺮ëº,K¨›ÆZk úÐ/gsDD‘|]×8ë±ø9§ýiÁUì‰zŒÇ¼muL5" µV2‡p½2ÓÀ¨cfÕˆ˜ÂªëòDÄèF¡¯«VK}N: 0¹.Dþë¼S,á™>„ óC´’7+YÊɤ€LsFÿ­ëÚZ«*qÎ�TUÕï÷“ÆAãÞƒÁÖÖ–fÑ'¢^¯§`^û“¦&-+ @ÙF!$Øk­µv:™pŒ²E øYS¨J­¶iúý~]×T– €EI«`v¸¼wÿM»–—¤‘õC£={vÐÖæte×Ñ^V“šLQ˜ÌhkR–%{?™ŒÄ{‹DÖ–BÀì›¦Ö !dï ‚× z)U¦… viÔÐcŒ-‹ÍÑÖêêª÷Þ$â¥`�ÀZjÝk¨U§´ó’v›vG•vUž«7p6÷"� jÝĪªrÎYõ鈩“ðä?:˜giI…„ÃÚ'-{Á̪¤öÌ ÓéĘ~Y–Ììõ Ó/,R.hA ZЂ´ ;,mmm]~Ù×g,# é?€ô¿#žv…2£¦: ˆ… EÏÿ»íYþOœ5u¶h6:3oä'$Ê1ç‘ùÒÁ6ÖÒÊŠ{外Æõª]×óSi¢äM ”|ÒªDk^¸(¥í‰Ì°Žfƒüñ0E¼;#ÊÕ.9ƒ žóP^½Ë  `›Pøµ…föÔ=M–”D¤‰ñCg¸å¹úó¤H�ìB…í° �’ÉØ’4,€±ýdóÍO–&:çQȲЌÎ5"¢ê•œ±ú-E踼¼|ðàÁÑÆæÎ;뺶³e)RSIËÓÁü¹—JbΡ‘öéê mžé‚| B.™˜‰´´Z¤Á.sΩ¾g=Pt¦¦pœàà­ûµrÌmI‰ñ¶“ØyîP’%ŒºEýÓç7?)O^=Õ5Å Äb‡ª™R=B~{>ÅNÎð6CD$˜@ î-¢(™CY݆u Dˆ¬AB1ă'ÁFØÒX×4EÙߨØZî,{ËkG;Bïj²£ÉÖfU÷‹¢a_FˆâÅ3;Q<ϳËY„²m¡³ØOMbÐÙÀ%óìcÇV™È<S=’¢-ß½3§éAÌ,8»30§8²Ù{“&´ªªétªÅöa‹CJž,GHuÙyb‡?ùWóëqA ZЂ´ -èGËËË÷?ûA,¬Š ôZZ�„%„¬^xá…Gh'i a@ܦ bQ:öj}8Ìã½OnéìØ9“ÍŸÏÒ®: pi›^´··´ö(Mìæ3ΠYõ8 Ì-?ï-"ês%³“‰H(ö¾ñð �€"о‘4è�98™ç83·¾¦‡¦G€°`B@Þ9"2†0ª|À 3{LÕ8æ³GTH¯ùB�avÂü#Ð(\B"P·ÅˆHdˆÀ{ØvfƒEQíÛ±À! æ)háJ ÒLp�’ ²„ÉGÄb�ÄyÇÌ¥Z)3þ„ÆæÔC¤clÇåV€Y0³”æ¨ÒKð}XZZmlŽF£];v$õ$Ïý\r™œ×”z0Íà¼Tç¦âQnrx̳4XÞv©ׇ¼8L¡ÅÚã!ä– Ê‘¨¤HÞÃ4rõ“ˆÎ@äInø…#Òü¦‘ï'3Ê©ìš<ƒ¦Œ$·½¥( A0Ƥ:˜U¼ƒ<à"hY[ý޵�� ÙL¢Q�²02A¶DÚ|¨"Þ¹j*M]Oªá°ïk)оkd}\ûa¤rye¼19×ë š¦iªº4ì«Y‹C`�ñÂ$ÛiBã+f˜]œ]™Ï…Ò#(êõã1ªiböU�oŸÂ3b~€¨¢ bOQé%Ë4Î8=©°é$@Ý´Á ‰ˆrו½ÛB³ê¼ÿp¤ê? ZЂ´ -hAw,ŠY®[T2“;„á$–[†BÒt»­ñ'Ü6››-}Μ‡€Î8ßì nÉ52§\HÏâÃÄN§kr‚ˆªò“®~5o(ƒ³‡Ú î´cdj”è¡Ñ^™5`ÐdÞóýIð2y¤*߇cÔ B`ÍŘîˆHÑ“=‘¢,$´ Y°aª¿>Ë–<&9´¾ IÒ_"|ðê¼+2$¾¥>§ÑåŽ 3ãm@kVm/ÈÞCVï@²ü|ùEÄ`gÖ¬øu]Çãååå‰kònPVX1g¬ö.‰hû·Á·YIŒÓ³0† tÛLÀO!¨ëPìÔD4j'Ê-R¡𡾃ÉûCD8çéÐáIçÛ|F:»Ä,»Â'OJ'Y ˜6ø5è@D¬µš-={D´Ö¦ˆÙÇéâje &¿WŸµ´ÏÀÆ´{½a, �9­Œª£õÞƒs€F€P€P�-R3mªI]k,—L5•k®Ù»gùhS@±²F½ƒ'•ñUUynÐí%§Æ“�‹H(e©Ê q]Ð΄”9'Q¼´ _Ââ"Iâ‘‘Ò'ÐYz™27Í‹R¬QšÉF|‡©¬™ €j)¦iš&E”pL„;Üt›Úö—%_ãó;Ì‚´ -hA ZÐw9a4'GoÙxXBÞ·áð”ËÒYpÎüvó-ç¨C²äÛj 0 éŸÞœñ6¬tèOÀ2 ˜ž…™™cúk‰É·$kp[«TêIðÈ€w*óÈZÖŽ˜ ÐÞŽ¡Žw T§€í³Z`�12/ît%vijšó8yËë4¨¥Q =Á#bí÷¾ cŒñKü‘™¤†³~˳0 UR@DBBö¬Þ¤É álÔ:;jÿõ™Ê€5 A-ÆÊUžÕ�Í0"äÅÃesž£"r¨Ê‰™üÇãñò`èœ;tèÐÒÊrÞxgꓼ¥ED Ñaæ-’¤z¾�Hžë]ëf6õ½ë‰ÈÏÂÙ¶ChHçI3�¨WˆbfÏLˆZa#È:w;–瘩Ùì›u‡˜ñ§#6ó”|(ô΋ª! ²$">&•@Ä¢°8›¢r¶-?·{pÒ›8ñ8mÚgDaÀ•#ij’¼²n'DØõ­åÚ¶›‡Æ~iP×lMoܰ§rR×¶i*Ï“¦cÝxç“‚<‹GñÀPÂjÕ7’;´¢òö§Ñ‰„2"âE¸ÅĘ́>$,�!¦!*‚vE™…i6gà-îì}~cZ·i½—e©å*Òìçá$0·rð[’m›*?Ý ˜ ­šA«wXЂ´ -hA úî¡jlK’yp–pöžm| ’ͧs™ˆ�fïg¿ê�ÝìÖm?Ÿm6]š]•Ð&�ä)ý8¦KŒcžÕhÄ7ÉkZoŒ gJÜ'Œ‡™%*ÿ7¤ßS !jæ8 �87ô¥Òe³z 5„¦®&‚ÜÂòœ‡m·“JB¯‡™”õ"¢ð3^i¾€ÎÔHŒ9'i}òóéÖ È-?[ÀÞ!AŽÖ!ª k­U¥IDZrÚ§“‘Ì•= ÖEO‡Äº\f>–±I)#Ñï G LŠˆ ™ü¡ª*b¯×S‡´f}ŽŒ;èô­Zgò $M̺D§©'¢,KÀ ±2MçW¹”­DÍ¢(´†'±BeDò4ѧ¨úIûŸÄÀÞónÏ;õtÆÛaQ&)3üiÿ9O`³ &Ζ•ªÂd¯,˪ª`6TJ¢_Éá& 2¡bEÈ™KäÙFÚ5ˆA % B†ˆH *ñÎ1Ídk ƒ©ArޛЛi `›©?t“éÖÖÁÑ´™X×`Š¢r#f#1úC['‚vÓ@`/ Ás.'ÀB1KeÚµfòA "B*Éa!ðDEHŸ—;üç3žÏr>­‡ƒñ±˜%�…˜’,IêažI$­Ü̳àf*„žÌ†ð$ß¹ÔÛ#,á-hA ZЂ´ ;½ù-oùô§>iñ¿³îw¿§>õ§nöÞpê†p VdõÞE®§�BÁU=fxÎaXkóð�£Ù3réˆ/Û© r•Tz}r€‡YgÊŽªù-çdz�‰¶²¶5�€Ö£>uŒ4†Ã.Ò šés²."Ä`l¼Ô† øÌã:A/™E,LÒ¡ Jn{ jQ“!` qh ‚ q&  ZUŽˆŒVLg¿Mã“ê·]¢ì(¯ØÌ‡lˆDÄ")¸:çªD¤àLŽ h$¨9ÂDaL.(Ig‘‰–úTç©"D<v¨Ê±Š{Ò �€‡üÞ@êÍ�+++ËkkÍòyÿ;‚—ý;Sa.¿¾ƒLfîRIÈW“ÛLzNêìÐQ?¥”ŠA©5>"HÈ™ös^QÅÒä²ÑÚö1Ëv™F”*gÈ-¡ÎÌòf1âCŒ~ADFc×˲Ô[,馮µöAj_åЃxïµàbNíÐSvŽ0Ì~ë…‰Á�&ç$/ÜÙÖ<0#°¸ºA7©Øš­­±wX›IIãQ…E)ŒÞˤª+ÓEÏ9çœTAÌú4ñ$ƒEZË<²È¶¹ fuj©‡1—ꌷÄ]EDE™»ê›üzœÝ¾”ÛeQ&™/IêI¢™���ÐÂUU¥[’gÁünœwlf[“öó™çÞb˜-hA ZЂ´ ; =çÙϾþú믺òªôɉw¾óÓŸöT>bîg¥ŽoeÄ&ˆˆÁtƒp"„—Í÷è1 5U<gu¬¾`òD÷íçJG8ŸµH€H=Šç.˜9ì¦Sc:b6M£u¶bˆl«5€ØÕÒä8c•Gkmž³ ¿>¦ìj¿2 <‹ÓäÜ^íá.&ýÓiáÙìwNÚxû`dÏ¥±Æ˜‚Ú±¶™ó3?'ÔY±ƒÄñ Ü �Ä·Š‘|Z¡@?´Ö–e©Ÿtb‰ÛnÄfÓ”oz±áhþBÍ-—¬¾Î¹Ú9ï½cŸŸûÑSÅ ìåó’j%ª«E~âO½J íRr;È€eb(€6dŒ¥L£7jæ|öÞ±ïõzÓ¦nšfÇ®šw=M}.ù¤$va,©bîÄÁ3S“ÌêrñKÏídjH€£é8�6K”ÊOj”tu0³Abfçœúè‡A¢|ûBD4H–LR½u%mv8‡àáÆ¥Bžæ�¼oW“ˆèârÎ9ç¼si5©*G‡ŒÞûº™v|ògmÛ“Î{Äm¶'ŒÎ¹�¨¢0àmaÇì¼wÂNÀ“€´D°ûèKÃþtc«Þo\G°àk9pý†qEß.Y(&c7Úª¥©§!dIÚÇ9¡Á¤nº¢ê‚æX¥ËË,’χO€qh¥"±´-Úç}œáÃvó›o‡ãs)Le$ÈnôÞë’¥®L$Y}™ÃÑÜD‹Ë?Ì÷„}ç߸ü†kö �ÈhóŠKÖ§‡Ý€þ³É]úžß{݇®¾ù3Ù- ¹é_ÞòÊwüûø[ÑÖöTí½øS_¾ñ Û·yݵšùÏÿ è°¬–éþ«oØú¶‰À·„ÜË>ó…kªÛr+o\õùÏ_þ_6þÿâÇ-è;ˆ¤:pýM“ow/Kíöx;ò™=ð7_úÒÕµ5}¿º¶úë/ùµ[ÓPLx¨Ç:DFß°xÐ7Óq5Ú6ÇãÍñèÐÖdckkcóàÞ›6šŽ'7íÝwÍWí¿aïhs‹câq0D…B=Ð7Þya dÁÆ;çbLž ÀñO2ÆE ÙúIÍŸ,àYŒµ€èÅ1x !‹dH<xž‘Á€ƅă¦4TíY* 8qµ¯MiÁ nØ9ñX_,Þ¦(-�ŠþÛ¸ZÕ%AK"ÿÛ~•yó²óÀ¢ŸC„÷Ž}P¨"kÈ4$Œ`­5…Íç<†|àšFœgï5~!g™Q Œ¥ÑhG„Þ�� �IDAT@�4 äõå8¢V}hÔ&(«õ=Æz“Š9}íA Œì…�¨i|ÓøôïÅ9v ¡ö’AÏ9œÓ0ãáph ë„=ZSôz޽)¬b΢°h †T‘˜iL( ïÙ’±dBGhµ ëÆƒ‰fm¢W 2ƒwÜ8ncú8ò"ŽYmÙ'[2lì½æÿ7$(޹ñà…€ YkŠÉ´¤Þp8mÛë&#[ÏNeb0ì³x2Øø†Ñh.n|ãØ1pRT¥‚ÊB®qÓ޽×ê%éÅ áóˆóÃB�Ïܸ„—QWŸQ%>š�,‘%"2�È� €H�è=×u“ÄØ DŠ•/PX¼c×�{ƒ` Hãjç �AQýYBƒ‚Ò¾ (˜@ÒË l¡{‚²ó(`ÉÆÆ$]Sý²g�I 4¶g avMå]]†Pz¥µ–ªjì CãѦ!ÒE}“SP‘«“LÛ/ Ç‚„F¼€ 1¼—ÂöF£‰1…nkǽÇZ ÌÇì>z²5±d kšéÄ€”¶@à^a ‚x?mN¦£÷^o,—®™Õ·¦<Åë¯Ýk<– ÉWŽÊ^Ó›nùjÓ—fXW•4u5 ËYØyMÖ¨»µsuœ*  Àªªú½ÒòMm cš¦`ÕÜÕumŒA†ÒÀL�qýZƒ­6�u1†ÏTɘ—™�­™jmâ$Ìjî8¤`�Ö úýiA‡…íµ%�ù†)˜D±®*�è÷ûAÙZ7©0–�ÓR²ÖŠƒVÝ4 ’!C(Z‹ÓFm ŒÁNÒ D݃ï¡WÅæ-ÿ]Ðí“ÜÞ¿þ{ÿï�¶ÎÿÈoÿÖ¥û¿]ç¹êÂ?yÑÿþ÷fç­¬Í!Í´š“C¾ö¼—ÿòû¯_ê}«:7GÍ¿½ú±xÅ¿Þ,Ê?lOøÊ?yôÝŸôŽë¿kè°¬®Ïþ™÷ûÍO~{tß"ò_ÿÓ'?ô—ÿß¡Û Çrà¼ç>èYóÿ¢9ù/~Ü‚¾ƒ¨zÏSïò ßûÂíu¥¶Ûã¦ËþóþçOzúûnËZMè|ð‡¯ùƒ¢°…-þà÷ï–7ÞÁ ¢ 4,èÝpíuëîÝ»÷ÆoÜ¿wßúƒ£Íéh<'£ñæúÆtsäçêfº9ÚØÚØÜ\ßXê cÙ{-v­Æ=DÔºÖŠUŒ1Ãá0%©êدÒSBù1Ñ�hÔoà@kûò"3iöa´lwL� õ’™Ž[³óœ·S•0ÙœeÖrÛÚÜfhE´æ!cüÓGoHžÁ3FŸj‰.åñÆÖü¥°<OËÈwìcùÙ= yF¨æB²óîìͭ3Ò¤èÜìšG] ¼¯ëZ#lä¹P;6j[#Çü‘qt’U‹ì Êp£s©5ÌŒ™œ™º!+èÔC!‡:ifsÀÓ8‡ˆƒÁ išÑhd­U/hï½µVb‚ÀA™Œ:“’‹A’Õ¢(ʲœç<D÷Š\PÓ{m?ŸÖ4MiRRÇZI›ÛÔï q¦#3е´D(€úØÏPg:„P¨ís'ò¨#uéö|Pù5yOš¦!@cŒ A¦( í]×u]§bŒ) 3N«ªjš†CåØ6:)ß|rAêÈ< bQMÓôz½†}¿ßoš¦vÍp8,ŠB¼ßµkW3­&“I¯,š¦™NÇ� ì67×ûeI„pÚi§Ýxãõ‡Ö÷7Õx`ŒMF›£‚z;—W·±?ðýØ :œÞpõ =*-¡Eðuíš*ô“L¯×Cc,PŸ ïÅ;Aï}al]×ÌÎãœcæ²´ª€¢(”uˆØ+JªtMî­ˆv¶»ù+;´í·ÛŠ "¦´Ãápeee0X2ι¢(’Xku­AVè$ÉvQI/$0æ4IÏ ûŒþ`x(];¤˜&DÐ\”·–ܵÿüêŸzÀ©»ýµîû?^uÁMaØÍ•ïýŇÝuç`éØ{ÿ?øÄŒ£/¿åÇOÚõäÿ›¬ƒÍ•ÿâÇœq̰·|ü÷þøï|xƒ.ßøáßzì=ŽúžGýú?^7{¿êçžØ»Ë >>š\ü—Ï~ÀI«ƒ•î÷ô?ýÂ(}úŽŸ{Èi»ýµÿÛS^ÿ™õm&ª¾âïžy?òg{õ»Coì`æwmð¸w¬ÏÝäo<ÿ—ð¨xå%ÉÏcô¥·=÷A§ì vžú çþå—·5…Ïߥ$ûþáy§÷WžôÞÛdE½Ðä_Þý¾úÑç>dùÖÜÄ×¼éÇ<âMÝ3(_ýÞwæžçüø]o‹ˆ~KéöÓ“Œn«ôJÛo³ûdséß<ÿQ÷¾ÓJ¯·z§û<þ·Î¿a¡ÚøvÓáýN¿kWÎüÑg>ó1÷XúÖº8¾ñ oxã^ëî‰%À$"Vˆ‡'²½RW—–W†KJËËËÃá°×ëYk{½Þ°ßöûý²,­]wïÚuì±ÇS¤1½^oy0ìõzD¤Q³EQhBAÏ!…µ÷]_q�Hž Â’hšz §À6,–4 7“0†”Àg¢å!C€9$€ tC~ÒM(ëpÇ_œÕ�@Ñy™’âCÿ„9ÔZ³H&F•çOÁ0:õ×í�{P‹vý|W!‹Èí0gÛ‹aN“ðg‚îªÎ Ô�¡j@Q=€¡ 3Ù k;Écî�Š‘öùŸ©ÃBÈ7 ‹Y ÙL“ÛŽ¢ógGm„1 Ã{OÆ,-/Ñx<.Š"éGnï[ç‰WIy¡ &ßþ�Ë{•÷œ²è‰ô‰Þ‹1„'Ÿ—ô³‡ö)sS-€ 5gºß*ÅDSëÏöYoW7{ˆ¹÷[}M&ÿÊ;ŸÏoê~Û4 "&ï"*È@ð쪺ªªº®½súæ8ƒuB*´q5tgœÉlš"¨ê‰ª8˲dæÉdRô{ƒÁ`ß¾}HR7S�(Š¢n*©ëúÐúúòò²1¸¹¹¹ººzÖYgyæ™7ÜpÝd´µÜïƒk6Ö:°ß ã}×Ý0Zß(¨/ ”¶ŒÃþ€ï¥ªu-QØZE¼çÆûF²˜k­oœ÷ž „@’8wu]ëx›ºöª/h§òˆŽ÷­AgS—UØnOŽDªòÞ—=«ÙoôOçœê—“ŽŽW—aþˆ<­F»(2YQúW$”“A’¤b†x0I2&t$>lÏœõ/œÿ…ÝÏøÓé |É©Ÿûͧ¼ìc�øK_÷?ŸöÞ£_|þÅÿñÎ'ÜôªsžÿÞý ëýͯ=îûòâìk11ý O{ÊûîôòO\·ÿ²ÿ󓛯{ò‹þ¾ƒÀùºw<ç‰orÏxÏE_úÀÏ–ù¤güÙU­ÄŽ?óÛçüÊ…M±MϧŸzù9?÷É{¿æ_¿òé?9ë‹/:ç×/€û·Wœû¿>óƒoü½û.zëõ%Oú­OÔù]ãËÞÿŠ'ÝײַyÏÕí!{ÇO¾ë†ƒJ¾òçO8þÎÒCVò›šk>ú¿Ÿuö}Ÿüg—f¾˜õ§^þ?^ð™ûþñçoØ{ÑŸ?äâýÄË>9 ù·½+|õ•?yòsÞ·Þ¿ãF„Œ>ö®÷Þû á­»ëÊÍ+r¯ø?ïþü÷žó„»ÜJÇ„o=Ý~z’Ñmdõ‚¾#iû­£»OÚãïùØç¿õ‚¯\yéÇ_wö•¯~ΫoÞfA úϦò{ÎýÍßøñÓŠow?B”‡»=ŠSQj†ÒC[]×ê °¹¹yèСÉh´µµ5'“Éh4šN§Î9W׊(šª®¦Ój:寉ˆ%“Ì¿jѪëZ °9$K`&b3A@&UVGoÕtq<,b° ³6ðü,Û1ΛŽ;§Þ„WóctGwÐ9'„³mƒ)ËCNDd£+uxD„—Úó˜pt¦ˆ3apÞA>ùe¹ïœï;§ÿ|83sÄO$è=ç«IPÞ©üDêgçMÞB>‰ùijr'Îw.›—Š\Þ´c)Ðg“«c ÐÐO’ŸóÒÒ’. µükì†Ìú•(¥ä)@.çê­1Ä:u#÷èðY¢¥4qŒCÐDè1/œÑ ©¡ìÙgó.ayN£ðM`TÒ—åR”OMzîáœM¶•¨y9„ v†£÷êð1j4RZi†±fÇü2é<¥½+¾I£VUToÐÇι^¯·¾¾>™LTœêº>ꨣî{ßûžqÆƘÑhtì±Ç>øÁÞ³gÏ®];†Ã~Qö•°³HûoÚ»¹~Рgò²~àÐh}ÄUSO¦¾nzE‰¸GDMÓ¸ºöÞ³«ëº‹­J%ˆ.Qè­ §šâ­µ:wi‚Ìy æ*¼ælIïók:¬Ëÿ…9U×|SÎ9µÁ`PU•ö“™5J’亮±(Š´¥§yW—´¢(’Cbø]›ßÞ½K ׺ƽ" 0oWãÈ„G=êÕïü£Ÿ~È™§ÞýÏü¹Ç¿ïk—p_ú›·ÿû}^øª§ÿɧ=ôW_ö¤âïÿâ7 €¿öòÑÿ£ï|ÚqmOÜ×.ºdð€'<æ´Ë{î÷¤Ç±uùåûg¦ƒ¯}ïÛþùØŸþž}ê)g=ïwž}—ù‹¿»<¨¶.|Ùsþî>üû^ïZý¯õŽkþâßùñ3OºÇã^þkºéoûØ`tÉEWÿПxð‰++'þðqÒÞ¯_1cåǽ_ßû=/þ‡üâé¶ý°®íرcÇŽôÙ?úí ï÷Ê?:çxÙŸü÷='ýÔÿ¹I�}ý꣟uÞG÷þeºÇ_yÁǾqÆ“~ö‘'¯­œð ½àa7ž÷·ŸmdﻟtçãõæË¶½K»~Ñ=ûµ«¿õú§ÜéfÐhóéùÕ‡¾éißÿúg>꼿øÇCGHW!‡®ûà‹ßýs÷ýSôöW¾úÒë*C—üÁýÿü/?Ç��õÕþð·¼ñ�à®{Ç£ßüÇçׇoìÐæGÞõÁâ±ç>p� 7]øš'ßïÄÕþ`÷Ýþ‚w~e� ûÞÿ gŸ~§ƒ²\:öxþÛ/Jª’ú_^x²AÄÁ9ç©ZÅí¼w_ôƒçþØ �Æ¿ãù¿ûîAQ.uÒ½ñªO7��îšxÙÝçøåþÒž{=î×?p•bÙü÷?{΃OÝ5èï8ùìg¾éߢãH}åß¿ä±÷:n©,;O8ãÁ/þ§®GÉä’w¾à÷¼ÓêpÇÉ?ô¼w^š´;3=Ùºèm?ûлíö—Ž>ýéï¾A� ùÂ<ä„Õþ`×)Š·ù/¿åÉgÝíØ•^9<æá¯ûªß¶«²ï}/|àé'ì–Åàè3ÿË¿ý û¾×ÃÝwÔË>¼O;Ç{?öêŸ<ëÔÝKË{îõ„ßMn;3¬Ý?ÿÎ9÷=a¥?X»óÙ¯øL�rð}Ïùž£†½åãîo“zÉ#îsÒ®AÙ[=åyÿ89 £¦¼ì!÷¼Ëîå^ÑÛyׇ?ÿå/>÷¬Sv ;N~ð ßû DFÝ6r_xÝ}ß)Ç,÷ŠþŽ»Üï'_yþu®sÅôÃ/º÷ñ;‡½þÎS~ègßuYÍ'é´¥‡¾ñZ­©½ïmY=ýÅŸíÞÕ%¾ñ£¯zâÜyµWô׎»ûýŸû·×0^Z¦_ýÛ_ü‘ÓöWNø'¾úc{uw’CŸ}Ã3xÊÎ~Ñ[9æÔxâ›»î@Ð\õþ—<þ¾'í®œðƒOyãç7;2V}ý½¿ú£÷ܳÔî:õqo¼ÔæAî?^ûøï;õØÕ~Q®žøÀg¿ì7Ÿö໳Ô_9á~ÏxÛ%ä»Àá¶Žù}WÎ|Ø|ÿ]۹ܷ+§ž¶çö¤øúN£Ñ»~|北ü›�ào¼îA+?ôÚ+@6ÿãÏžuöÉ;úý§<ø•ŸŠš›_ÿÙ4·ÊKó{»ìûcWOzþ �@õáçžpÌSß?�h.ü…Szâyùíã‹ÞúŒ³N\ë•K»OûŸ}CýOÏ>îøçœ_ß‚Íç¿ ß Ò#z<Êå‡d"¢¢(¨°¦,¨°¶Wö•†ƒ~¿¯nÃþ`y¸4èõ½¾º¹V“)7Î" zý¥Á°°–X\UÆ:çô´§Ú4Tö{@FÒ‹Œ`ðm­ÙKEÝ¡1šÕŸA#ÿÕ¹]_^X_y˜7‚&?ÔeH‚€Hä™�‰ô¨ùÂ[ü sïs´–Ãm¡ˆÞ…,1¯xhÇ�iôvx 0  9öš8"¼À‡:‹"â…5ŸÖ~SŸ‚öÄ0ƒÚgqfê[+æ™w}<:··Èvà-—“ÒxøLÆBµ=IUcSQMÕ ™ÍSû Sò!¢�¸•ó©£û¼¦ |;k®WHl­UO„¼Wz1ƒ4Þåz"…Ãáˆ666TM@Dι¤,Èû =€h…h#¼TQ‘^*rrkÀsBMóŽVtÉ18kÕ¬ˆMF6…jÌ,)ƒ]du •äÎŒëü¥’ú©^ó’ ÙÅœ9ÏW® �T)"cÒ™Q”T“ét<Ñl¦JœŠ1ˆ,)Û_輪&E32ÌÂEe(MÓìØ±CDF£3÷û})ËÒ{?Ž=fÏ™§ßcÐë«re<÷ûýº®/¼ð_>÷¹Ïxç;½ë¨CW–ûas}½šŽÊÂT“)z>~Ï˜ŽÆÕtÚLëfÚxçP�Äy¡H¥²´Æ º¨y¼i‚q ÷z=u{)˲ß/uç+ËREWbÕR˜ýˆ;p;¡ñóPEu~OØvg82i³“Éd82;kI÷g]_eYöû}Dlšf2™ bgk¨=TfNC”EâH¦YÓ§ ‚�i*‚ðs€�„É7æ›1^O¿xþ¿÷ágïF˜\rÑå;Ï<S•å÷¹‡¿ø‹_u€»~ä%¯ý¥Çœ¶œ?¦øþGýwóž—¿ôï¿vèú¿æ­_ÈOwLµî+_¼N¿÷÷X��sÚ}Îì}å‹;�€ñ¿þöÿúà_÷{ܽÍ—oøÒ—œxÏ3V�`éÌûÜuëË}ÃÃÊ}öÞ·ýúïäêõ+Þó‡ïýÄO?r-¿opÿ¾ö¥çÜkçvNå|Å_þî»×žûçîA� Õ“ï{ÖY÷:~€`Ï|æk^ùôû“iÀwú÷¬|åƒïþôSWº~Ã7~ãÚ'Üëþg}ß]V¶½ �š‹_û³z§W¼þÉ'Þ¬c»½û=ömOyÃGžú¢ÃO¾â_?³q˜ëxó‚ßøÀû÷Ýå™ïxÚkÞøƒÇ|ö#¯yíµõêθÛä²/®3€¿âÚ¯î¯.ÿâÀ×^ÿՃǜñ½åaÚºE´~þ»þqéñçÞ¿|õ_<ýÇ^}ý£Þòé˾ò¡—žòÑŸyÜ‹/ÈèòÏ}~é©ÿ÷â+¾þùó~þø<ï‘Ï{_@:ÅþàÒÑd29ô×Oè�ø¯üÝß^zÖ9¿úÇ>ú¹œòÿpÉÕWýÇëtàS_üÿì½y¸mIQ'™kÚÃîT·Fj@dx ÚˆÚ6Âëæ)h; ŠÊ ¨Ø¢â�ˆCƒ"*¢ý„T¤EAü´["]€Å CQV5Rý÷ {\CfÄû#ÖÊ{Ÿsª®Ý`û;¾ún³vî\9ŸüEü"â³ à>ñòoù¶×Â÷¼ñ#7~ìOŸ“ÿÑS¾ù¥m@vþÛ}ã}øa/ý›Oþã{~ã+¯{Á~àÏÎ @ùþŸÂSÞØ{Λ>vó­×ýÑ¿Ç~ø¦r©ñ³w½ðIϹú~¿ð7ŸúÌÕ/ºìíÏ~Æoß ‡òRKÞúÃßðÃ×<èÅoûħ¯÷ëükO!�˜û|Ó+ÞyÝg®ûËçmÿù³Ÿûº[ÀßþÁwÞø_þàÍ·êï^ýíWÈáMÞôá ŸþÖo¿åïú×7üæË>òe/{û'nüЫ¿îæ_yî¯ÈðͯyÚ7½bü­oøØÍ×þáïþÕ§þÔÛ&«C þúßøÖo~uý¿ÿ?õ‘¿|åSh�û_þ¼7èÆO½ÿw}ó/}÷‹ßUÈèúw¿Ç<å-Ÿ¼íÖOüõ •1Pîö^S~Ã\Ûm×½é['¯ý¥·_üÂ?ÿÈóSª×þÀ/¼c~õ?)|×µï»íË^òÁoú‡ÿñŠÇý­'?éW>¶ŒQ²‡=ûß}ýgï¼á/žnßøý/xË®ýÒ¯ÜÉý?ÿýœ�ÀìïÞyÍæ×~ÝCìáÕ·â?ùŠoyÒ+öŸüÚk>}ë?þõ÷Ÿüè57ŒäÈÕ2ÿÛŸyâÓßzúÇÿò7~ð÷ž¼÷›ßô]¯þ ßþ†ï}üÏ|òQ/¿ú†[?ýþŸ{À ï¿î̲ý ùø¯~ËSÿxðCÿõú›?ôʯøèO|ç/ îJù¾}ãÓþ|û‡þôcŸþÇ÷¿é—…9üEÀwâý·}ùË?vËí7þÕþÙ/ÿ>ã¿\sÃ'ßþ¼íÿúÃ/xÓY‰N¡£ŽŽ£ÎÉê-ß¾Ñ;ùÀoú“ úåßó/Ê‹æ CÆïøñ'=ïýü…w\ÿ©ýñÅfûgñÞwÁç[ì²#î8‡í³í¯~ôCϾïï>åÜõï~ï]»xßuÀæ½ï=óe~TÄ“so~á¼ûÁ¿ùѳ£;?þ/zì’¦ê^Ÿ{ø ò¹½qÁâ¨Ï‘úý~¯×R·V/ô„6M i%(ï—�§ãñt<©ËJñZ2Y–õû}%”€Z~8¢¿® h†–d«—ò€Wq™€hT©÷!†…+0²Ôe×kìlò1î %ƒõ,nC¸¡ÆU©]×u¡×µ-ÇVßX§�þÛfû),ná÷ÆM]”¡ÌJ¯WjŽ‘[¬2eËóÙYÅ„!CìLôæÏEgà]ð„㩌Y¡6Œ°}°.®´#’ük<E¾èaôS]“ñõSŠÂ=(B+7Ƥi:¹Ë&“bT @Ç ë'0«*k–·ç* #,�ĦÔðî"üǵݳĄçœoœf7X`ÅnîâjWpÕÁÄe5Mܰ¸§±Äµ…Â:æøaD¥Që:t;  @`-a”f/ÔW¾2V‡6 DÂ@¢~¿ŸyUUûûû½^/Þø»»»×\sÍÇ?þñA¯¯aî¾óη½ímï}Ïßž½ûŒ¡Éhw¼®©f†¹&áÙhTÎ&muïÜYvÍæ`8OšªFU±£q÷$µ ¡´©X´wÎ;ç˜}¼¨Qñ¶²x´ýJ¾€hOÅ£-øGŸÀòÉ?\)¦+€–VN²�f³™î—º®‰H™kãñXKêÚ.ËreR1ìÇ¥öDá'V|XQ}‚¬<liÑòRšÏüÑ3¿ý?Ÿ|ñ«ž{2O¸?´ù(Ã>LFG©úñÄ7¾øE¼ñwŸóØû]õ¸ÿÛ=ù¹¿Œäìk¿¾o­µ¶øŠ—\?Oóa¿½úcثǓÀ]÷Êçÿé—üÂÏ=z3´ºyï}Qf­µÉöwýÅÎx ýáR#ƺüi/ùÑ+Þÿ²§}õ•xÊŸŸúîgíñóíuõw¯ü­ÿ›ý‡)'Oý_ÿñÏÞøüGö(>ü†—¾þ‡ôÄ+7|É~ñçl‘>ê'ÿø-¿øoOõR¾íõ?ùŸúÏÉ7Ÿy·¶.½´×Û~ñ·<à¾Íîgð0–ÏÞð?®é?öùx蕃¼ÿSðªé_~âõðÁܾýšÛF"»º½¼ï±Ù‡n;Ç0þЭ·ß÷>:vïo?Rd÷mo|ûÖ“¿õ+Rà›ÞôÚw^ôý¿þÂ÷€Kîóeßñk¿ø¤½7¼öÊß þé+ïsñ¥_üÕÏüßü.óg¯þ ½å!Ù4Ïó<K�À]û'oúôW}ëN#ÈÞ[_õÇõ·½ôåO}Äå§/¼ü>'5Ž ûû?|ÝG¿ô'^ñܯ¹ï%W=êÙ¯xÁW]ÿ{¯ÿ@½ûÖ×ü)<å%/ùæ‡^vÙƒ¿ñ^ö=½?Í_œ•ê]¯yÝ-yÑ+ŸýUW]xú²+Nðƒ-¯þý7Üý¸Ÿþ¥'Þïä}êO>íª¿Û;ÏÈjKþòÕo‚ïxé¯|Ûï¸èÒû?ü��m]ñàû]ráü¾|üàƒïýH{»OŽ_vÅé“^uùI<´© ��Ç/:}òÂ<þOº¯ïßçÿøâK.~Àã¿ç ÷¹åc׎„?ó§¿÷®K¿ï?>÷_]tâòÇüÄóþÏÉ_¿íÃÍòPƒûÈë_óá/û‰Wþð×Ýÿ’‹®xÈC¯P}\záý|ùE—<èž÷Ý?óþ÷ݤHž^yÉ©S—^uñ`ïð�Àlûôé“\õèg}Û—&ÙE÷ÐeÝ÷ÑÏø–‡Œ>þ±Ûü‘õ¿"öØ¥W^zñ{ÂϾö§qíkÿ` \¿ï—\uÁÖö¥_õœïúWõ?^«Ï¾òÛ¾ùÔ»ÞüÖ3õ‡ßùó˜¯ä=ǹtùƒWàÁ?öÛ?ñØû_|úÒ+/Þ ��9bµ”W¿îõw>îE¿ñ=_~Å%_ü˜ÿÍÿpßw¿öOnlnyó«þêØ³~ãŸøKO_|ÕeÇ`l÷÷oøƒkñ£/ý·\pò~ßø‚ç|ùÍoû?.t(ÕÕ¯ý½Ï>îç뙺ê¢K¾èK|Izø‹ôvãÔE§N]öÈg}×Wgpò‹zùE÷yä3Ÿú•îÚÞè¢SèšÕ«l�� �IDATð£ãs²•ìÉÿegï¶¾þ›îú™'üÐ[‚kù<Iù®7¼yô ?÷²§>üò‹¯|Ä¿îþ‘vëžwÁç[î²C‹~¶——}Ýcï÷ï¼únáÛ¯~×äAÜ×Õ·°œ}×;ÿáÁÿöߘ×-þ²’†Ãl÷ÆÜ°Ãƒ pßS«ðîñð€#ÿ‚,j8`˜kmáDÿ4òÌÂÊÈŒ|â×Îçó¢( \ËD¤išz^öòb>²óÞûj^Zk‹~½´8Ù1£¡¼ß E¿Çà±Ñ $",N­@Q? �¡ÆÄÒB„B"rŽQÃ-´¼™E$1‡Ã!îxÎ+Ÿ /¥¾ Âí9ôZë7Øz5û.a¨<Ü•¼szµwˆˆØ~Ý,œ&Úç°øÙbËœ_1Áé�ÕöQúñøR„´Ø"ìàz{ïg9´_b@gÜ~éÔ lŒˆ|§ ÞÅ‚¨?DKh‰Î}ö®YU^vå奸v"£†¦D`B�H€Åó¢ê 3c0þ‡˜Þ­tG›ê WÛ¦( :âú¢›¼šTR¡²vœ 6Ms÷Ýwommõz= kÇ̽.Öz,&±k¶\0  clükÀ±±3Bø�ˆíºÅ¥dŸ��´�Â?GDí,ÿù [Étzª(i|Gî°ªsÿD†°ØˆH£r®,Qa@8€0± ]¹ÀuÝ`R—Ó”ºÐ'Z]×êÄ^×5�¨Ó4E¨Á&³,3It@Ð¢J<2HHŸAD$èêfÿÜÎx<>¾±å„ÃáÆÆd^ž;wn:Ÿ:uª®"2æø«›f°¹U…g@CÝvÞ`è‘6·NL'åþ¨Ü¾àâK¯zÐ~gK†,/¶¶z½ü³7}:÷eýÜ ×~xs¥†s†¸inê¼ÈPx<ÚËóÔ"ÕUåª[]§Æj�EDdqyžiV×õh´W…gç½¯Ë ?žæ™‡Eœa‡ºÆ.|£À"]bÈ""KS °M²¨GÜbÍ3ˆxï«Ùüö[oôz§NòÞßqûg777ë²bÀétº±±Q×µ’Øîºë®~¿¯1ç(ÖCYk5o¢>rÞSä›Ö.-�D"ðÞ3 YBRÝJÛl@yÉ úIO~2Ü£ríïx‡=¦÷P™ÏçÛÇŽŸOɵ¬e-_˜röqn-kYËZV„ M&“Áà¼b´šó»œ�@U×yÌcVŸ"þÙ[Þò¼çÿ(u–‘Ö=“YÑ"â`0pn·(ÂÛuU@ÑïàÀ97›Íªªl “$)Šˆïêº>{ö,ì IhûøñÁ`P×®ªKB›f v¡é$b50¢õ¾ñz?@!DA4�ÈìˆH‹,ÀtnÿƒµczXly…aáš«bƒ3,0A±£é®€dýzP��JŽpæÿêð+±EŠÁäÊwÃÏÔùá둵I’‚Æêr9Ì‹ˆ©Ô_A§q…Wš>""²¸í¦Âï@1躶ԒPµVnüí{â‰i@:D$Áð§ŠyGü‚äº)'9^ *Î9õ‘qÎK 1ëîPwôÁ`PUÕ`0˜N§6Y¬jî‚«A§;ˆg<`X|°J¼ÃbX "væqi³(r�P*C»6´~Œ†:ê% ¬øÅKTŸ„}6Èʤ9[¢z¬pim§ ,KÛÁ凡¡=¡p ÜACÊŸW‡]fê²ê Ü c Ù¤[ -ž÷ãBXˆZÝœ£Ô=¡iF`ÏÖÚ²©ó¢ cöý~00eYî§Æçü©“'ÇÖZÃHÖ0s’¥£Ñ$ËRL’²œÍgÓ"/€k†½ÌF“K¾èþù`£qÕ'O=ö‘¿æê¿þÔµêõ �›ªt$„Å7UID¼8ïÀûº‘ÄX4‚yžîïïEAD{;;ÖÚ|cs¿,Ë­­-Ïf<{ï³,kcs8èh¯œT+Ó·rP”E ê.Q�æó¹cŸ÷zª^Ü.½ôRpYÖñAó‰¨C‡sns¸áœ«ª*vHÑE΄•Õ/§ö!³,VWûA´¡àŸ¤^ôcÿÝù{ﻯþʯúšB½kYËZ¾ÀäoÞñWxЃþw·b-kYË¿\ùôŸúʯþ×çSòêwþõÃöÐó)ùþk>pÔGŠa¤ó;è¨Á VoÅÆÚΣw.ACe˜¤óé aïk×�@Q@¨‰¬À öz=uÇu”’ˆÔuÍÌ|^F14Ìl @£ö@cL:皪ÎòÄ Õu  ¶5QjÐoC„Ö,ŒÉñe7h‚OAè<.G¼‹!œDùíµ˜«w!ÄB=Š4bÖn §Ñk¨´Vb#"ây¡ Œ—÷ÞƒGDâº8äÖXcŒ‹â>r°Z#’µÚ‹²,µªXƒ ½€ˆ„ñQ:}0±EÈç: ê$"]VB­D:Õ†N%"j^ƒÆ;äE€=""ಂwišBD¤×èñkŒNˆ€xï`A²õÀ(€H¸ ‘{AÕ2h íWP(ÏŸ:0 £ê sfYGÏtÊýæ…(I)Ëò‚ .¸é¦›&“ÉÆÆÆþþ~Qµ¦Üë4Ü…o ƒ–+¤‹È/Kì‚@§ä.èbu-¯äWs—B"S¼÷^C(cßAhÓµùà€JB$4 ™y6›™ÄêvÖá ó«oÁΪˆX¸K‰JDâÛüdMÈaïYèbRÄÛªíª!Ç^¿‹JhJ’¤_ôæóy]×ÖÚ4I…Ùk ³,ÓUíêÊ#HÎ92�½oÙ%Y–9v€ìý"’¨÷Ž áp8O'EQxæÜZçëáF<ÝݹðÔ½^žæy›jÁ;cÌl6ÁíãÇ å“É$µÉ|:?~vçìæp«¬<{7ïíê^:¸ß•WžÙͧÓÍÞàÆo(Ë™M¨šWijÄs–$"~>A$(gó"ͪºtuc�­µ Œž`2™ôûý,Oæó¹R*F£½Ùl²±±aŒ™Î&D„(IbD¼*sBOÃDc«§…–œa…Aÿ•“°=ÿT‡hÑ@§ë�M0Y–åd6;¾½yâı½ÑîöæÖööæÝwßY–vccÀÌ“É(ÏÓ.ppšZì’)öûýªj4æ®®çœþåŠUE‹_[6ƒ!Zc QsÅ´[¦Õñž—,¹Ž.vÞ%ײ–µ¬e-kYËZÊ?ó]Â#žÔW"ïLl}VÐH‹m@ÈL­µ ³1Y*ž½Hãý‰cÇq>ŸÏÊ9t7HJì 7 "$AYDhS;#D]hMÜ�h¡ªË^¯?ÜØÚßÛsÞ ³Ù,Âà©ÕÃ"ÆÚxBl1 Ü{ZΩ¾jZÙ¿ñPÁÎʺ‚E¤ 黄‹;z”. 4Nˆty®¢C÷1!êX°?/V@×Gî¼C¬/\¶"†¡€eοD¶ef®5Ê`”&P"({p—~ Ÿ‡�J[G¥•1Ô=¥È–îë }”8½ œÙ52žÁ ÎÅBîÒ+†’ú«5-|‡ãñX5bŠu1j†Dš&ˆ–k¼äââÑ8´püo<GáÔX©*À?"Yl´0ðèYhiƒ`¤M Í8«Ç¡(�~¹k%e„h?B”Ë-‘DkC•z+Z¼ ¡ k8~‹ÖëŤãAˆs†á<[=Ž%k­V„žÅPØ u]·MP"6";vl2™ŒFã²,ítŠÖdE^ úÃáPà nnnî§MÓôz=m­¯!“‹dš¦A4E¿W–åÉ“ÇûEï–[ï˜UnãøE^ì–ó~äç/½¢—×]÷±ztçvŽE–ö66›Ù¨¬g(^s¢R æó:±¦èõ1$Ëp®iªÄXïý¹3Y–5MSÖUžçEQ0³†’ «½,K:Ì¿²Ò`ù`‰×j<ס@¼Å0zˆË_ÙÞÞÖpŒê_ ¾ûûûªõÞollè¤hžÀú ¼ž•“MÄ„‚ÃÄ9×+zH8LFÓ‘!ʲ �,YÐ þ|ÿ Ɔß}Õ«¾ïYÏŠàQ%ײ–µ¬�à)lêZÖ²–/‰ïÿù5¯yÆÓŸ~X©ÏûybW› �u�B“X4Æ€Éz…¯ï½c?™ÍúÃÁÖ±íãæ"ŽgÓýýýÉlºubËBꪃ° !Í.À�`ldœ3y¯èõÛ9ãG³Íá iª½ýq^¤À-c•��Y=_%¨•�¬ŸÎ Û4zV`¼sÞQÐz&µ]¯`K8úŠËÌéRÇ$¼xÎ"ФIX�A€<ƒ0Zk-Rékc"Wóý,ù„[¾A4‘‘p[Š!rB¿W _�uÆuðŽûÕöÂDFònDÄE–p§GD�Æ6Ô€t S3ŸVÅnê¨öêPYM ³ EÞ%@U3|ªR/·? tþ‚ @0rhI`Õ“bcccgg§ªªáp8™L("@§V ¢Öh¿„íc+‡i Vô Üó’‹'¨­¡;5˜aèÀ ,e€‹mÈ]E¢�dUÃâ¥>{H—Ñq¡Æj€�°@È_Ø-ãX¡°bÜPËg‘@…©]£I"ÌHÌuÓ gk­‰2qZBcÐqÞ3³AÃ!‰`«%l+Ãm@P^I–$Ž}XÏ’eÙ™3gÎÙD}Oö÷÷76¶ªªJÒ,I“fÆÇ-#©i𯹼WK§N¯BpyJ©óçÎÜAIvâøé“'Nìóˆx>ŸÎõròU5YB4H¤§±ó†(ÏóÄ`Y–Õ¼DD4d­uKR£z$In¹å–<±_|±±TUU»˜1I’Ú5z�†-�$‹ »´–X� Ž®Ÿ6ñÒ]^ B‘®V@¬µÇŽ»óŽÛEFlnŒÇ㺮p0cvwwÕY7ì¾x™éꪘ—Î7f¦šÄX àx_D²4=¶¹%,Î5^Ý#t³Ÿ·ã@ë@äç_üâ[n¾åYÏ|æâQ«%X-¹–µ¬e-­¬µkYËZî]–îïýÛ÷ÞvÛm?÷³?»ZêóžžG%\= !"fEÞI]SbO^pJ›²ϦÆ4´}üØ kÁ�Ãctmíœ ŠM’eÇÝñø²ã§>óéÏll ®¼üÊ3gïÂDRB4$Ј[ØÛõÚx¸õ{ &E9·WÌb+Üø~Œˆz¿½gí@\óJ3VÊÈòSáûÎ-?.z_€XCÁ@·ò XF•1<‹aXlQç.¤ éCŽþÇ(n("æE�Øú‘ë�vÜ!\ê]xnŒ%53ª‡?�‹AÞ™ˆT»ƒ‘h±àCC…�$ò5Ô[»$|jLN’¤uЈ"_¨„z’$QhžçÆîáÀ xæÃ@­<‰§2 Kܵ{@>+‹!žÇ•Õ~²DœY4Ì`L¨Í{OØÚü™¹ "b8¸Ðuð[ÿ Ó¤5Ñj)ÂäÊrÐîÂ=ÄŒ{'];U~éñ‚ˆ¾qØ©9Ú>zey�„¸!þe´òˆ˜E£ªšÌZ‹BÆAðÞ7ìƒÁp8T·ymžç�PVõ`0@›hd„,O„Í|>Oú}ñuµ±Ù)<J Ía盆™›¦©›rk˜Ïæâ|ÀMU&½\– B’$E­ñƘ”R EQlookáù|~|ëtš¦žhšĺ®5³€n‰¨0È+ODVb\¶š¤•¹�X:�÷$b¨¨&N}Žˆ¨(Š¢(ÆãqžJ"Ш7ê0¢êéò‘ªª²¬X4àSöɳŒ°©êº©‘È‚gAcÏ;)Ör¦’ùÑÛßß�Y^« «%ײ–µ¬%Ƚþu^ËZÖ²–•»„ˆÜ|ÓÍÏ{Þ¼ü×^—ú\'÷pi±ñ}P£ ÇöF$DCŒ€6IÈ$ •M÷ŠbÐχ}"ÒŒ‰eYö7ú«¶&!T#®ŒD‚@6oªrûØï{߇‹¢8³³÷™›oùâû]iL&^ˆ=t®!Ýý F’ˆQû6›CÑDX(ÕÒ¬RÜÃ…Á:Ƭެ£Ñ×h"£�HÓ4IE ι�‚[tª]Ó¢t @^ØÏ! ¼m-®Š™…Úª¤.:}ÌÜV´ÌQ€ÆÐ…x¸–>BDDà Y‘' všŒz§_dfÏ-2kùÞ{bºæqguÎ! êY–Z‚ À,^á§gv^Áhœë#ÌÒZû•ªjÓ  ^=>`ÑT‘v(êº>~üøÙ³gÇãñp8ôÑz‹‡(žkˆÔÊz€eí@:oÛæŠÄ“(à8 iÔÄ".J½ �Àm";%ãèl6Þ#"„Fªƒ%"""$ ̾ãÿ/êd!@ƒo¢�,WÖ§Ú5®~È@‘?-MwP+� �Gi/’$At� ÌmþgŒlÝ ˆˆkŒñµB"bfgA �h@’çU] ‚-Ò$I,¡a:uêÔÞ¹ʲÌ"åy>žLçó¹1Nä6q 1¦ßæói]W¶ØÌ‹AUMÑ X³CÄét:ÁŠ]“‡;»gsÖ˜¦,@À Bbm5›I¿H²¼_ôÔÎïœ+góáF_Õ%ι³[[Ãáp6›™ÔVMSUóm$þcÓB·9V-�È!Š'é”B‡®ê ‚€¢»ÌÅ1f5=mloÝqÛíóiyÉ%—4M³µµ¼4|IˆÏ"¾CãŒZc)Êò>²)15Ö{_Nç"˜f‘õ ]t>_<ás¾ÿû›Æ--øPl¡$_ËZÖ²–µ¬e-kù'Ë¡w‰ýýýg?ç9ÿéw~石%‡° ×z@5 "PbѽgYÖr½GD›$½~»»§¦Sþ°^éZj“AÈS5~:Úûš¯}ÌþàÞðÉOüöïüÖîÞh{k#Í{Ò”Œˆh�˜d‰X˜‰/¸ ®€´`·TÜÂËSr°Â• á^îJ“â'ª&HÓ´ßïEÁ MÓL&½‡‹oÀê €®æ+º’˜ŠãÁÞéÏȆ"˜‹Í!Nñj%è}û …ŽÖ.Æd饲h$ø¶Bçœx¯”rýÈ×xF0´ˆ€wmó xuäï ÈD$ˆÐ~fF2 «¬µjTs´¶¿Ó<`˜wé_×õÆÆÆÞÞÞt: áE¡Ý‹`å½p~²ò­•ù>bXoË}×Q’·¯Ô¿¼;Ú䈀ȦŸ•BÍ´H¨íl}bµŽ²?âéhµ€ˆÄQƒ‰®®x…ÞKh‰tšÁðD!e¢Ë¸Ó€>y ›hDÆ–ì@š†AËbdÖA8øjïý`0˜ŽÆ»»»yoÐëõX\f³áp:›ÍçU¯×ƒf³™�²ÞIUÖiÖÚªt³É˜Ó"1X³óMíšfkkcoÐ'ªEDˆ (Š~¿n:MR㜯›FdI¤­2HDTUU¿ß¯ëÚ9wÛm·]yå•-´&`檪±ßï÷û}DtM«“jµºž.–bÐ9®œ®íš”K«ûªHK‘N%ª‡[Ó4iš2óæææwܾ¥c ƒMà’çy%¥ò2TE(QÖî¶@¼Àà±Öîïî³çÍÍ~¯_UUݸ”¨b6�,à:´¯¢l˜îg±WrÔ‚,•\ËZÖ²–eY³ Ö²–µÜ›,ß%¢+Ç[Ççäm8¤AVÕÁn¾ÙÚ«EÀ2ˆ¡ÄÚª®¹cÿ&Ì`‘d/¾†" xE},ˆd‘|Ý\qÅý~ò'jskpùe•Õì7~ýeƒaž'M]A{ÕÝJuX±L.™%Üæ(£ÁÁX¹Àá²kk ¢^õâëuü•îÚ��@�±KñŠ"ó4M‹¢�€Æ6u]WUå=kê²6�yl4ø �HKm (,Ÿ!½d+lÓç±û@ø!I’ð;gàX¹Ðùí·¿DDåx´Eº( Šƒš VUÄÒÚY� IlÞŒ!â.aÄ/Ð`BŠI¿KÄÐ#†¼ªZø§¼t�ˆrhƒã7”뜇;;;eY&i*‘&´Ãœ¬,¤%pIøèÔç#Î�v�Xû{ïߊcñC“k:^°NBÇ»p?×UÄ®Ífº>ŠDÃվȘ˜ê¢“†EDPXg@#„ˆ£Àâ,6M°],"€¨œƒ �‚ð^q¾i�c [E̬z9Ç"~‘ò#ù/’4IÌÆÆÆx<€~¿Ÿ%I–e&MÙÃØœ«±’²,‘l–÷ö“Éd8Hmj¬µÌ†gmRîÍOžNçóùx÷ÌîÎÙ—Ȳ㓽s"¢غ®ó¬/HMU¡¦<¬ëz6›Íf3cL‘å[[[;»gËrVýÑh”¦i–eÌ<÷'#cÛC&µIžç±VE—ŠhvÜ(Åf;>JÕ¥{p%uN¶*A4ߊEVWs�PÊ€ˆ¤iŠDD½ q:ê(ŠB%Ty–PY–Ô%nÉ¥8�n\¯(ò<ͬ™ïï¹ûn�lo6Þ¥ylÒ4õ½nÐÙÐÓßùíßú±çÿøþþè°îß‹æb-kYËZÖ²–µ¬åeù.!��››/ûÕ_ùg¾`´FT G˯ÆÎf"‘q[˜½Á%v³Äó—Ž �˜4•vZA0ÂŽPn¿õ¦KN>dR–Ÿºá†Á`Ðu9EmLdN—.ÇU<pÔ…—aêàÃC€Dfêä ¢1$j`\¾Žo[†íêóPm¸a¨)‘·¼ABDôDY’Ž<«©Í+"â½gl³XŠHàQ+4 ÀŒ�T¦i m‹HÓ4!¤��! !JË8м÷jÄÃŽ•7 3¢!𞕸ODË¡ì ÀƒŠÇ„^dií·]"B"AÏŒ ž³"—Zó_’¡¥¬Ð)­°#M°°¢`XŸÐ™| 0`ÑÖújŒˆ$Ö"‘º$(F‡"$êCDdY™wÞù|ÔYžKdoã)"Œ ÝD¨ìÐ…$qˆ¾è+1ìjXÕpIg3_‚sÝaf4jõUŸae�èTàʿ޷ICcZʺš‡;…KÜT‰|Îi aµˆ ¥˜±¢FûðõNAåa$e.€‰6GQîtÁ£!/]BÏ­O{x Faósš@D-¶›^È w¡4›¦©ËÊ$¬5ˆèš–3�u9ð�FÙ(y‘–•­ªÊ{ŸIó‚R«¡õÙË]Ÿ½(»ðâ+9ëížYc«é„j9¶½9:Ó›ŽGh ×Ïo¿ë¶±,Óél¼1,¬µMD4¨6›™ó<W½Àh4J’ìî»ï¾òÊËó</ËrZÎg³™±h³TÏs²ÆGÔ§°¨$b‰ˆ×3_³â@w˜ëúˆi,‡ž²ú©.”²,•P�]<‘ÙlF£éd4 ƒ4M5ñªHR›Í]¨Šˆê.amX$‡ƺý�MÓl®šÝrÃM0-O k³ÝÛosý^š&‰Í˜ÎsP„%VáÿêK_ò‹¿ôË·Üzk¬¦$‘Õ’kYËZÖ²¤{/³–µ¬å [Ü%ä²K/ýé¾`éÊŸŸódùVeQ˜=vŽÌÔÝ̼ˆA#Þ£€AP‚†ö� ´€¾Ì,H­kt8E¯ŒÞ£¡,M泩µIšeóédsØû‡~ø¿vöë¿þq·ß~ûûÛ†}i*+‚" ¼xåD «k>¶Q幋qemÚ‚k�cZ»zëÏß©NP\Ð{€=^©޶1sð™wÞëœkZüß‚z¢½C‹€‘µD¤PG¼wuíêE¦³Ùx4JŒMŒž™ÑZëÔœ Ë�È�3{�BB“�Ù4/êºö÷‚‚FPí¢ À 5ì-’îòÞ+Ñ: �KÀ :±ÖZ›€cn-øH ÆïŒ1Išt©Ël@¶-´&$×TýÁ`VNMÚæ<Ga@ 2¾Kd@D”Û¦i€�jW©þÅ`²Æ{( ¾Eþ,èÑ9ÇÌ ùå=~rëÆ¬®Î{çDÄ ¹ª¢Ä@B@�Bu~Çåhܸn(DÄ  óŒHIšy–ícÇÏž=;艨ª«^¯§¸e>Ÿ÷‡çœÑ*Z’~§¨b€Žª¯·†˜QõSVD¼zaXR‡ï‘ê.:|΋=Œ€ çf!!D4ˆÖ4ä½on�"^ЃĈ$ € (À,Ìž“$iÉâþ ~ €"@‚„ä…ëº1HIbQЋ°° ˆ5ˆ(®©ÀùV÷d“.‘ñ^¼fÒ܈ª2¡a£*³�Ø’%ÂöÐ!0Ö$�PWµˆc wR‹× C†tMk=MÓXkÕ­Ç €$Æ"8W#ø,Ëkš¦ñ@ä¹1SK㺟òt6¶&E@nœAÛ4MQMYY¤<ïUeÝï÷ö÷÷Êùlss‹ÙÕåls7;gÏ\|Ñi3tàP�� �IDATKIY—®©ó~’˜”ÊÊ¢¤]9µ óñ$)Œ±�Ày‘ßç>—–³Ñí·ÞvÅ}.õ6išÆ×œÚDD¦Ó9"nlmZkÉkm9½ëîã[Ûýþ°itìMbçó¹M2Ç�F€Ð˜Ä9Ç­[‰Ñ�)D¤éW‚2NDT Ä�`5S†FBÔ¡–†ÃmÞöÈCad-J„Ț󂀑„\åš²Imе/I–2Èt>³iR5µFlA¢¬È«ªBC^XC"bØkš ‘лµMÀ YU*éÙEˆ„�iZXÀ³w}vèÊÿ˜Gö@;Žßøw׸^^ “ógÀB{¨òÂüÔë~ï÷—¢Zr-kYËZZ9hYZËZÖ²–ß%ñð‡?ãéß{ÈÕâstž¨ý¡YR3ÑJX>µÂ!òm¦¿`Ým¯÷Ác5E2ª3°„ØH`ˆ¯ºâÒO\ýËícDtlkóÔ±-f7óµa€Ãî[1æ‡e"�tñ´ˆ’Îs~ź+Ë×]�1Ôg͸¶ä‚.ÀbŒ SDm³ `¯m3ˆ^D@ÔP)"Šr˲lÔÚ‘üCUi’`§H ± wtÝ×·Ì«²‹Ù¦÷dDnLDB€‚NXT#ÂÁ1θ”葺¨ d 3{a&”9Hú]Œ,¹+{afn_Ë.†4›!€‡PD„Žk^L%‚µ¶Õ�@dÕ‡8 `¤ bHDÆ‹?ßæàsÆÙ“eÙÞÞÞ\Ð4M]×ê>mŒ‘£s°‡6Ç›%®6&M„qán‘ß‹•2žˆ�œ”‘C„š!‘Xb¨Ì|Ui°±(:!¼´¨BÍmX:t&µoº¤ž]x‚ÐøØOADÈ�tÅ:µÌ’ŸyÜ‘výH§h‰ìÆùÀǼÇ>DOˆ »ŒŒu]køCc Y%3$º›¦‘ºöÞ[@kL]Õ¾nšª�ç7N½Ô¹-Ä;�ÓÔ–e™¦yY–Míûýþ èíŽöëºN“D�Ø5Y–ß5ÚÛÉòcÇŽ‰Èx¼?—¼(†£qIìA¼ˆ–„Lj,ošÚ˜>‹KÓ4/Rç!¦Iâ\�£Ñ…Ÿ8Ñô½÷„4 öÎí$I²µµ¥M³ÌsS»f^•h­5Æ0‚‰–»L"‚‡žï¸ð¦‰—..‡„Àeâ@{ÈD<‰B6MSUÕææ&"VU5ŸÎúE¬ÙÙÙ9}út¯×ÛÛÛÓ­T×u¯×SHˆv!Ý ,3îþc�@F‘ÕOöGW³¯xÀ.í÷}Ã×Üø©+.<qÝÞN~ìÄùãy>Èøî§}çÒÃŽ{·f¬e-k9\ÖZ‚µ¬e-÷.Kw‰§ïw~¯ø<ž'í…jaF^ Tp˜pdç ×DZÃ2Õ9×<Ïâ¤&"q¼ÿ!bšZÜÛß™ÍfÃaŸ½F– ŒèÖj*Qx9½ßJäõÍ h(¨`®A\ñí6n*�€g"ŠCpµ]‹’ÀaBâ‘‘ ®€C%'KGÏžÏçzYwÎiï~¹yQ(†aïã1'@eD¤VúØÇž�°ÈÇb�5¹ 2ÆxWÕ†š²C{1ƒÝ�uädˆb%òÑ!úÑ! °rÞ™A$ V oZ8¶0òopà@ô• 1–¼ßêŽ$gA^¬vô:øÃÚC%†=­¶ËÚ^¯wîÌÙ“'O&I¢lj Qއ®µ+‹$Æ]+øq• s¯8TÂ×Û9Z±ñ²gPZþ¸�@=mÃ4P"°´º8ÔÐ$šà�˜™ v.Ý)†<K�í —øHÓ÷‘™•C€ ˆÈšuB›^€AhѱwÂdTÁ±¶­5Ì ai]Øf–-…È ¥ÆŽÝ\:fJ]×i‘ë9"gΜ9yòd¿œÛÝɲ"˲4³d ×ëÍçsí¬kdɳl{{»išñxœeYS7Ø8ÇÕ"Q¨�dI^äVšy–e«>ýéOïœ9Û.lnœxöu3Žó<ï÷û ~CÖÚÚ;cÌp8¬\�Æ¢¯46§@¬Àì�Á/­ ’V-ÖnŠ»Ü!÷G<Aúû=¬Ø…çˆ(“¥;fYP`<÷z½áæÆ7Þ¸¹¹™¦éîî®F7Ôãº?-YKD꺖.{GÛž6M§ûQޘͫ­›LXz¨z['eô±ëL–OægO¡ñçþ'þ3qO…γäZÖ²–/LÑ¿û‡%gYËZÖ²•ó½K|Ž.÷ð¢VM°‚@¨8ê{q*Ô¾0ß-cÅd1ŠA—g¨Ý$˲"ÚØØ@”­­ B™ÏÕRÔÝHiaånßrÀT»tU= Y!bœ@+®-@‹옡Ðbžå:cø!`^d‹V'm›}<·ƒ},®ºom: fÕ³Àò\ÆhS£‡ÊWf}›G¢�rÆÔª¨Òçj¸Ó¥+0¶t)Êb$7 :$†ˆhÉpÔ@„o•f‘¢¤í‚XQ±ŠbóõBma¢UG­®ca]ñü—£ÕaὡwÆu ßÛÛ;~üx]ת•0Æ8瀯G"ÍØnKýíZpûbɇšàКWÆ "õÇêw5¬]|– ÈrBZd PÖ±ÍXaÔtïú®W,)¡B¿â‰{-Qf¾x›·gN¤�:tc†«õ‡Py¡ÎªªD‰,%d‘@Ä9Wä…)DDåŒäyn�«²Ô“akk+MSñ<N;†ˆ³É´,+k-!•u­nÌ|b{ëìîÞ™³wßÚ¶é ˲ýý)�ŠkÄ3 3’cêÚeYFÄã½}ñ<(zh’Ä 4MÓ41fk{CDf³Ù`cX—Õl:^¤<t­ÑŒ yšA„ˆ´@<Øijˆxù¶ÚòHâµÿÑÒGa.¢)kË´s ��Î9›&z¤xïó<oš±%;Ìçs]íyžWó2¨PÃA­š&A¨«Ê¦‰AUY²óGí ¡ÄÞqælåÅö†×Þ|Ûõÿpã èn¹ëLZäh¬ÀyûõO`QÕûüÓC®e-kùBé4°kYËZÖr¸œç]âs|’ dšñ|$¶$·uéu÷ÈÅk“$é š.ñ5wž‹lxêÄq"ªªª©Kkpoo?Ïs£ß¢€.»·^êŽðØ9«s mjšg‚E€ˆ<_iêÈm�ÿs¿¢¨ûáSï½—J`4ì‹ �@Ò…ôߺÀ9ue¢Î@Ê ì§qÙÚ«¼00·jµØ3wàž�YX¸Cx"â¬â:AðÞk,@ˆ¯ûˆ¾Sµœ¢Á… =ŠJÐvÛa·HLĈF# P§$ˆ†(Ø ;eà ì@d‹yˆÈÀ‚ºs[ô[ÝBí8 p顊……¤?ommÝyçÛÛÛI’4M£‘ä½÷Güáõ@×;Œœ2VÐo¼¶cÕ@÷éƒ|€º¾¾²%b„‹‘B±µ‰vüˆR†WçNãÀ²ú:x8D Ô™YÓK�„˜ ÊxT|…< @DuŒ€ öm ã@ư÷ŒÊ¯1„4!r¤1l©1z/#Y^x(šp” ¼sÎ9o|«é$„SDF£Q¯ŸgybMÒï÷÷÷dzÙÌZ;žN¦ã fYF"©±ì}Ó4€¨¿( 'ˆâCuU×u-IcIš¦r¼÷ιÝÝÝí^𦽼�vÕ¼´†%ϳªªzy>lΫ�Š,ïïíììl†Óñd4åyJMÓ &sð:tº³ˆÈ‰‡î|ñÈ‹Ä4��È Ê#h§ÕÀ‚~kÚ(‹õ³¬ê’£ÿ¼Q]×t6™;vÌ9WUÕööv//\Ý´‰ 3“1]ô �i–ùÆ9çZ•¢:Ø^©ýáàÔÅ—}æöÛþ࿽=c0HSæ½O^yżnÈšCšx˜¼ç=ïùœ—\ËZÖò…)×}âÿ»›°–µ¬å_´œÿ]âý×|àó× ƒŠðtqç;–Ž®WSY<oÿídlђκžåÉþh§®›^¯h‡ˆ½,‡!«/n[?,œ/„"%ÒÆÏæÄX/°Ä£^¦mÇ‘½cxoqA2§üÈYô@¨Jë À"CzŒÐBKô¼Ô#}îœó]’pµ§�H;ø x}䥑ªXœÌ‚6__ÍÌÉCä.—½à'¯”YY6m—P:Ÿ‘…ʑٓÆâC @!j³l´ÖËÅJ“Îu_߆;VHŒÀÃ�†vJçÒ5o‘³"0 GËÁI€~¿ïœ›N§š‚Nº0t�®¬TžÄ¯ÆÈ@WWÑ ÀùŠÈRv ‰Ú¶¢}8tV§ž9v‚Ò‡ ¨¢ëVÙãZÐDƒp8ee –Úq1bðÕÓZ¼‰¨ñjSôë:GÚU-©Ãˆ„ζ‡–e^4ȳ~¯GH왈f³Y–eUU™"ÙØØØÛ;söÄ©“Y–MÇ3èNk“ýýýã½ æy>ËùÔäN­)-²o81 ‰8D$0‰1Õ¼4€`ôŽ™07n8N§S$iªJ<EÁÌ£ý}WÕ›§/,góýݽÞŧU­i‘æeå'àÃiáÆµƒFD‚+:w§ƒrPA°2¤ŸÇ“HDàÛ ©ålÞ’4æÜ¹su]kå[›wÞ}×ÞÞÞÉã't™åyÞªoE$MS̓¨dµ7®ëm¢Í&¢£¼IžìF‰M_xIÚp5™T^6Žm5®lnßqîl–ýâŠ<æ19ŸbkYËZÖ²–µ¬e-ÿß\ù+ ãç¡W½£”€C-x£(Ö²WB`&«QJUI/Ë,cnêz2c( šÖÜ��ÔEØŽIËÜd™ÛT^qãÕsµÓ6�HZŠ€� DjˆÄnºT‹úoë%ÛuÍ{¯‘½!ºIw}§ I :fNÐ��" P›‹4I; ¨'±NÓ4à‘áP@CþŪ¥I ßIs6ˆ³æ*W(NdH Zs›rq™y¡‘áL±ÎoÈ{«N]'fZ)Y„!L´ë©a`âÅØ#ü J–ö£ˆØ,mÈÊì�¸Y„‡ôÞAä¸sŽjÿ â.ñç±cÇöööNœ8¡q×àèÍ,ïaa¦."@ú18ztËé(§ˆˆñCŽ"/„ˆ ¸L¦hçH©ÿh3(Xˆñ“Píø¥á…Á#"¥„¦ˆ ‡…•‘Y<Yè1u·[úG¯k?è5耻Ԍh­µ$BÆuŒj¼GCD†êº¶ÖúÆÕu]ô{™Mœ0!–eÕTõ ×/gsB›$ÉÆÆ ,çÀ¢™z½ž±v2™¤i:›Ì6š¦èõŽomïÛ9wîÜñSy""âQØ»˜³ÄBM%`­=qâÄxïœxv¾&/EQ H eÝ”dÀ{ÞÙÙ)EnÒ½ÝÝét:‹,'¢ù|®:šÔa_—¥xo ’�x¦V—â•”€Ô«FEÍòKar¤ó)Ó‘4¸à¡´½8^»Uk»‘Û„£I’è’˜Ïç›Ã^^¤y&"Y’~voÿØÖ6"Vórc0Ì’´,Kçœ÷^ãŒ@çé¶Øb%¼Èª¦[êª*†tà)•·NœáÑdÒöKWc@šCwÓZÖ²–µ¬e-kYË‚ ™é`‰3$ÀÝ{®4øØ+ttαó1k�;B¸t^²!­:�Xk�9Ë2$™ÏçIj†}î¢pdž¬¶­²€+— ?Ä $n*wŠ-ØQïLd± åU¥�ËJ#µkÊ›_„ÑÕ9®* ¸tôu” ¢÷^üj;M'Úª6»{]7M£`u¥SÁŽªîÍÇ ?¼:Ôä Ü ¨Á#âÏê)ÔG£Uø,´Eqå"¢éþ„yQ&ž©Å:ކn¥¡kI’`Ç6©¶6LÓ¡±"ñš‰K6MsòäÉétÚ4&„õ£ê‰ ½[Y`+ãZ®rÏûqEVjWuA1âa\ù.F¡(Â?¸ãBI]]j«×Éídšp8ˆH¢ 5[ GMá]БJTâeŸ ±ZºÔ!Þûétê‡]™Ê5MÓèÚNŒuÎÍç󺬼÷â< L&c±Ö6MS•¥÷~ss3I’ù|>™L&“Q–eÊ5kÍÞÞÎþþ~’$ƒ~a ›¦žÏ§ žsUÉ®ñ:€Þ{_Îæõ¼ÌR›©:QN~QI’L&mùÞÞxÞÚØÔ¥¥#¬y@õ(^™5-c5'+,¦@åh€Wu+Û$,Å•zè4…·Ôe•e™fÕfœ<y²( "*Ërkk«×ë ‡Ã<Ï˲ÔHˆèœSý‚ÖC€::•6IŒ1ÂâÝ‘ÁÀÐbÓ4ãÉ4ë8ÍÏ̦S�—%5Àx<ôz°’‚x-kYËZÖ²–µ¬e-��`¸ZA)úÐG·v¢U?ä`,nÿÂm3öÌz±^\ë4•wNPªjŽˆIb€½+”Ó’¦EšK/öÉŽ“¬ D@�‘ÀñÞ sÐSø(Y€0 KGõ_ÆÊ;�5aE—c`a"ßAV$ ¡ÀÌì¼~ý„Yƒk˜ø®ÙDÜÑþ…EózY2–ЋhFÓ½”ÄÄ!jšF/ú"Ò4÷^Mv5iƒ1I’tI[{8{gŒáÈSÛ”@1Bè¦ .pBHŠ4úÞÅÏÌ,Þ (Ó[D<{kmS7y–×®©gK&!Ó±µÑ0D~õ!9K`½+ÕÂ9×Kó�`QÇA×€ÜKFÑe—neI„È‘Aë°Nž<yæÌ™­­-ç\¯×sÎ9ö+z“£Ö[WI뇢ª B):øªå1‹I¤ÿ ¬ŠË!bbŽÒùàðrƒ•Õμ¤¡�U„¹Næ½×ª @y¹SaH±ÓDÄÍcfÏ‹³"0ôp€e……´+ŸÁ¢š$" ô ¯ˆ‹y÷I𝠲jn¼Èb_4 �XkÉàFаŸÏçHD†ˆÈè§®n½^j-"úÆÙ4ÙÝÝÍf'Ožèç…ªF`>¯¬1“ÑØZ»½½M„ØïõʲìõzÑ5Õl6ÙÜÜÜßõ“ÑèÒ ®Ü9ß4E8™Œ.8u,ÏSiJð^J@’@^d�P—µµV|“eÉ|>-Šlk¸1'û£áp˜¤fwïÜEŸ¾öÚkç³Ùp8¬æå ßß9{Ø'&ós¶î÷û-+ªnÔ hAfßh†Z°£¥+Ñ2–vëqç9¥Çoœt6æéè´ ,ý~wXê²rY3ŸÎ R¿ßŸÍçišŽÇãÍÍÍétŠZ9MS�ǧOŸæÆÕó²·µUUÕ¼œ½^XùªD°‰uMk…ÚÕHÀì±èåÓª!Ìó¹0c-g”ù¦²çÁp-kYËZÖ²–µ¬åÿ—‚ Šqû@@D¤Å±™ÛÏDV ÿ†JeÙH ùö–’ª„†ÑG„šÏ,„² *Ctá[Üü`{9º~ˆ¬ÁAÅõDn‡J¨Y–IK·Ò¸§¡GáF«?‡ náFÛ–·mÃeúƒ!�5–¡ohwNàØ™‹QCD0@C.<ÞD4€@WCh@làÕˆhÉÁù˜#—ŽÎ¤o¥®kABkŒ%c@3’ áPU� ûÕâÊÌØ1S,™x(DC´HÀÍËÁÆÇ£½2•ZØûÊ謵I’œ;wnssS-ŸhÚøðq%÷°´ÂèÅge?ÆÉPèNüÃAvþ&°œäR—sH½¹ø""RDˆh?êXñÕ_áÀV Uý…ÐÜöKÍŽ«B V¸Øt²,®|¡›p>øeÄ'XXÛñsf®šÊ #"%H,N˜=‹E›eYíyg4„íím%ôz½ÍÍÍÍÁ3W®QgvªªÊ{iDß4Y–µ{£ý,›mn÷ÏùÉhê}ÓT°³ÖæÅ`2™Ôó’}e¶†Õ¤ÜÛ9wlc8èåûûûU]õû}cLUÍûE¡ƒÐ4Íþþ~]×ý~ÿøñãÆ"îîî*£ass“™§Óižç£ÑHƒkBç£qèц(ú”)xŸt²tn/ót( ¶b4»dç�mÔTçÒ4Íó|2™$Æcö÷÷Ïœ9#¸È«Ôm^×uš¦Y–9ççÀZ ††Ã¡1Ôî5DkmÓ8ö‹t-Ѻmÿ �0�“1,à[­³€Ç69ëZÖ²–µ¬e-kYËZ�ã«Ñye:8ˆ ‚¬è[oó¨ðªï@T'²0´ÁÎÛVižvd�¢O·Lg.^Aþr€’Mê3{ סÈ-Ü/ÛÛpŒÉ[xÓºà*|鮡L UÅàyu|œ§I@œ¸Ñu©}6@NDióƒkT?�DÀ,ÌÎ %‰õþE¶Û@úÊm†-#¢AÔqó"y©ˆ+Ö…4VèÍ-Ä]ùxê½÷@HdÈB šÑ´ê €"&D+`F`f@BDCâçHCþƒ¢DDŒYbÍåH@ž¡…ñ<®4 Õ­$IRŹsçÔ™© ¨y[&Œ¢{ŠÁ�–ø `ÓF®hãÅ Ó§ü½_igp±ŒMÞRD„È��±tÎKú]ÀÅÿé÷uÛ†_@PÚ}È8DcÕ%AB@$@ÑÅŒFŸ¢Ž¿j: I• í!à%Ö´£ €Ý;hL˜Ù !¤å– ²gl©˜g³3WóÒ$é|>·šÇ¡ñˆ˜$Y?/fý|4åi‘%)"ð|>?˜¦Ù,MËr6ôŠ¢€½éd<ÊÓáX|5ŸÚlP–sc=Zc¦ÎclBDÄ΋gD”¦iú'NXkûE=jF“Ñû½|Ð/F£"Î'ÓÍÁp:5[[X²$aç<7Æ.¢˜*…ˆ¨>Zc"�©cxïI hþY„Ã�Bd‘˜G°²_tÆ"c꺞Íf½^OšÄŠˆrijxÖ ’ÌœeY?/¸qˆ˜e™1¦ö.ÉÒx…‡ÓõàvCD$âÆC›UW�E„™=HÌOZËZÖ²–µ¬e-kùB•€5–HËÍŽ±Ç/.Œo+Oº 6Àøç•VÐŽ~@€BÞ‹ "[i3,´µ-¿kwúƃ%WðÒÁV�𢿞/õ Ç+¨,ÂØ«¦N} NÅêƒÀØz³ó˜€¥bG}—ÎÕ�š¦!jY¸Âl`齆£6Y%F„+·ä€Q[ëzÔ» µWǃ)"â™Â*À†Ú,s„=+•�}�šª�$jÝÝÕï# á•u±²cìw!t6Ny°$TBi–»Á`°³³3›ÍÔŽ;DHÄ&ˆß»ëmË ¢ åWØ(GMÇâ!.†"¶'¯pîqhÍÀËZ¡¸æƒï‚Å[|%pR©å°>+ÛˆÞA Ô¢( F! $@Aðù"Á»ÄÞÌ\äEÃmüfÆÿ·½wÙ’%·fæîqß™ª’T}º§Ïœ>½˜ÿ›ýœùž©EÏbjQµRRÞ¼ˆð‡ Ì3÷¸JÕ®O_ê*ÒÝÜŒF‚ ^ă0ç”)Õ¹–ó¥”òêÕ«Zëñéùô|ÌD,òõó篟?#âÇq‡¡´ˆ"­Ž �èÎ/ïÞ¼ýòøõÏúéÝëWïÞ¾þø—Ÿÿáûðp¸»\æyžß¿z·• 3âo~ó›åùëãù«ˆì÷{DT¿úyžõ-"r:‡Ã0 çó9¥t8 ò8Žÿú¯ÿòôôôáÝ{�8ŸqÒZ�†ÌlU�@ Fc®Šº##¢p_Ö¸@²Îê¨î9D5èÀm”óñx¬µîv»œó8Žwww¿ÿýïŸOGÍy>ŸOjh£çççW¯^ùÀ†aH)].—Ó|j9àÝ0 `‘GÈB´¥çJ™bµ°¥ @ œÕ¡˜õ=7Á÷ö½}oßÛ÷ö½}oÿë6ÑäÓ1Ò_/dÿ/jò+!>ÞãºÜMû¦‡~=xPJB�H ˜ûYeÓIô©xâƒWêMpi•AÝõó~l»Ãõеä·~z·Ø|Zõ½«Shw|› |F"Üú$"Æž±_¬Œ|Sû UÄÏzì©…ß–Å{NHŒmúD$„€˜r"‹$78w ©$0K ŠYwÞÖãb;ntôˆz×µu¼¦0 POt¡2—:ä¤JKãUç× ‚Vý¡Á¤rÔö…ÅCßÅ3®Û ëšö8”v‹›C¾?u½ŽÝ%}\ïv»ÝÃÃÃÇÿîïþ�´ _\}\Ÿvn1Íò8l.Æ~€ä†¹áýûÈ{JjIµs_qUüt`½Äˆ•´öBÐE<Ò$^ŒÝÆ1ü;šTBý000²TG@ÀD, �’(ë@1TRì±ôêVÐ÷ a—4Û“0‚ ÃPÎ�8ìö"r<Ÿj­»Ãž+œæ‹f(LéB¤Ö-yxxÍKY.—Ëé„)!â0$Ý9¥Ýnú×ùSyûöý/øòôôõ°?dÉã4ÝÝÝýô¯ç}¦Z"È™ŽË²Ì—LiGEÍV ) sÎüë1gß�� �IDATîÍ«‡‡‡yž+�3ïv;fF_~þøpØÏe9ã8"FM"�È�šúee#…–ã––1õ½ïT°aJNoq`ÊŒÍ@àÙ+5 as͸»»CeY.—‹ÖG”Ê µÌ "jÉCýµÓ^€eYÎË<Ž£ç¹pâË¥?�’±UA(I€�„°�W�VcÂ÷ö½}oßÛ÷ö½}oßÛÿ²m- E}(Ë y ¿ÝN½æz£»mCЈb‡U!J–@È è…탞V6#|©¹ˆ‰á‘8Oïw=kïMos2¿©Iþš+›ëRÙuU´TêUg�ÐØû”Ó\.êº �•kT)žóZk—Ñaý–¨Z·>¯ ¬íÚö±Y¾ì™ }0D$Lf€ "\;ü3#�!j8@BO(("RYLÍ“P+!"®Rl®l1ö ñÈ _Öx[pûß`‚}ÞúÅlÐ ß¼yóÓO?µšð"0õ]`nàì¶¿YO}1¦™Ð›Ebífð9^çVh£µý»ñ¦ÑÌ—±ôœ&q•¹–ÍȯiÅõÄýbŸÝ_Û¿UG’„IË3´„*  ©ª¤Z«……E„°¿@®zŽØ«ùDçy�ʉr‹á_x©RÇã1ç<¤ 2¥<M‡ÃAÓ£–RK)¥*Ê8ŽZ’ Ã8Žc.óéaxs>Ÿïîî‡ÝÏ?ÿüöÝo^½úúéó~Ç»Wï»iY.?~üíšñôÏÿãg(eG-† EO"} :ŸÏ§ÓiÜïN§SFªµ¾yóæ|>ã¸,Ëù|ÞÓ4M ‰�‘yI)€@E��Ö27×8E7™ÂºH‡˜Ïb‹\I”<…„–{†aÇ/Ÿ>‹ˆ^ÕZúé'}|Çyžõž»»»–w�ÉÓ,Ë‚9½~ýšrš­E¦”R"Zjt(›‚€J"IŒ–"¢�‹‡œü{-YßÛ÷ö½}oßÛ÷ö½}oÿó6lâœÝ£'&Ëíw£ êáÑMÀŸºÖ3ýÕþ£Ô•Ýú©š{Šz« ˆ¸)¢åc†›]›Å¿.mGÁFäÕVAÌРf ÀZ«+[ݶ֓­M¡¯„Þ°vÿngÝ¢¨DBHÒ~õa7R Ì x^7$­-È�P™+³¾®‚�‚Xìw ®ïÈøxÐ¥7*÷M5£¯K©ÌÁùŸ›Þ­žX5îÝW°ÖŠ–ŒÐÜ›ƒ˜�‰ˆåE ¼OíVv ¼JFà+¯ßLmû/¥ìv»iš>þüþý{?]•¹9H…XÉÀÍëúðÊ¢±Ù›7Çé:Ú{Ïxa“U§ ×ã[6[ÉÛK椿µÝ„p´28²!2"B‹Šå6ª;�h1 µ9"q€L$Üj1hɈ„™ˆ™O§Ó~œ¦iRýv:ŒÃ4¶¢ƒÁ²s:ÏÌ<ÏsΔSÒ™óù2Ï3åDˆ?¾ÿð‡ãùr¹¼ºýñëüüü¼H¾{ûa¹œ…¹A­åét<¤”Ò¸”º, "@B<ž—år9Ïçóð/ùË\›Iqžç)g"BZëŸÿüçËå‚^ìc˜3_.—Ãá°, ú–Šn |é q”„ÁoË­™ {Ó *ãXÓUµ\.—iš4€âÝë7ŠðUx̃ˆ Óøõk‹¶R Q}h4—¡˜oH¤<DT¡Š(]ê±Em H" HLÂBŒ¬¥vXFumûþÛûw í÷ö½}oßÛ÷ö½}oßÛÿìí¶ÆŠ(Zé ˆàªÃ@F;½ÛQF9þ¥WnH÷ûQª�’Ô]8À¢˜xSùðWxõÆ.•%äút÷ZãÕFÍ#^š7o-&lE¯ß"WÖ’JÿTÓZ+&Òg�Ð<oUÚ©r¶ƒNóœéÉ[«ªÈU½¬†DÎYJõÜq¾Öb'“Þg”ïý éÁ´e5w¥D¯'õkb)(R¥ŠÔj­ U˜¡å&t”bfF Ð*•K)ZˆMOzÝr‘RªKwæwEºEOLð¿c£=$+åÀÜë÷ÅiJ( “ À˶)G°RÊ4M÷÷÷?ÿüó›7oÝèçšóµ]@!¬zæÆ´!6¾ÄÁÂvè» 2æU*G�X„J*W`4Ó^Èç·ÒôÖñn6Š«WŠ¢_!Ãío@5¶h¦aî'žŠ‚«B"b”ŽÏmÊ„q9´Gý’ˆ�*W( µ`&ÂLR5Cá  öj­)Ñׯ_Û ø»;èÁ¸f ¸\.9çœó²,™h©õ˧_~÷ÿáùtÊÓîõë·§ÓeJü°ß•š¾<?A-C>|ø@0×ZÏçóÝn/˼,KÛ¡u©µA]x.—eY¦iÚOÃóóóù|ÖXuCR^–eÈyžgÝò ‰KÅSJ—y¾\.÷÷÷¸Æçˆ0‘d]C__]HuCZùR!äRqA!"RNXññé)!b¢7oÞ|’Oóùòðð°»;PNê4¡]}ùòå<_4¸ 2_æ¹Öz¸»#¢OŸ>}üøñîážA´z¨:\¤a¤D\5ŸkŸŸ:Òp[è† šß‚™iP‰àÿü¿þof¨"ÌälY.«9I re¶>Р3F@ wI(µî¦éË—/¯ß¼¾õú§Ÿ~º\æwïÞÎçÓ8Ž\™Í¥(åY–%儈¥ÖW¯^iÒ$ª\Õ(ÓÈPƒ¸ÙÍu�öÕٟ߯&?½¡QŒk3_ØÝ›Æ†Ô|Ù˜™•NÞl\+‹ ¥^¸—Zê‡Û{~Ã.õ}`Ž;בSÙŸnßö¹®ÍÄBò’×RR#—ƒÔÚ&ì ±åbm~iónh2l@¬mßrÍDÔý©Ý&bwYN|{µ»CÖM›ø‹� ÉuPqÐ"2ÃñtRæÊÇãñõ«WRyž—ÓéòþÝ»KY´WL$ÒÒ'w/í[áb�ÒJ»�¬ü53²V7&YW>òææþÎbôí„ÂR[¶f"Ä› ë;¢A)+Ãh¾½kåþ.w³„>hÒŠÂj)ÞƒvLæx~h#KÄ1?~ˆ|\@t�îyƒu†Æ›~Пˆ�ƒ€� ‰æ*Ö¼×̵*¬J©D”†AKçèàY²M_«,)¹ƒ0ªÈDµ4øzQ0äMô*Aw­—5\$@ܸœ[mDzÇ"’Û?¬._Kþm¤ý©¶OÑPˆ……ÓmUè%¹Qñ<âšè¥OI³}û ¸Þ/+îLÔ‘?±h=Ž=ô r«P½¹Ÿðéò·@ÀÏí 7Æ”"dPÉ"s5Ò®ÀÔ4h¡Ã¾/¸ µÏ¥ãJ‹a·ÀÔ&êwñÞU5­ÈÝ6u  D("ä ºýÛé­ø¬Kž~]\µ Œ àå H/ Ã*y�а3£oŸ”¿Ø ЛñëÛõ?"Ž>$Kƒ™ ¶Ïj¤!¥AJ™›XP ‹pUv5ä ‰æ²èÁw)e²ˆ TaF‘œ’û5åµÚ6UæRsÎ U™‹ÖáÖüZ¶Ê*s÷öB}ºÎÂ!øW�ƒ~û±$›;«Ý)R¹cÎ4òªpiŽò˜EÈÔf—j�AÍH¨…Ü›'¹²±ˆH­LD‰¨Ô^h ÍH�„+ˆ€0³•'"‘êÉü[©x=úãR@³yé ’”–Œ�*£HFj#AºœO"¢Òæy¤ýÝá<«vÔKÜëÀ”Õ·è}D$H¤ìí0—A å\„�…k DiÈIX¤FY¸ BB¸œODTD$%JU­$, ‰Ròùh§±Öêù Ä")æå‚ˆ‰MEP!NÕÔ#q¥„ˆÉc0èØª“kºµË”R¶óå’RJAöR—ˆ >Ï2sÊôá‡÷?ýéçËéÕôjYŠfqÇižg¢TkÁ+B7be¬!3¨:€�@e�¤ÎäŒ@Ó˜2fT÷øFðX”…c°¡¨XM„(Œ‰µÚ�RJ*-'BLÔs;…vhP3'uùØh(*ùè{P�D´�¸N¢œÞ:1ç§Y)¥ZmHÌGÀq[;Ô µ"äÜcã©ih ‚³:ˆµ©èTJs¦`æK=§”Ç$@)˜td™@—Ë\kBH)I©ÏçcÎyžÏÇó )À9 ؆aR‘¥ž3R©k‘e~~üšòt9wó\ñ|ÊÃÝñt:Œ÷ÈuH)aN$_??ìw•‡” †Tç¥r†ñtzN€)Ón< ⧇û׌PJd"‚Z3¥„éñËç§§¯ã8V®—ãiš©üüõKÞO–°Є]h²Þ]ŠHóGЍ4¢œAÍT"’Rv"Ƶ2 sÊ™¥°0€ÌUëSàS“ˆüî?üîùééÕ«û?<~)Pò0ç4dÝq¥”wÞÿó?ÿóû÷ï/—ËñxzýöÍóóóét‘ý~ÿåéqw؇óÓ‘…¡òÝnϵJå Ô*`  H˜XÑ“1"!¨;rVáÊMT…MÓ^Ë‘ªæ¥¤‰d€¤»>!!¦x@¡œžÏÇ×ïÞä<ð²dDš†Ëé"ǧc)E¤íôœå5ÏË<í÷°ßï/Ë\ʲÛï5¥ï.Y&nQ¡¼ŒD$Õ@I�RN�PK…ð ZÙ’qêÙO5O &M«¨ÐrƒÛÕ4! *ºÙ)6r¤k>®‰t¡0ÊÇ­io"Ð\[%%­XQûNï4Õ�"²ÒUx`³Ý4!•yªfØÜý‚*ÛFÖõ™ ®õ æ«×Užb&l}ÜuJ´ u¡ 5/ÎéFj @oB6º Ø;+¥”R©K猴HÍ”–ù¢4€¥N»"žÏKJ ãÃþ—Tëc¶åá¶$ ¢ «†Ë[@¨ÊŠˆ/•Éí^K IÀÄýú‚å¥k–®)ʉ~%JbAU†cˆ¤vö%F„ÆÅ€0wD‰e›èD£¯ÚÀDêM.¡ã·©ÖA\ož¦*‡‰�ýëz§©d\» ¨ÄÌÒ4«D^\6 C­E晇qL)¥*\™‡œ‰°ÖÊ¥¢RWh†€>md�kðjÚ†å# Kfú!„ùePu8i$Õ-6±$®¬&^“ß8çÌFã[ˆVv=D+{³VqUßYe~Ze-“FCtHí "Š 6›¥‡UsBD}lÐmíÀíí¢r—zãºÄZ�,dÀ¨t²µˆ¦d�@îäÞ¿a·D# Úä³­7¬ "mf.µáv ATUé»ÐþËBjÀD#ÀŠ7î € Æq4[¡ˆHµ`;ldÝ2팭=.ˆÍè¯æʀؒUU®h› ÔXm[ŒæØ”À¢fá`Œ¸ªn]ö@Ú[Í%gq ¢€Å_+ °Ï)gPôˆ\ÃŒ2�æ¹r0V™É@mCJjójPÌ:JGˆ24Ûž·QÃo/“ “›&F…«m}@ÒÐqÖêâs4Nà Ù«È¡«jŸÓ¯NC½â=Æ$¤,²×oP}ØÉ[¡of¦%Äå¢{ì=¬×5êüQ¸¹!x��@<i/UËV1í!r–žgÎÚfšè1ê½™ÍLQ'Sâ¸ÓÔ×DzLá_ ¿©±\²,ŒÜuìvÌÂÂ\ü¨Å”¥@¦T‰‰HsÔ`¦DDj<THj–@qý¼¹‹¥T³6xèt2ê™ÒÄ™¢6f¦õÉÚi9®—çªé�ÔÙ5pTGh"Å M/Wkýá‡5¸úééIëºesŒ˜y=â` $VÜpŽæÄp×¥™Ñë J+ié�Ññ'TqÁ¬) PE×hP•[µâ+(mš¬ÓF´¼v¦ÐŽJ­Ž]qi8ØJb'±gÍE ¥.ØòSF�*ÿÀ+¨\[IÅF¦4y>�|}z$"@®µ¥y$Š'ºÔåt:ÏgDÇq Tk=L»iš–Ý8L£0áÂuž/H‰™˜SJ sÞïviY>?}}ûþÀt<ùôå‡~sØíÏ—…¹�/OOO3—ÏŸ¿þý¯o>ÿü‡ý4-—³�@•e>� '@J ¤»Ýn7ˆˆrÎ,e,õ|>Ïçs­u7MÃ0<¥”„¸ßígžPó&ŒùIóàu”‡ˆ Å&t c[i‹“.…8 `ad`æ¹–”Ò~¿'"™Ë²,?ÿüó?þXçZü” ðÛíÆq,\k­sYN—33SÎX¶{±F´ð LšnâÊ!0¢VÆP ÑÄ2eŠ A^ÔB›A Q±"îVÄÆKÅ(‚òTDdËšÆ9#Ò0 \ë¼,cΈ„Ã~Háp­Š”Z…9§œçyΊÄ9ù±Ûj*V#*‡ŽR§ÊkºÑÐØr²f’(�øg“ V<l›šA뉅…‘È„>qáÖ‘kú^3 ôaCóTëdœ…ÝkÀT!èö¢…SÜÊàãë%¶3@t%Ù´D�X‰õˆ©Z±Y •VíXk�¦5:'ºõW@»§µFÜ(ÁÖ—³¡7„]fX ®éøö[=ÛGÞÇ+€Í5¦c:[FÎ9§$ D(Õ«1À ºÑTtcDÓMû Û‚€nh›ìÆðns™.§]²¿7ï¿>ÝD7¾\?Óhˆ}ñqºü--àÆÊß§CJx×­Á¼´ë6¯êJ\4+Ä .]Ej~Uþ1;#&BÀ¡ 6™K­Ì2Ï9§œsJj-‡ª*¡ÚV*EÀö®Õ©ûFZM9N¶ÿ…¯HSÛÊ Ê‹Ô`¸¹ÑCÃBÚM’‘¶Q{h³¯6krcp÷Å{mãÞ–®…½€lÆ}®ŸsRÈ|¥Ö·Á¦Ÿ­Û ÁпcSµ|R«Bu ëøi§@€Õ"ëRtÝ>ºYÙ¬q…Ç[r¹3B·ØßöÍÈŽÃ.ø[[`‘NÔìt)¨OÀ<X7o ¿ßI#u8¿bÈMžÖ•j›Ðz`ÒÑž­)ßêµY3Ÿ#  ´¤SD¤âöÍ­†¯ET/(Ì€XY¨¿•öaê¼AD�µð!ÀZ=p±¾sÇkøB‹@P?Ä2#lnؼkóµiYá~Oª÷Ò`6CN¹qî›§}ûI€ŽªûN[ã,0Nßÿ¶Þ<Oƒ´¢q"(M ,ÂU$QJDj#ˆ>äà¦ÓHE„¹€ÙYQÕuƒ ""¥ $j¢ŸYX=Ìô`”%J`Ké·YÓ™ ]k\ctLuàøÚÕÍLK–êl…<|Td‰ÖÅìã8^.—RÊoûÛüÇÜív¯_¿žçy¿ß;0‰º7rÇ–õ×-V,J¯Ò§¼&a7ñ³{aE‘Ýñ3b>v|ìûQß kÊ+fâÜnÕkT¼žàKmã·)fà7a#+~ž¶¥b3JÔòsª±&­Ò¼­& =p5ƒìï?þÝo.—Ë0 À"EÎçùÕÛ»»»»a–Z„͵œ.g®""uáœó|:_.—û»»Êœ‘–ËLÃaÇœóÝÝ]Nôøñ—<> ûý~¿Ky(¥<?Þ~ØÀ²,§Óéî°†q™"æœK) é|9Ÿêé|> ¾\.—² b(ç4bZ–…çe¿ßOcžçy7 _—s­•A˜™i½UPž÷#>T±m ºX€"µ{Ž�€¾‹¡ùäkWlx¥_Çq\ ãøñãÇ÷>Ð0a­U列sΧÓiFieP‰ .KJIËŽj”j9f$BXvŠ$ú‹2RÓV$U}{(Ï·iI<w ógUWj®d�‚„µò�¨.Hñ!ÂyD %†1S3  p]æåx>ë ‹Ê)Sb¾Ån|'Ú€Ðe4Öƒ;Åp=B�@Ë )%�áæ¨I/É /Õpƒ©•x ÚRùF؇ˆh�‚¸ h—Óœ(Zϸ6¡eãl,kq±Ko[A-Jºˆ\mñÃK}q®T¾•\±‘Ó7^ëv)b¶'D7î¸U¶Í¹[RP‘´õ‰«ý§`;±÷ÄÍHC`Y\ˆˆ°¥¼!v©•(‰@ó8£tO¼kì`‰Ë~nç·7T Ýùqñ`q-†¾tpÛ¬¿ô÷ßôÿdmsé†Yä-àÃJÿY©ñp5±]c`¹–Ln<ù’j}ë‰tcĵVJ$ˆ‚ ÌjxS¢ª³Z¹”efÈ€‚€‰ëz#BN©óG´ `iÝúv ùNx_Xö6l\m@ÔH¨á:µ"7.Ø”ÝïÉÝ.Z—Á˜ þÕlDëU[5ÝnýýáÄvÂFœ[=~ uÓi®n…O°ßÄäI”«ûDaB½uƒˆõ«»Î Î 0ñ÷F3ƒ¸˜OÖFX]‰ë: ¤Ay»ÐΜ]ª\ß`p#3NUiWsê*ÀЊ=ä|òÖ⺗G³nØø9 ]hwç´ÖÝíèòø!V÷Û´—¶@�EüÜ¢‚5|º r¸eBÐü�XàJH¶ÙGì¹ñ^ 'Y;רÚReá †ðh1?7hWP6©ÖZU¦G`´Êö¥Š”†q¸T«u¦u¹½¿ŸÛè$ñÅ· y[¹F>o^ß[Ÿµ³q! ®œ˜ƒúµöµQÏâP7çœ]`µ÷ú³Út3öNº‰í*æÜ­¤þST«šŒnQ:KY< žrJ Vž0£•¿z0sÄ~ü]Ì,ò­ÛUé¬Õn“G´ š˜Rí@H͹©þ:$ÁJïm6Œø à‰U½~„Q_”ë�B ƒ€pˆë{6ȉæY°,‹ÖŸÓógÍxG–>ç\×bʵ¡çµ>n”‰ÍïÞh]мÒúâÚé4=-EÓLQ÷…ù#DÌÇ`¯Ý`¦ó TÅtr½smø`ÑsCøøýׯèÅíe`J4dÜx¤~ïãàh¬žA ·q7I3~QÊH˜•a|þüùÝ»woÞ¼UÝçºÌµÇ”’:#T„a7 ´Ëã0S)åršsΟæå|>ë,öûý—/_þþÕ»»»»²À|¾L‡ánøôtšeØNoïïüñÇåüér¹ìwwÏÃ'DL€ê>sww—êy^ª°ˆœÏçZZ*Jp>Ÿ‘¹ŽSÎ9Ã4M㞟Ÿk­ó</µ¶Y×:ä1¥Tæ \ÑGx +Õð ª&;t¼Ú –˜S7K©³š ÝF»ÚívZš�’Àû÷ïÿðoÿ6ÏóÝþ€IÙ«çTJ©”òêþA3¸ÛŽˆìÇÉ3MFœL)iíV0þÒ‘š+EDÕ~ÛÈ&¢Y”þvÓ€…ì5Érm Ch AÔ(‚DTÊ’sVcâ0 §Ó3Íe)°ˆ³ �‘™ã óÒ0 B(˲xÈ á_àwm]l‚ˆ} D¤á˜ �h¼f¬7|½_|Ëv«ª¾\í>ïÐ¥6o‘[¾K×È´_—z}GlÕšS â–þG*wã½AœÅÕ ¦ÝnD#Î  R¥� ™ÏÌüäj*ŒGÅ›Ü�…M×k"H~íÔµ þ"+Y¼ÉØT&ÐýÛLÍ´”2ì¦IïHC®·Äܶs >òÝŽ\zÃ:U…–Óp£‰UV:‰}pP‡\£ÃÉ­ÍÌ+®ï¯ ï·‡ùí§ M-S»–­þ_y¯aË–HûgDt»˜Œ×Ž@jcún)ÔŸˆ¨–R–²”R0§<š f©˜SÊÃ0°ä^ÍȧèÐ\¼óŽðØ ZIþbîcm_D)ÎpÍF€¦Õu—FÛMáx]Žl·•�Ò ‹ñJóxjS!»Ó•ös¶ÁTuMîì¸|m(Ó`Š�Ãß§WçÔÆ£ã¡)­ýiúª]K¼!Ð`Cuo¡£bßN/,ôV·ì¤a�`VÏAUK‚9ÏIÔŸšTÇ7…Ú{éfÔÞ1n‡‡¸YÿµŸ8aw˯>s‹aÉÕoL÷W6g8SDig½vnOà¨":áÞŒ¢ÿ¹æè“RJ9Ó™¹Î¨kùöÛìI£—4Ã\­¥ð0å< yšÆqdæÓÓóùt”Z‰3 ˆ&(÷TÛÈ-Cõ5ºØL××_¶ ZƒñþÆ?Œs šå¾yÝ43¿Î«Ê*%ž/°¬ ι`á_o5®™ÏѬ€4«)ÔSøøÖj°™c|šÚ< Ö"…)KM†‹œ2²Lò³â–Ò‰ÑTôjÞ˜›²Šˆ)eU`j­È¸™³˜\¢"ˆˆ׺ …h£©µRî:ysFK%h *˜b©g# |ÁÀ z^Fhj°?ÅW§ 1ý /Ý:´kø*",’r>Nûý¾ÔúøôôÃ?þùOúüùó?üp:tÌË2NÓ¹‰H…yCÌ7c‹Bä5ÆE¿F9m>…ˆÞI”·:Î zJ&‰o vk§ÊZ¨Ý˜œüžÌÚ` 4ªŸ™—²(ŸVp'JÞ g ÍècºÆ*W‹å1œ‡ÊNô\=¥D9fÅgDÙOr…R #œ—y°�™<)¥¹\ΧsJiÚïrΕ¾ð0í 3r„aÊÃ0Ћðež§iZ–òõË—ßf�xzzz}ÿêááá¸<-"§ãù¡”ßü懯ŸêyþŒËrðÄÑê�� �IDAT¥Ðããc-óÃÃ×¢9íî‡a,¥Ìµ Ã0ä Iê¼,Ë<æOǧ¥.°0‹<>=jYJ)K® Ëe‘!ݦZ-ê×ú‰ˆ(Èm¶ód hœ ˜‘ºÔ‹æˆ¤”ÊRJ)Dùõë×?ýñÏÏÏôËãˆCÊjÙó�,À2æAó•H©È"‡Ã!¥Äµl™ÞDÉxYÎ$”ñDB”vB¥¢BC@u“ñ]Öü|«=îÁµ"6ˆšìxžÇÝŽk) ×q·C¤K™/u)¥h`a-˜ÇqÜï÷”Råº,õ€‡”Ò\‹Êedþê&MøßhfJ‚ëˆ^ÑU´¦Õ©<›¼Ì¨ˆûžÒ jÛ곑Hå ¤DªåâH—úP�`•ä¬Í ¤/¬s“‡¶×Wâw¿¦�2KâJГp–×Ù@DZ¦€ë¾ü}ªS¬PB(§Ï×άšWšI/:*r^¿šŒ‹›+.X–g�@"aÔáÕ©Z;ŽãxVÙJLyoˆÄ"š„¯ÖJ4ˆQZ–eÜíÀ|†%õ"¼n¢ëù’ÚD}ó>»ñ­çzW© £Ù_i+µýJÛ–cšöWÖ¤_×®&a²å©¢Ùºtþ%“`ˆWtÅ:dÚŒœP؃Ã8ø‘C;*Ómîœ)%$T¯ÊL¥DP#Rc¦¯F¨ÎªÛL‚ÙnÍí5)è4«+d'@acÞ¿£D·xµŒz†×lPX\” ´±ž¨EÃØzFW²Ùu»ù›’fH¡|µúëÅòÒ »IÐò;^ –„¿6 õ.· €F:˜¶óøLáþ_¿o#.†‹6å5]°L‹Í/ ±%²ô}Úm;A ÖÅvq5ìð©ï@¼±×ìán#hØ t¸ñ� )ŵÎmOq߇[í6vÝTÚ,TçCU‚E¤e›Ïvè½4š¬ë9ϳ�@Êã0ŽûÍó¬™çq…Š6XøoÆDÄq˜îß¿™öw‡Ã*þåÓ/?ÿ¹ÌK­ì6Ÿ(Ó\ãÊKÔ?H/ÌÃnóÝh¯àø¸¿×ãÌ]Teƃ "MÙ†:¯‰ÎfFr ke,­ÙDzÈèMP`ðˆÂwüÿFeCÓ >Í~V¬±—–n@L÷ö‰‰Ù´„»Jqš‰Ý_D€EÓkÚ³æ–ßi+"&$¨®Úq”) ’l¦ ðäa`šÎ£©‘Ð /—‹úRBPYÁ2óm@f‹ñn×(‡¬1‚krÎÏÏÏïÞ½ûúåËÓÓÓ?þèp»É¢ÐãÀ£H½RD ‡•¶ààçòûiïZ˜ àvìÖß"",BšBzÆ/Wõe‹ý{çhÎqGhóãýÈàõ~´ºú“ÆUªWJ{Hî"½wøö6el+¨>íêbóîÝ»Zë2/Ó45ßZ‰¨ ŸçK)ewØ·T9DD¤.3SDD9ã~7M!Îó\ªì§án·/óòóÏŸ« “ÖÑœvÀµJ9>>Õ¥ Ȉûý~Ò—§Ï—ËåþõÃþþî~w¹\ÔL€–rÙß"P7–Ëår·Û@f€<˲°°žØçœ=É3SnèäÍ¶ÚøÁX©må¿N›Ë²äœ§»»Óé¤FÔÒãããÃë7Ä �Ó4-—Ë<Ï"²,Ëårñ5u·=Ô¢à>Šˆ°Æ´HG;Å6¯BßÈN4Í6 X ›æ£†F£-z¿}„¤9k#ŽLRJ”¨” �ã8žN'xýö 37y‚Y*#âòårÆŠ—ÓÂÌ•ëRÊ´›(õ„5ë�`Ô´IúÅm+§åøÀ©DÜüBcîbé%'ÉÝ�@KeÒ£ [ÞíÕ:+÷Úv´ÕåuOlÞØÞÛ†1Ó¸Þ:WéÏ®x¬¬DíöŠ7Æßpä–"냇@ß&MI^ Qce2Û|6Ì C¯¡.€¹;˜ÝnQ[ž¬|bûí]Ì)å”Z®~aÆ¡rm±ÌÂr{ÖÞagd.}-mðoCðowß }#Ü^xì*¾¯=øûoÞ/WòçÕÓ[»§€çÌ{6(YOíEª»–½[Ÿý‡nÅíŒ5<l.ȶX„½ö»4?;ýqÇTk«?µ”K)y¦iRN½, yrejËZAÀË8¢®7Þõ.»:¹ï³¼ ó)À5^ékÀ¬~·ßÑÖº ˆØ¼õ*’7þtóë7xåfäЯom<äiqLY¹´z;/w#MsŽ»D6DÖ<Ó|¦1‘³Âu CßzkjÜ[”BW¯ØQ|©ÄòÔ˜ü¿]/¹¡45%9lµÉàÚ„µ"KmLÎøšuD§¨ç¬�]áV[†³¶kÄ·üúæ¢xt"véŽ]hœJ"s»®7�öÿlnÌZ’‡™!Qr= D5©õf¢…&ôƒ’—Æ w‡ý»?ãnš&®uYêñx¼È€«p„V׺""J¤µX£TI ¹oƒ/žXÆ­¸QTnÞkë€ÀÀK¢šiqÜüÑYÚ5“í#)ޤï�¢&œº%ODôÈ'¾è%àÒCk`Z`;ÕÛ*H5G=¢ŒšýV@�…°}S½Bkå ðQó�t[š¹AD,Ñ &¤dTLDJ)@”SnþÜ»ìL¿hNFéPkû­°J;a¨ì X³ÀnRq“V\n´ã”±¯p«J\ïPc \e-¥ÜßßÇÏŸ?ï÷û¥"RŸ u.¯Ršf©€dËBè„kAF_ß8»kÄvÈ蕈‡®Ôá-sRŸ—ÄÌgQÝâU{ãêë7šö毭œ-“ç†0æ(¾f²µ°´Í,ÄŽ¾Ñ;ŠHEÑ™¹¤šÒ0 ) ©ã: #’ cÄË<—e¡!—r©ó‚ˆÌQ´”À0UA4¥Ç0ó<cNu^v»Ý~¿ÿôóÇWoßì»qšž¿>¦i'‚>¼þúùñ|92î¾>Ÿ~ûŸþË/_¿üü—_~xww?NËãã³”e 4 ÃÝa'"ÏÏÏ)¥1ed©µ¤”¦œr™ÒÄË’s>O_¿~ÕÚœ‡Ãááááðp@ÄÂËÓÓ×q7톹 C*sÝ Œ%W ©žìT*ÒŸˆl¾¢ýÔˆ6±ƒ…ÃÑ’óˆqµÁ8M§Ë™rÖ\ªzÓáóùÜãƒDrJ§Rj­y•€RJ²ÞÈЗÈ54ùV#zƒê¥6;d-ÊKîã©Þg~îíØn¨ˆ@”Ô—Zõc çΔH(–æÓq<÷‡æa$Bfˆ�ðr<}yúº¦ÝnÏ)çhúôã_ `lcXy,káÃf·ÕÍj’< `"P»9% õŠ<õ–×äfÃfõýIGƒ>B�Íï§è=UPì1DçZg°ý‚F âƒ"`”s5DX妶$å¦N¬ñ,–d5lëL ,›a57ã( �@u2 ÆáÆèÆPsp'ÅNo%÷©müp“¬6DÝjB��‚=ŒB}t  kÑ&H¸ù¦yج»î>€íÛÍØÒoþ&_ðxè~%è{›×ÁU?€ƒˆæSFãý¬Ð·ŠÐ¯ikúsc:èxfá+à>ö/m?yá[‘=Y9RØP‘µrH±ÄÌZA i?ˆ˜(' ®•+/—¡R’Þ£b«Õˆ %®>F9/ÀäoóZž÷LxQ Xß­ë%"Ûz+%Øfê*ñF§Z'ˆ"¥/·˜Z±YÄÍÆß Çs6óE"u²ë¬Á}Ýɰ*ôó"àl+ý…W”$µ‘n“PqÊ5f½"¶¥·¯Wо0 >S{µ]‘Õ¶>j mù”í)V/ju[ð)l[Ù{êûÀ­…œ9€Ý0A LD¤gõâ›ÁY¿B<þZ‘@ßtŠ£±qº•BÔ7Â*ªVéaÖãEðÜši_�æZêYÔ'—¹8û£úVÕ¬rÉÌL”‡a8î+3—V +!iНx`ø""ÝÿÕ-*ä]fE¬µÇØc8ˆÖóXYJÿæÊÒDœïÇF¸^¤õÈã0¾=[0ܲ½@è]¼ŽJМ7}ʺ®¡CÆ5Þ2V,i¿Æ\g¨ò§Ô¡ šB ž,"d¦ ²tb‰’'îÓk”>—ªÛ+ò3ÝÚ~ÅU\Oèà,¡`-Å'+!Á~;ä þ·Ú­› \µÐ†að™‚iãÃ0”R"X|œdÕõv»ÝétòäÝn—sþüùó«W¯žÇišÆqT˜mµ_‰®˜‡VÚÑŠ¯ÐÝà=~ëÀ`‰x("R™r‚PÈŸºÁƒ] Ý^ÙÎÅGâ ·™ ".Ë¢‡öfœsnÖG  ðR[\O[àñé«#"ZÒm@© °­ÊFD«7 PJ9µÖ÷oÞvûOŸ>}üøq.ËeYRJ`¨Ìµìò4M“ž‡×Zàùùù€éOùy·Û1—ÿüŸÿÓ›÷¿ûïÿß¿üîwŸ¦ñrþšó˜Òüø|¼»»»[fqH—K9ŸÏ?þøã›7o´7­HT¨�0¦DDÇãñëׯC¦ÝnGD—ù4îv9çz)êDRZN Xí’¸@ñÆÎlVðÁ®ï×Ý =·8ŽË²T‡™w»ÝóéHDšúA+�€î .UM�¼çóywwÀNy:µ¼I{!Ès&_ ¯Ë°–‡¹1`óªí稺jA\ÂæË Ù¨ŸÆ`i=r¤\+€€-p`.Ë÷Ó8M"2Ï3¥<åa™ç§Ç§áípØãÆid%¶Ë²8˰³È,|K„ÝA)5¡VõÃID˲P"d(•¡Õ0ë|Ë€AæªykÄÜ%šÃs+lTÕ× ýá•ÐájA¼à úbõÜc‘Ù9’÷FØdBìVt‹Q é>€hÕgM«Eƒ'{¥åÆ>|ãüɉ]‰_ý$"*Aú»tXlÆ´Gj£·ë iCŒÛAÜlÅÝé-ˆˆä”K)˜Hì\A ÙjòB¶LÔœ.¯ 6ƒÎÔVë¸ö°Nyì„âf#ô0C¦écNyÖ[Öãp‹'D²Í7 l]œqÔàù×ÚÒú^€>i.†X°|£mÒ=Jw&òñ˦/Ãu@HDQHD•M6#"JÉ•ŸV!†a€a¨µ^æ™kûÃn·”]çÄNrhE LY¡«(Ž6¨ /´fÁé,vóY$àZS³ïߎN¶ïÐ\<º ÀÖV&+l¥»¾½/þzm²„õŽí7švpëר‘ƒ°m}Zù=@�H€„”:°•ÕÐY¡ã9i‹ÍXW_6RŸ µ$¼¤mí¦oØÌksÊ"„›úW1vÀÌè%lšDG>0�ÔпvÁâ ÆÍ{ÐbÄ®ÄW‡ÒFðn÷¯/jyûÎÔŒa½ÄO¿µö7ï7Y%ÂuÃ:@t¼…:·d 3¯kº¿"BúÝý"’Ç@MÉWIÓ§D”¬8¹ˆ¾»”2¤<L#3Wæ”G ÌÓnÈÃ8޵ÖÇ/_ŸkY4ø-9íà–°ùÊJ·Qx Ðî&±m Æ·Ñ+µVoT3tÐæqm9 âIΰyéûz[^½Nn¥-TY9>Õž…~³ƒ¥”ÐRè�ö§,S½û™o`¢kÑÒ˜Û[¢”[b|}„±±Äþ¬åí“`_sêÎkûÆAkåRA4=csØ&�€Êh#ˆÔR¹VyzzÊ9ßÝßÀóñxw7—EIj÷á¯,,Ze#[>î^Ǻ¬ î»yBrWœv¯ÙöâÔôpOu'Ÿ¬Z"rβ÷Å<—,ÒW¨/:š/½æW{óæÍúéõë×ûýþëׯª¹ÍµåqtBîû\‚� &ó<“5ŸBJ©.‚ùFñÁ—©-VˆíŒw6É (í:¼”nè͵f1õGE˜np͈rÑ.ÃÌjŽÑ ÿUw‰[É­W0ä!§DHH)ç¬æanæ"•~¨Y@Àr”êöB�MaˆyÈZ<åa�L­øZH¶ˆ�HRÎ#"²FÁ0Ó´Ìóãã#Œ»Q†qÈc>žž‰RJIXN§Ó~7ÎÇÏŸ~ùáÃ{Jé°¿ûòõ(i�Ñoÿþ·¿ÿýï_½y7îïþñ¿ÿ¿ÿ»ßþñœFz}7?}ùøó4 Ó|9¿yx@‘OŸyûö­æM¬š×ƒ%Ò(ÍœÒÓÓÓçÏa™—yžîoÞ¼9Ïu·Û].ÊPêÒ#7ÜøH¶Í@ÃÝ;Láª>ó×Ä–Öꇯ2“ ‡Â*…ë8NeYv»(&ŒÃ/¿ü‚”îæóùîpЭôüôôêÕ+Y–ECf²¥¹Ìó«×¯)ÑÝáðüü<Ÿ/½¾ìJ÷X!jû€Íq »ñ+H("¬öñv—£˜@à¸ÝÅU€&´jP8J•ýaˆ—Ó™E†!ŸNç»ûû<sY–¥ì¦‰™äãÏK-oÞ¾EÄq€«Ö âœ3Š«Êâ&TÍB„HˆD¶|~:‡ˆ ÊíÄ šÈÞ¶˜>MD”6J¬ørG …ºŽ!QJ�(Âê0’Ü*|šnIMhÒz¶+”P³ÚèVW@‹ ·p€mÔ4àQ“T!)ª  á²ÌT5{ ­Oð 6I—Z–„–‹!!hÀ?‘C�±Ì°ˆÎpuÁ°Õgm$’êb)ص)y—q[*_{;ˆ`{i+=bë)àÎSë�qf„†¡ÌKÎIcΟžž^½zE”Ññt¼x¨Ì•ë´›–Rô-„žÃDTJQO›5m0iA¡Ú¡'Ê?«wnBŽâ¬ÿ3–Ô¨±~Fô2éÎÑ´c“ü†Ð�ô¼ñŠO¹Ã6ZofÊhߨw¡ Eò·oþ!FÎÛw+é4€´å³^Lß] º7¦ûFÃëP+JGѾ†AvªlÀÌ­Þ{[Ž:Gˆ� iÒú2–bc™gTa+%?3#¤–¯ikƒ«¶N ‚k/:�ubÆVEÛc©Ñ3lj=GlÊ!nAç‹«ÓYÙÃu°Zfá!‘óßI{{A`s~‘ˆ ²KGº‡zUrEÍø/žMFœéÚf˜0¢›Yl)õxÌòN*N‹)óÊ$¼¨“¬¬cbä!樊À‰Ê¢š<tŽˆœrrZƒ¶66úD¢¦ŠÁ¦€ 41­„ÀAa[Ã`Ð&–‘’ùùöFhÆÀ°Á‰4 uÓù87J¥Û‚  ¼¾Ù¼7“г6 “µ@Ì ,Ñê_ç­Šf†1†:ª€WæðkÃfÖàqÔà¯6 ›®ÜäEj¶fCa0`4¤hj†éE\A€Ð5. ª¶tkN€T.L™Y* –å2ÿò—?ýºS3Áóãã²\ ²Vq³‘f‘ž*ÃÛµÖ›cOÔÙn6v#*~㊄ãMïAÌùüÛo¹¾Ø7öŠvݸ_ÁAÚˆÃãÊ/½ÂGè>í®\³)B§nªùBS5LK$*i;ŸÏzÜZJ‘ºMŒW5qnØÃ>B½BæÀÌó<{û  Vì-´fC K£S¨­ì/á†v .D¤ºkDo¬±Á·ÍbÅ!"oß¾ýå—_~øá‡Ãá ‡Øí�¿ï@»y(Ž*øG Äóvÿñm3¤ †ãÚâúþoï)onŠw/qïDûqým3ZE�Á5rê×(ˆ×Zàz ºŠÕá£r¶'³$¢œ3°$¤j'Þ‚˜† ˆ Ðí ef¬!DX–EϺk­RjÚï�@}d–eÙï÷(8å ©¾|ùòáÇR*d¢û·ïÆq¬Ëå/ùË"_þíOŸ�Ç?jJͦ‘)-ç ãÝÝN'-Õéû%¥D©WΛO§Óé4Ï33¿~ýúýû÷ÿð¿»\.¿|ùåù8¿zýz¿ß»<Ïs´ôK<@€eY´éwbbW6K ñ®‘js[²pÍ©®?Ñù|�­³�(ÍCM[6§!1¯"n¡Ô­hK—„" v;#Å”þñ¤Ò!HÒrqq¯‚4¾IâŽÑ[2`%aa^ªšPEdG¥¥¥‚ ÔKDæZK-ûý^Ý&+3 `¦ ¦9›T×4“Ìú¶2�P$müÎôÎÔÃØšH„­kÛŒ"Ás’¨Å ¯!è)M—2¬ìÈî*6Ñíô~Å2¶¸©ÈÐr6F„• +êäÔ»Iw.8éêh íé}»óJ�PöÕ­~:Ïç:SkûÂ»ÂÆq1^ C÷1[à\é®}¤ ·AÄû”´®ür̓üzÃð«ŸüæÆnõ@’ÕñPÔPSç4)XŸ ºh="Ú‘¤®æ°j%ò" ƒ€Õ«?l2¼ƒÚ¤êH’WÓt°b~�ÙYž˜¨ 뾤áümèÇÉú°Ÿ@ôÃ9�`–f’kaM-’n0»UMüÏ7ZäÎ׫yrá%W,Uw6í×)YGgÕ±VN"€˜ÆaŸ“0/\˲@ÁÊ5s&; 2»|ÎYU ÖZ¹"RJ¹ŸÜH �„úéö÷Z)ðfãÅuO·`Ò¸ %Dsþ×oÊè+xª—ˆFo‰ÝíS|AŽºJ•È"¬DXñAO£Ðe9p$ œÊ»Âkµ=»B!¾¦ ®Û;Åþ^Ø-߸NÎF?¥pêd–ÍVq/¸ÛÜÚ¿®#;VXVx ÙâãÈ1ð¶³›4´ÅW%ò†:ý-z‡ƒÉ_#KÓÞ‡rMǯ«¿Àš0­ùÏŠ¼ÙŠC÷·óß²õ;»Iy3-°ÓÖŒÏb.L-ï½½K)GÒŒ*îôÎÎÍ̵”¯_>&ÍH/µb-D˜5Ñ4�rH Å^O" ¯´åØ’žýD¿‰³š€ÈÝc$‚R„ÁÍ®¶ÌÂÐUµ¨¤ñ-6›3æoßæ'ÆM[¥ƒúƒ±€b•ÿï5Yo¥¥ !€Fö÷ö®^ ¯d±ú>£RŠÖogËnæÓÎÌ( "ÕŸUmS"È"¥&h˪7/Ë’õt·I·@€jR<ãu~cs¤á¾:Ò[äð—u 4ûŸßƒ±D"°¹¸2x¶�4‹~S0˜EäÇÿôOÿtÿúõëçó DšÉœƒ# !x¦âµ ¡°¥4£[ v¢ë8‚¶/Ò*‡¢v5Þ×ð<²YeŽè0ì>Ò-o¼9þJD�=ú…Å=™›±�€¬šMDÒ‘õ76Ç–ÊÀâqdÚµ ‹ I2 ‘z”ŠŠ´ÃJЕTK(iåy©\pF†œ@,Ý4MÏÏÏê�/RËÂ)%bÜ0ó4蹿¿¿œÎ™g"¢ÇÇÇi�µÖÓéô§_þô‡?}ú‡ÿýÿx|~zxxGY.ã°CÄÒ‘àr9iò 503ƒ¤” èÆ‘óéy>_¦iÚï§”ðÇ?ýˆOOÃ8î÷{´LÚ',zŠÎ¡j±Tn‚cX8É«<)·6Wc‚ªf ŠÖgˆ2ˆ:’ 'J”sž¦éùñEZQ@àRÝÀ¡›å¥TPT_;É ¬ÛòÇÿÍõÆq¥³¡–¶@ÄJ;'F5Ô#(Îëá4T¤£ý%`ÌêÆ%RkÙí^Gµ� ä!—RQ¤”r¹\~øáG-\/Ì”ˆrN)10³$êBödÅ}.,zB�ÈêL«¼-PÈtlÕú,CŠ>¯Â"úÉ*6áŠ!@SËm@6(±ÖT YЋ£QSÂÑ%FëñªòàjõÕ΋Nº/.¨b¶Óï«ö¶™E—ò£Ó¿íˆÈ€Ü¨sêÜÐ^V×gð hÒDhw‹ðÿ#%âèR®D)3.¬çZcÔuWA€g–”¨Q °•èf2è_€ÉRÖ-6Çà.~Bscb#÷‰@ÿ`úOgI–äZ¥Žœ%®¯„+Ò6¸´ Û)ž*,/TôhoC96õ�–hÃ$t³*EÓÀMù*ô¼ÖA¾%·¸Ê¶ßj¸ëçÃ0WÝtUGßpÍ4`R2‰†7žQΉ`™™«03WÎ"ã0P"LI˜!‘†AH;Ö,½2Âͽ Í4ÙEôo#Î|û>|ñ‹ãÜ*1ŠØx`½`LtŸE|ã1°nR•÷—Ê6ìZÂÚ­ JÆÌ¢ŠêˆŠëA]Ϻ¨•èÓž(’wkVñÔz“vÕæÛ@ îQúú/éIåmq¹®Ñª›÷o.*À™GFÜG/4»ß Ú¤…«b:vûŠXBS»«3êî ë—7¾ìß8’†ë×ù¤¯ç`ö&é¼\†‰߇¼"Nmöj¾ô“@H­dFËv)6BwÕWæÐYFî“QW j9¿õ .×ÚµDZ,É Íé@mX¹,ê½�€ $ˆ·ðY[÷´tð[3˜â_‡ãÕŽ?yÿÑùÇoPÖá{Ø- ¸>ó”–<¾÷æD|£SJX–öa#n>Ç9 ÝßÁ§š‚WToN$:G¨’±ùø 34‘Ç!)•Q …Cr©=‚@3ÞŽBƒ q¨Gè ÔG»oW ;Ø741ÎtÕáÍ~jµù#†ˆ©âÓ4MÓôøøxww—sžç92$š0?ˆËçwn0Ü—ææ˜7SÛüÿnìØbûËf¦$iapÆi}zyA™Qc��¾IDAT ÏLGD\'z¸ÆCŸ…ˆ)*V0DD%Xla‘F5ŸŽœ}îh)*ÕA¡û˵3%€VéC*ï÷û§§§§¯÷÷÷9gd‚iÚ!âR ".óRçå°ÛŸN§ãñ¸?Ü¿º»ÿ·Ÿþô¼ÿ‚r/ÓÈõrÑŠãȵîv;”s­u´`D™çåRJðêÕ+f)‰HY–¬t5e|~~�Å´?ÿùÏϧãÝÝÝápøôé“bK 3ãˆQZïˆî½¿n׸¡EþS¤×K¹ÌË8Žh ðˆ(ãýýýþðÇyžw»Nœ¡™&Õþ7‚ˆô�%Ã="jìy=fÕÆ|Önؘ§ ›‘%œL[¦ºÎþUgèV0-ñÆè%¢ÒüVð<ÏD´Ûí 0‹%”B‰êq©µîv¶LêɧÕΑHÀX "0W×>Ü‚ \±–‘e•ºÝ5ˆml5‹ >X!CT󃃺g€&s€XŠã¹F$��ÝR³µÿ&ïIð @¤>©5ñ 4ÓNx$¿@¯®‰»ˆ$JôUŒ«KlFÑ|ˆ·¡5? ›h+!ˆq‡žÈ©œCÁõŠæøº&¡º#$xk^Kú¸ åfˆ$MD©Ö:ŽƒÚfÑ6@444g_E5éžqï¬h…˜ˈESaWÖú*l¬íÓËž²þ"ÍÒ-m™¢¡yF^èã¢Þäw‘eßx<›Ió]Bò“néRìp=©okG5Éõ³j d# Jf…ÌM³.RNjWâDHÃ0 ×Z¹ÖÊ˲�sÇ1eG\ʵâh¹ZK-©ŠÛ�Š‚µ¡¼�‡õ³»®Oe}úýþ+%J/®XXüÃmí³CÞÈÂJÄÒPã!^s{ܔ֕jŒ«ÎÛD4Sà{½žµ8}@Ë{+ó\›íͱ¹L¶Ç0Yåé[ÄÍÿܸ`9€Wj#n4aßìÎ×ú|¡é„®óã³ÀënãOq¶by¸DT0*~µÒ²Á¬Ø«~^ËBáØouò¯ª°*ÛEqèg~gœJT¬od=ŒlUá¤ék ›E@Ä F÷´BÄ  ’HÓU2‘v(º$ ~§–Y¹mK£p$(í0P*3 &@Rl.�iåJØó½ �àÚí%j2¦¸k­ ÂBbP¡_z uË]úoþ ë¥Ýß~ÑϾ¸VDTSS“=”^YAìÖ†(lÞÅúÍð6“ÕNúñx«EØ 4:fFAs®K‰ÄBô!¥¤†y1/°*½U<DÑÊú®Z«Z šJÌÒŸn%mš›@_¦5x;`7x¢]¡¹h|Äêè!Щhûð±ùëâ\x`‘ÝlÚó<Ï¿ùÍoþíÿãËÓã»wï.—K\>=Å%@©ÀVrœÚ!ÖR5c¬ÂìIX×ÂÇ ½âêG‹R„[„ÀÃìÔw³»ÏŽ]ÙxÙlDÕ€BŒƒ‡b®6Ôæùd‹ç·ÁÛš `ËP(R+’–z6ƒŽ´Ø:RL‰ ²f³\÷ RQ%5ÊE-²iDÄR—Ãn? ãóé|:ö÷w)Q&àóñi¹”œ3/å|>Ëò§¿ ãîîîNýê‡!•2ùz¬îïçóqØÝ‰H)e â…uCÔZI ˆ�H¦T—Ed'& iQO`x~z>ûiw?LÃüiy~~>zVŸ§TdAÆa¦al‘ÚM°ßS]ŠÙv¹&•Â{s¡±‰;"²Ô2È€a‰ ñp8ˆÈñxT·|Br­LÕ€’SYã,4:cµ#RâZK)뱡üb¹W¶¨µ•—t;€ ²R¹ÑU_§“.Š ¨ež�XŠ™*"ÕZ–ea èÓŒÍ0`z*…ˆXÜ£b¯íqB±”E]éj>à aWÏ@€Ùk’u €ˆ´Â% €+ÿnÌ[3м@ßB²:¯ë‡h5Ÿ$„�˜9Q;ˆÏv-¶¯i˳A° úËaˆX¹lˆ³J¢¦RÚ¯Ûœiíþ+:¦,»ÖYŒSÎæ‚ÑÜŽ˜ôýÊ‘›ïØ ±¾nif¤êºAc”–ð žþ¡r½-ª¿ F ˆpm+ȬÉz´¬—0çœJY¦iDÀvÑž‚€üz-²€­õíJ/Ãõõ–'½+gÒv†Nl—9û÷Y_+Õ]VB 38¥B o|ÑKôŠDÀr0±½Ø¢b²®WÚU@z1âJ ÚÌ÷¯L`s]ØôFll¶€ôà3 aBa®Ìšÿi7¦*\+—²,K)Ka8à©TRJºLÌrcMÃÈ7üEƒîÐÙŽþj¾J¢_ɶÊSo]̰ïÚ„ms›ö3¨«”‡&+é¾=»Ï{æuÖðx˜ÄU»Þ·Á¨6x0õ4ܦÊzÁÔí `ý¬tÛ‡‡\µæâ¥yÙ­ {Û\oÔ¢‰ °˜R*~¦à­sKкˆ v$¬y÷V7›1Q@ô©u;‹Øïl÷êêÃÊrÞVÖQè›øüW¾ƒ.¡Õ3ƒ—§¾JqÍêä\³æ«~��àÿ’^]WoŒH����IEND®B`‚���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0014304�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/.gitignore����������������������������������������������������������������������0000664�0000000�0000000�00000000050�15003540030�0016267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.pyc build dist *.egg-info __pycache__ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/LICENSE�������������������������������������������������������������������������0000664�0000000�0000000�00000002132�15003540030�0015307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Source: https://github.com/gustavo-iniguez-goya/opensnitch Upstream-Name: python3-opensnitch-ui Files: * Copyright: 2017-2018 evilsocket 2019-2020 Gustavo Iñiguez Goia Comment: Debian packaging is licensed under the same terms as upstream License: GPL-3.0 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. . 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 for more details. . You should have received a copy of the GNU General Public License along with this program. If not, If not, see http://www.gnu.org/licenses/. . On Debian systems, the full text of the GNU General Public License version 3 can be found in the file '/usr/share/common-licenses/GPL-3'. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/MANIFEST.in���������������������������������������������������������������������0000664�0000000�0000000�00000000266�15003540030�0016046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������recursive-include opensnitch/proto * recursive-include opensnitch/res * recursive-include opensnitch/i18n *.qm recursive-include opensnitch/database/migrations *.sql include LICENSE ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/Makefile������������������������������������������������������������������������0000664�0000000�0000000�00000000714�15003540030�0015746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������all: opensnitch/resources_rc.py install: @pip3 install --upgrade . opensnitch/resources_rc.py: translations deps @pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc @find opensnitch/proto/ -name 'ui_pb2_grpc.py' -exec sed -i 's/^import ui_pb2/from . import ui_pb2/' {} \; translations: @cd i18n ; make deps: @pip3 install -r requirements.txt clean: @rm -rf *.pyc @rm -rf opensnitch/resources_rc.py @find i18n/ -name '*.qm' -delete ����������������������������������������������������opensnitch-1.6.9/ui/bin/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015054�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/bin/opensnitch-ui���������������������������������������������������������������0000775�0000000�0000000�00000024215�15003540030�0017573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 # Copyright (C) 2018 Simone Margaritelli # 2018 MiWCryptAnalytics # 2023 munix9 # 2023 Wojtek Widomski # 2019-2023 Gustavo Iñiguez Goia # # This file is part of OpenSnitch. # # OpenSnitch 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. # # OpenSnitch 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 for more details. # # You should have received a copy of the GNU General Public License # along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. from PyQt5 import QtWidgets, QtCore from PyQt5.QtNetwork import QLocalServer, QLocalSocket import sys import os import signal import argparse import logging from concurrent import futures import grpc dist_path = '/usr/lib/python3/dist-packages/' if dist_path not in sys.path: sys.path.append(dist_path) from opensnitch.service import UIService from opensnitch.config import Config from opensnitch.utils import Themes, Utils, Versions, Message from opensnitch.utils.xdg import xdg_opensnitch_dir, xdg_current_session from opensnitch import auth import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() app_id = os.path.join(xdg_opensnitch_dir, "io.github.evilsocket.opensnitch") def on_exit(): server.stop(0) app.quit() try: os.remove(app_id) except: pass sys.exit(0) def restrict_socket_perms(socket): """Restrict socket reading to the current user""" try: if socket.startswith("unix://") and os.path.exists(socket[7:]): os.chmod(socket[7:], 0o640) except Exception as e: print("Unable to change unix socket permissions:", socket, e) def configure_screen_scale_factor(cfg): """configure qt screen scale: https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt """ auto_screen_factor = cfg.getBool(Config.QT_AUTO_SCREEN_SCALE_FACTOR, default_value=True) screen_factor = cfg.getSettings(Config.QT_SCREEN_SCALE_FACTOR) if screen_factor is None or screen_factor == "": screen_factor = "1" print("QT_AUTO_SCREEN_SCALE_FACTOR:", auto_screen_factor) os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = str(int(auto_screen_factor)) if auto_screen_factor is False: print("QT_SCREEN_SCALE_FACTORS:", screen_factor) os.environ["QT_SCREEN_SCALE_FACTORS"] = screen_factor def configure_qt_platform_plugin(cfg): qt_plugin = cfg.getSettings(Config.QT_PLATFORM_PLUGIN) if qt_plugin is None or qt_plugin == "": return print("QT_QPA_PLATFORM:", qt_plugin) os.environ["QT_QPA_PLATFORM"] = qt_plugin def check_environ(): if xdg_current_session == "": print(""" Warning: XDG_SESSION_TYPE is not set. If there're no icons on the GUI, please, read the following comment: https://github.com/evilsocket/opensnitch/discussions/999#discussioncomment-6579273 """) def supported_qt_version(major, medium, minor): q = QtCore.QT_VERSION_STR.split(".") return int(q[0]) >= major and int(q[1]) >= medium and int(q[2]) >= minor if __name__ == '__main__': gui_version, grpcversion, protoversion = Versions.get() print("\t ~ OpenSnitch GUI -", gui_version, "~") print("\tprotobuf:", protoversion, "-", "grpc:", grpcversion) print("-" * 50, "\n") parser = argparse.ArgumentParser(description='OpenSnitch UI service.', formatter_class=argparse.RawTextHelpFormatter) parser.add_argument("--socket", dest="socket", help=''' Path of the unix socket for the gRPC service (https://github.com/grpc/grpc/blob/master/doc/naming.md). Default: unix:///tmp/osui.sock Examples: - Listening on Unix socket: opensnitch-ui --socket unix:///tmp/osui.sock * Use unix:///run/1000/YOUR_USER/opensnitch/osui.sock for better privacy. - Listening on port 50051, all interfaces: opensnitch-ui --socket "[::]:50051" ''', metavar="FILE") parser.add_argument("--socket-auth", dest="socket_auth", help="Auth type: simple, tls-simple, tls-mutual") parser.add_argument("--tls-ca-cert", dest="tls_ca_cert", help="path to the CA cert") parser.add_argument("--tls-cert", dest="tls_cert", help="path to the server cert") parser.add_argument("--tls-key", dest="tls_key", help="path to the server key") parser.add_argument("--max-clients", dest="serverWorkers", default=10, help="Max number of allowed clients (incoming connections).") parser.add_argument("--debug", dest="debug", action="store_true", help="Enable debug logs") parser.add_argument("--debug-grpc", dest="debug_grpc", action="store_true", help="Enable gRPC debug logs") parser.add_argument("--background", dest="background", action="store_true", help="Start UI in background even, when tray is not available") args = parser.parse_args() if args.debug: import faulthandler faulthandler.enable() logging.getLogger().disabled = not args.debug cfg = Config.get() configure_screen_scale_factor(cfg) configure_qt_platform_plugin(cfg) if args.debug and args.debug_grpc: os.environ["GRPC_TRACE"] = "all" os.environ["GRPC_VERBOSITY"] = "debug" if supported_qt_version(5,6,0): try: # NOTE: maybe we also need Qt::AA_UseHighDpiPixmaps QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) except Exception: pass service = None try: Utils.create_socket_dirs() app = QtWidgets.QApplication(sys.argv) localsocket = QLocalSocket() localsocket.connectToServer(app_id) if localsocket.waitForConnected(): raise Exception("GUI already running, opening its window and exiting.") else: localserver = QLocalServer() localserver.setSocketOptions(QLocalServer.UserAccessOption) localserver.removeServer(app_id) localserver.listen(app_id) if hasattr(QtCore.Qt, 'AA_UseHighDpiPixmaps'): app.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) thm = Themes.instance() thm.load_theme(app) if args.socket == None: # default args.socket = "unix:///tmp/osui.sock" addr = cfg.getSettings(Config.DEFAULT_SERVER_ADDR) if addr != None and addr != "": if addr.startswith("unix://"): if not os.path.exists(os.path.dirname(addr[7:])): print("WARNING: unix socket path does not exist, using unix:///tmp/osui.sock, ", addr) else: args.socket = addr else: args.socket = addr maxmsglen = cfg.getMaxMsgLength() service = UIService(app, on_exit, start_in_bg=args.background) check_environ() localserver.newConnection.connect(service.OpenWindow) # @doc: https://grpc.github.io/grpc/python/grpc.html#server-object server = grpc.server(futures.ThreadPoolExecutor(), options=( # https://github.com/grpc/grpc/blob/master/doc/keepalive.md # https://grpc.github.io/grpc/core/group__grpc__arg__keys.html # send keepalive ping every 5 second, default is 2 hours) ('grpc.keepalive_time_ms', 5000), # after 5s of inactivity, wait 20s and close the connection if # there's no response. ('grpc.keepalive_timeout_ms', 20000), ('grpc.keepalive_permit_without_calls', True), ('grpc.max_send_message_length', maxmsglen), ('grpc.max_receive_message_length', maxmsglen), )) ui_pb2_grpc.add_UIServicer_to_server(service, server) auth_type = auth.Simple if args.socket_auth != None: auth_type = args.socket_auth elif cfg.getSettings(Config.AUTH_TYPE) != None: auth_type = cfg.getSettings(Config.AUTH_TYPE) # grpc python doesn't seem to accept unix:@address to listen on an # abstract unix socket, so use unix-abstract: and transform it to what # the Go client understands. if args.socket.startswith("unix:@"): parts = args.socket.split("@") args.socket = "unix-abstract:{0}".format(parts[1]) print("Using server address:", args.socket, "auth type:", auth_type) if auth_type == auth.Simple or auth_type == "": server.add_insecure_port(args.socket) else: auth_ca_cert = args.tls_ca_cert auth_cert = args.tls_cert auth_certkey = args.tls_key if auth_cert == None: auth_cert = cfg.getSettings(Config.AUTH_CERT) if auth_certkey == None: auth_certkey = cfg.getSettings(Config.AUTH_CERTKEY) if auth_ca_cert == None: auth_ca_cert = cfg.getSettings(Config.AUTH_CA_CERT) tls_creds = auth.get_tls_credentials(auth_ca_cert, auth_cert, auth_certkey) if tls_creds == None: raise Exception("Invalid TLS credentials. Review the server key and cert files.") server.add_secure_port(args.socket, tls_creds) # https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt signal.signal(signal.SIGINT, signal.SIG_DFL) # print "OpenSnitch UI service running on %s ..." % socket server.start() restrict_socket_perms(args.socket) app.exec_() except KeyboardInterrupt: on_exit() except Exception as e: print(e) finally: if service: # finish gracefully, closing notifications channel. service.close() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015063�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/Makefile�������������������������������������������������������������������0000664�0000000�0000000�00000002633�15003540030�0016527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������SOURCES += ../opensnitch/service.py \ ../opensnitch/dialogs/prompt.py \ ../opensnitch/dialogs/preferences.py \ ../opensnitch/dialogs/ruleseditor.py \ ../opensnitch/dialogs/processdetails.py \ ../opensnitch/dialogs/stats.py FORMS += ../opensnitch/res/prompt.ui \ ../opensnitch/res/ruleseditor.ui \ ../opensnitch/res/preferences.ui \ ../opensnitch/res/process_details.ui \ ../opensnitch/res/stats.ui #TSFILES contains all *.ts files in locales/ and its subfolders TSFILES := $(shell find locales/ -type f -name '*.ts') #QMFILES contains all *.qm files in locales/ and its subfolders QMFILES := $(shell find locales/ -type f -name '*.qm') #if QMFILES is empty, we set it to phony target to run unconditionally ifeq ($(QMFILES),) QMFILES := "qmfiles" endif all: $(TSFILES) $(QMFILES) #if any file from SOURCES or FORMS is older than any file from $(TSFILES) #or if opensnitch_i18n.pro was manually modified $(TSFILES): $(SOURCES) $(FORMS) opensnitch_i18n.pro @pylupdate5 opensnitch_i18n.pro #if any of the *.ts files are older that any of the *.qm files #QMFILES may also be a phony target (when no *.qm exist yet) which will always run $(QMFILES):$(TSFILES) @./generate_i18n.sh for lang in $$(ls locales/); do \ if [ ! -d ../opensnitch/i18n/$$lang ]; then mkdir -p ../opensnitch/i18n/$$lang ; fi ; \ cp locales/$$lang/opensnitch-$$lang.qm ../opensnitch/i18n/$$lang/ ; \ done �����������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/README.md������������������������������������������������������������������0000664�0000000�0000000�00000001750�15003540030�0016345�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ### Adding a new translation: 0. Install needed packages: `apt install qtchooser pyqt5-dev-tools` 1. mkdir `locales/<YOUR LOCALE>/` (echo $LANG) 2. add the path to opensnitch_i18n.pro: ``` TRANSLATIONS += locales/es_ES/opensnitch-es_ES.ts \ locales/<YOUR LOCALE>/opensnitch-<YOUR LOCALE>.ts ``` 3. make ### Updating translations: 1. update translations definitions: - pylupdate5 opensnitch_i18n.pro 2. translate a language: - linguist locales/es_ES/opensnitch-es_ES.ts 3. create .qm file: - lrelease locales/es_ES/opensnitch-es_ES.ts -qm locales/es_ES/opensnitch-es_ES.qm or: 1. make 2. linguist locales/es_ES/opensnitch-es_ES.ts 3. make ### Installing translations (manually) In order to test a new translation: `mkdir -p /usr/lib/python3/dist-packages/opensnitch/i18n/<YOUR LOCALE>/` `cp locales/<YOUR LOCALE>/opensnitch-<YOUR LOCALE>.qm /usr/lib/python3/dist-packages/opensnitch/i18n/<YOUR LOCALE>/` Note: the destination path may vary depending on your system. ������������������������opensnitch-1.6.9/ui/i18n/generate_i18n.sh�����������������������������������������������������������0000775�0000000�0000000�00000000512�15003540030�0020051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh app_name="opensnitch" langs_dir="./locales" lrelease="lrelease" if ! command -v lrelease >/dev/null; then # on fedora lrelease="lrelease-qt5" fi #pylupdate5 opensnitch_i18n.pro for lang in $(ls $langs_dir) do lang_path="$langs_dir/$lang/$app_name-$lang" $lrelease $lang_path.ts -qm $lang_path.qm done ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016505�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/de_DE/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017445�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/de_DE/opensnitch-de_DE.ts������������������������������������������0000664�0000000�0000000�00000455022�15003540030�0023135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="de_DE"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>OpenSnitch Firewall</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>User ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Ausgeführt von</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Quell-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>Prozess ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Zielport</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="150"/> <source>Chromium Web Browser</source> <translation type="obsolete">Chromium-Webbrowser</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="226"/> <source>(/path/to/bin/chromium)</source> <translation type="obsolete">(Pfad/zur/bin/chromium)</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="271"/> <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source> <translation type="obsolete">Der Chromium-Webbrowser möchte eine Verbindung zu www.evilsocket.net über TCP-Port 443 herstellen. Und möglicherweise zu www.goodsocket.net über Port 344</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>von dieser ausführbaren Datei</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>von dieser Kommandozeile</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>dieser Zielport</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>dieser Benutzer</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>diese Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>einmal</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1h</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">für diese Sitzung</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>für immer</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation type="unfinished">Verweigern</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation type="unfinished">Knoten</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished">Aktivieren</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation type="unfinished">Aktion</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation type="unfinished">Löschen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished">Speichern</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Einstellungen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Popup-Fenster</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Dieses Zeitlimit ist der Countdown, der angezeigt wird, wenn ein Popup-Fenster angezeigt wird.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Standardzeitlimit</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>Popup-Standarddauer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Standarddauer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Popup-Standardaktion</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Standardaktion</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Standardfilterung</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>mittig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>oben rechts</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>unten rechts</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>oben links</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>unten links</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Grundposition</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>nach ausführbarer Datei</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>nach Befehl</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>nach Zielport</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>nach Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>nach UID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>einmal</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1h</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">für diese Sitzung</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>für immer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>verweigern</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>erlauben</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Popups deaktivieren, nur eine Warnung anzeigen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Knoten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Prozessüberwachungsmethode</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Die Standarddauer gilt, wenn keine Benutzeroberfläche verbunden ist.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Knotenadresse.</p><p>Standardmäßig: unix: ///tmp/osui.sock (unix: // ist erforderlich, wenn ein Unix-Socket vorhanden ist)</p><p>Es kann sich auch um eine IP mit diesem Format handeln: 127.0.0.1:50051, 192.168.1.122:12345 usw.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Standardprotokollstufe</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Die Standardaktion wird angewendet, wenn keine Benutzeroberfläche verbunden ist.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Protokolldatei, in welche die Protokolle geschrieben werden sollen.<br/></p><p>/dev/stdout schreibt die Protokolle in die Standardausgabe des Dienstes..</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Logdatei</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Wenn Sie diese Option aktivieren, werden Sie von OpenSnitch aufgefordert, Verbindungen zu akzeptieren oder zu verweigern, denen aus verschiedenen Gründen keine PID zugeordnet ist. Das Popup-Fenster enthält nur Informationen zur Verbindung. Hinweis: Diese Verbindungen müssen nicht darauf hinweisen, dass etwas Verdächtiges passiert. Einfach ist, dass wir die PID nicht entdeckt haben (zum Beispiel Verbindungen, die nicht vom Computer stammen, oder fehlerhafte Pakete).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Unbekannte Verbindungen abfangen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>HostName</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>immer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Konfiguration auf alle Knoten anwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Datenbank</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="630"/> <source>Database name</source> <translation type="obsolete">Datenbankname</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>Im Speicher</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Datei</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>/path/to/the/file.db</source> <translation type="obsolete">/Pfad/zu/der/Datei.db</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Schließen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Anwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Speichern</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>In der erweiterten Ansicht können Sie ganz einfach mehrere Felder auswählen, um Verbindungen zu filtern</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Standardmäßig erweiterte Ansicht anzeigen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Aktion</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Wenn diese Option aktiviert ist, werden die Pop-ups mit aktiver erweiterter Ansicht angezeigt.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Dauer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>Wenn ein neues Popup-Fenster in seiner einfachsten Form angezeigt wird, können Sie standardmäßig Verbindungen oder Anwendungen nach einer Eigenschaft der Verbindung (ausführbare Datei, Port, IP usw.) filtern.</p><p>Mit diesen Optionen können Sie mehrere Felder auswählen, nach denen Verbindungen gefiltert werden sollen.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Verbindungen auch filtern nach:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Wenn aktiviert, wird dieses Feld ausgewählt, wenn ein Popup angezeigt wird</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>User ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Ziel Port</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Dieses Timeout ist der Countdown, den Sie sehen, wenn ein Popup-Dialogfeld angezeigt wird.</p><p>Wenn das Popup nicht beantwortet wird, werden die Standardoptionen angewendet.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Datenbanktyp</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Auswählen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>Popup-Standardaktion.</p><p>Wenn eine neue ausgehende Verbindung hergestellt werden soll, wird diese Aktion standardmäßig ausgewählt. Wenn das Timeout auftritt, wird diese Option angewendet.</p><p><br/></p><p>Während ein Pop-up den Benutzer auffordert, eine Verbindung zuzulassen oder abzulehnen:</p><p>1. neue ausgehende Verbindungen werden verweigert.</p><p>2. bekannte Verbindungen werden nach den vom Benutzer definierten Regeln zugelassen oder verweigert.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Standardaktion, wenn die GUI getrennt ist</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Debugge ungültige Verbindungen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Pop-ups</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Standardoptionen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Standardposition auf dem Bildschirm</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>jede temporäre Regel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Wenn diese Option ausgewählt ist, werden die Regeln der ausgewählten Dauer nicht zur Liste der temporären Regeln in der GUI hinzugefügt.</p><p><br/></p><p>Temporäre Regeln sind weiterhin gültig und Sie können sie verwenden, wenn Sie dazu aufgefordert werden, eine neue Verbindung zuzulassen/zu verweigern.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="490"/> <source>Don't save rules of duration</source> <translation type="obsolete">Speichern Sie keine Regeln der Dauer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Zeit</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Ziel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Prozess</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Knoten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Wenn diese Option aktiviert ist, fordert Opensnitch Sie aus verschiedenen Gründen auf, Verbindungen zuzulassen oder zu verweigern, die keine zugeordnete PID haben, hauptsächlich aufgrund von Verbindungen mit schlechtem Status.</p><p>Der Popup-Dialog enthält nur Informationen über die Netzwerkverbindung.</p><p>Es gibt jedoch einige Szenarien, in denen dies gültige Verbindungen sind, z. B. beim Einrichten eines VPN mit Wireguard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation>Desktop-Benachrichtigungen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation>Systembenachrichtigungen verwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation>Qt-Benachrichtigungen verwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation>Minuten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation>Tage</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>Ablehnen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation>Design</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation type="unfinished">Regeln</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Prozessdetails</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>wird geladen...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: Laden...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>Speicherstatistik: Laden ...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Dateien öffnen</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>I/O Statistiken</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Dateien in den Speicher geladen</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Stapel</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Umgebungsvariablen</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Anwendungs-PIDs</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Starten oder beenden Sie die Überwachung dieses Prozesses</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Schließen</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Knoten</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Regel auf alle Knoten anwenden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Von dieser Kommandozeile</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>Von dieser ausführbaren Datei</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Aktion</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/Pfad/zur/ausführbaren/Datei, .*/bin/executable[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>Zu dieser IP / Netzwerk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>einmal</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation type="obsolete">30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation type="obsolete">5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation type="obsolete">15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation type="obsolete">30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation type="obsolete">1h</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>immer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>Zu diesem Port</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>Von dieser Benutzer-ID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>Kommas oder Leerzeichen dürfen nicht mehrere Domänen angeben. Verwenden Sie stattdessen reguläre Ausdrücke: .*(opensnitch|duckduckgo).com .*\.google.com oder eine einzelne Domain: www.gnu.org - es wird nur mit www.gnu.org, noch ftp.gnu.org oder www2.gnu.org übereinstimmen, ... gnu.org - es wird nur mit gnu.org, www.gnu.org oder ftp.gnu.org übereinstimmen, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Es sind nur TCP-, UDP- oder UDPLITE-Optionen zulässig.</p><p>Sie können reguläre Ausdrücke verwenden zu diesen Optionen, zum Beispiel TCP oder UDP: ^ (TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Sie können eine IP angeben: - 192.168.1.1 oder ein regulärer Ausdruck: - 192\.168\.1\.[0-9]+ mehrere IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Sie können auch ein Subnetz angeben: - 192.168.1.0/24 Hinweis: Kommas und Leerzeichen dürfen keine IPs oder Netzwerke angeben.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> <source>LAN</source> <translation type="obsolete">LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation type="obsolete">127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation type="obsolete">192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation type="obsolete">192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation type="obsolete">192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation type="obsolete">192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation type="obsolete">169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation type="obsolete">172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation type="obsolete">10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation type="obsolete">::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation type="obsolete">fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation type="obsolete">ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation type="obsolete">fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation type="obsolete">fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Dauer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>Zu diesem Host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Name</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Aktivieren</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>Regeln werden in alphabetischer Reihenfolge überprüft, daher können Sie diese so benennen, um sie zu priorisieren. 000-allow-localhost 0001-Deny-Broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> <source>leave blank to autocreate</source> <translation type="obsolete">Lassen Sie das Feld leer, um den Namen automatisch zuzuweisen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Wenn Sie diese Option aktivieren, hat diese Regel bei der Bewertung Vorrang vor den übrigen Regeln. Danach werden keine Regeln mehr überprüft. Sie müssen die Regel so benennen, dass sie zuerst überprüft wird, da sie in alphabetischer Reihenfolge überprüft wird. Zum Beispiel: [x] Priorität - 000-Prioritätsregel [] Priorität - 001-Regel mit weniger Priorität</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Prioritätsregel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Standardmäßig wird bei den Feldern einer Regel NICHT zwischen Groß- und Kleinschreibung unterschieden, d. H.; Wenn ein Prozess versucht, auf gOOgle.CoM zuzugreifen, und Sie eine Regel zum Verweigern haben. * Google.com, wird die Verbindung blockiert.<br/></p><p>Wenn Sie diese Option aktivieren und gOOgle.CoM GENAU blockieren möchten, müssen Sie dies im Regelfeld angeben, also die genaue Domain, die Sie filtern möchten (in diesem Fall: gOOgle.CoM).</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>Groß- und Kleinschreibung beachten</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Sie können mehrere Ports mit regulären Ausdrücken angeben:</p><p><br/></p><p>- 53, 80 oder 443: </p><p>^ (53|80|443)$</p><p><br/></p><p>- 53, 443 oder 5551, 5552, 5553 usw.:</p><p>^ (53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>Zu dieser Domainliste</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Wählen Sie ein Verzeichnis mit Domänenlisten aus, die blockiert oder zugelassen werden sollen.</p><p>Legen Sie in diesem Verzeichnis Dateien mit einer beliebigen Erweiterung ab, die Listen von Domänen enthalten.</p><p><br/>Das Format jedes Eintrags einer Liste ist wie folgt (Hosts-Format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>Anwendungen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation>Netzwerk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch-Netzwerkstatistik</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">Als CSV exportieren.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">Strg+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Erstellen Sie eine neue Regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Abfangen starten oder stoppen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Ereignisse</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filter</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Beispiel: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Knoten</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf die Addressenspalte, um Details eines Knotens anzuzeigen)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Regeln</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>aktivieren</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="671"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(Doppelklicken Sie auf die Namenspalte, um Details einer Regel anzuzeigen.)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">Suchregelname</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Anwendungsregeln</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>Dauerhaft</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Temporär</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf eine Element, um Details anzuzeigen.)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Anwendungen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Adressen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Ports</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Benutzer</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Verbindungen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Abgelehnt</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Betriebszeit</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Löschen Sie alle abgefangenen Ereignisse</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Regel bearbeiten</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Regel löschen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Löschen Sie alle abgefangenen Hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Löschen Sie alle abgefangenen Anwendungen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Löschen Sie alle abgefangenen Adressen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Löschen Sie alle abgefangenen Ports</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Löschen Sie alle abgefangenen Benutzer</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf eine Zeile, um Details zu einer Regel anzuzeigen)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Verbindungen löschen, die dieser Regel entsprechen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Alle Anwendungen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Statistiken</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Hilfe</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Schließen</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Aktivieren</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Deaktivieren</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished">Konfiguration angewendet.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished">Quell-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation type="unfinished">Warnung</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>für immer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Ausgehende Verbindung</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Prozess ausgeführt von:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>von dieser Kommandozeile</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>von dieser ausführbaren Datei</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Unbekannter Prozess</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>zum Port {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> stellt eine Verbindung zu <b>%s</b> an Port%s %d her</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>Remote-Prozess <b>%s</b>, der auf <b>%s</b> ausgeführt wird, stellt eine Verbindung zu <b>%s</b> auf %s Port %d her</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>zu {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>UID {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>zu {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>zu *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">zu *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation><b>Remote-Prozess </b> %s wird ausgeführt auf <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>stellt eine Verbindung zu <b>%s</b> auf %s Port %d her</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>versucht <b>%s</b> über%s,%s Port%d aufzulösen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Fehler beim Speichern der Konfiguration: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Konfiguration in %s anwenden ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>Die Serveradresse darf nicht leer sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Fehler beim Laden der Konfiguration %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>Konfiguration angewendet.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Fehler beim Anwenden der Konfiguration: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Fehler beim Speichern der Konfiguration: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Konfiguration in {0} anwenden ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Fehler beim Laden der Konfiguration {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Fehler beim Anwenden der Konfiguration: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Warnung</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Sie müssen eine Datei für die Datenbank auswählen<br>oder wählen Sie den Typ "Im Speicher".</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>DB-Typ geändert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>Starten Sie die GUI neu, damit die Effekte wirksam werden</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Fahren Sie mit der Maus über die Texte, um die Hilfe anzuzeigen<br><br>Vergessen Sie nicht, das Wiki zu besuchen: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Fehler beim Laden der Prozessinformationen:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Fehler beim Beenden des Überwachungsprozesses:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>Wird geladen...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>Es sind keine Knoten verbunden.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>Regel angewendet.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Fehler beim Anwenden der Regel:%s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>Das Protokoll darf nicht leer sein oder die Option deaktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Protokoll-Regexp-Fehler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>Prozesspfad darf nicht leer sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Prozesspfad-Regexp-Fehler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>Befehlszeile darf nicht leer sein oder die Option deaktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Befehlszeilen-Regexp-Fehler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>Der Zielport darf nicht leer sein oder die Option deaktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Fehler im regulären Ausdruck des Zielports</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>Der Zielhost kann nicht leer sein oder die Option deaktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Fehler beim regulären Ausdruck des Zielhosts</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>Ziel-IP / Netzwerk darf nicht leer sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Fehler beim regulären Ausdruck der Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>Die Benutzer-ID darf nicht leer sein oder die Option deaktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Regexp-Fehler der Benutzer-ID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Fehler beim Anwenden der Regel: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Fehler beim Laden der Regel</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>Listenfeld darf nicht leer sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>Listenfeld muss ein Verzeichnis sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Regel nicht unterstützt</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Gestoppt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Deaktiviert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Eingeschaltet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Sie sind im Begriff, diese Regel zu löschen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Bist du sicher?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch-Netzwerkstatistiken {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch-Netzwerkstatistiken für {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Name</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Adresse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">Status</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">Version</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Regeln</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Zeit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Aktion</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Dauer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Knoten</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation type="unfinished">Treffer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protokoll</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Als CSV speichern</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Aktiviert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Löschen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="575"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Fehler:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Warnung:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Immer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Deaktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Aktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Duplizieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Bearbeiten</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Regel von diesem Namen und Knoten nicht gefunden</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Sie sind dabei, diese Regel zu löschen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Name</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Name</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Adresse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Status</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Version</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regeln</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Zeit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aktion</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dauer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Knoten</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aktiviert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Treffer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protokoll</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Name</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regeln</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Zeit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Aktion</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Dauer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Knoten</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Aktiviert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Treffer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Prozess</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ziel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>BenutzerID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>LetzteVerbindung</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ZielIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ZielHost</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ZielPort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> <source>LastConnection</source> <translation type="obsolete">LetzteVerbindung</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> <source>Uptime</source> <translation type="obsolete">Betriebszeit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> <source>Connections</source> <translation type="obsolete">Verbindungen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> <source>Dropped</source> <translation type="obsolete">Abgelehnt</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Betriebszeit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Verbindungen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Abgelehnt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/es_ES/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017503�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/es_ES/opensnitch-es_ES.ts������������������������������������������0000664�0000000�0000000�00000456434�15003540030�0023241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="es_ES"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>UID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation>Ejecutado desde</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>Etiqueta de texto</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>IP origen</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>PID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>IP destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Puerto destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>de este ejecutable</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>de este comando</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>este puerto destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>este usuario</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>esta IP destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>una vez</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>para siempre</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation type="unfinished">Denegar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>a partir de este PID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>acción</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30 segundos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1 hora</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation type="unfinished">Cerrar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation type="unfinished">Nodo</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished">Habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished">Descripción</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation type="unfinished">Eliminar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished">Guardar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Preferencias</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>UI</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Timeout por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>Duración por defecto (de la acción/regla)</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Duración por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Acción por defecto de la ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Acción por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Filtrado por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>centro</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>Arriba a la derecha</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>Abajo a la derecha</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>Arriba a la izquierda</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>Abajo a la izquierda</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posición por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>por ejecutable</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>por comando</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>por puerto destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>por IP destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>por UID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>una vez</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>para siempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Deshabilitar ventanas emergentes, sólo mostrar alerta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Nodos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Método parar monitorizar procesos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation>La Duración por defecto se aplicará cuando no haya ninguna UI conectada</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Dirección del nodo.</p><p>Por defecto: unix:///tmp/osui.sock (unix:// es obligatorio si es un socket Unix)</p><p>También puede ser una IP con este formato: 127.0.0.1:50051, 192.168.1.122:12345, etc..</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Dirección</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Nivel de log por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Versión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation>La Acción por defecto se aplicará cuando no haya ninguna UI conectada</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Fichero en el que escribir los logs.<br/></p><p>/dev/stdout escribirá los logs por la salida estándar del servicio.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Fichero de log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. La ventana emergente sólo contendrá información relativa a la conexión. Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexiones desconocidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>Nombre del host</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>siempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Aplicar configuración a todos los nodos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Datos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>En memoria</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Fichero</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Cerrar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Aplicar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Guardar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Tipo de base de datos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Seleccionar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posición en pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="102"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Mostrar vista avanzada por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Acción</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation>Si se selecciona, las ventanas emergentes se mostrarán con la vista avanzada activada</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Duración</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation>Por defecto cuando una ventana emergente aparece, en su forma más simple, puedes filtrar conexiones por un parámetro de la conexión (ejecutable, puerto, IP, etc). Con estas opciones, puedes seleccionar varios campos por los que filtrar por defecto conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Filtrar conexiones también por:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="362"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>UID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Puerto destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>IP destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation>Este timeout es la cuenta atrás que ves cuando se muestra una ventana emergente Si no respondes a la ventana emergente, se aplicarán las opciones por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>La vista avanzada te permite seleccionar fácilmente múltiples campos para filtrar conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Si se selecciona, este campo estará marcado cuando una ventana emergente aparezca</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>Acción por defecto de la ventana emergente.</p><p>Cuando una nueva conexión saliente está a punto de establecerse, esta acción será la predeterminada, por lo que si llega el timeout, está será la que se aplique.</p><p><br/></p><p>Mientras una ventana emergente está activa esperando ser aprobada o denegada:</p><p>1. Las nuevas conexiones salientes son denegadas (según la configuración del demonio)</p><p>2. Las conexiones ya conocidas se permitirán o denegarán en base a las reglas ya creadas por el usuario.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Opción por defecto cuando la GUI no está conectada</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Depurar conexiones inválidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Opciones por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Posición por defecto en la pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>cualquier regla temporal</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Cuando esta opción está seleccionada, las reglas de la duración elegida no se añadirán a la lista de reglas temporales en la GUI.</p><p><br/></p><p>Las reglas temporales seguirán siendo válidas, y puedes usarlas cuando se pregunte para permitir o denegar una nueva conexión.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="490"/> <source>Don't save rules of duration</source> <translation type="obsolete">No guardar reglas de duración</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source>Show events columns</source> <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Hora</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Aplicación</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Regla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Nodo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>por PID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation>Deshabilitar ventanas emergentes, sólo mostrar notificaciones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation>Notificaciones de escritorio</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation>Usar notificaciones del sistema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation>Usar notificaciones de Qt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation>Probar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation><html><head/><body><p>Si lo marcas, OpenSnitch sólo te preguntará para denegar o permitir conexiones que por diversas razones no tengan un PID/aplicación asociado. Generalmente son conexiones en estado erróneo.</p><p>La ventana emergente sólo contendrá información sobre la conexión de red.</p><p>Hay algunos casos en los que estas conexiones pueden ser válidas, como cuando se establecen conexiones VPN.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation>minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation>Minutos entre borrado de eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation>días</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation>Máximo de días a guardar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>rechazar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation>Sistema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation>Línea de comando</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation>Tema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30 segundos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1 hora</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Detalles del proceso</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>cargando...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: cargando...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>estadísticas de memoria: cargando...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Ficheros abiertos</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>Estadísticas Entrada/Salida</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Ficheros cargados en memoria</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Pila</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Variables de entorno</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>PIDs de la aplicación</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Iniciar o Parar el monitorizado de este proceso</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Cerrar</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Regla</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Nodo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Aplicar regla a todos los nodos</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>De este comando</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>De este ejecutable</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Acción</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>A esta IP/Red</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>una vez</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>siempre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>A este puerto</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>De este UID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>No se permiten ni comas ni espacios para especificar múltiples dominios. Puedes usar expresiones regulares en su lugar: .*(opensnitch|duckduckgo).com .*\.google.com o un único dominio: www.gnu.org - sólo filtrará www.gnu.org, NO filtrará ftp.gnu.org ni www2.gnu.org gnu.org - sólo filtrará gnu.org, ni www.gnu.org, ni ftp. gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.dominio.org, .*\.dominio.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation>Sólo se permiten las opciones TCP, UDP o UDPLITE. Puedes usar expresiones regulares sobre estas opciones, por ejemplo TCP o UDP: ^(TCP|UDP)$</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Puedes especificar una IP: - 192.168.1.1 o una expresión regular: - 192\.168\.1\.[0-9]+ múltiples IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ También puedes especificar una subnet: - 192.168.1.0/24 Nota: No se permiten ni comas ni espacios para especificar IPs o redes.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Duración</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>A este host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Nombre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>Las reglas se comprueban en orden alfabético, por lo que debes nombrarlas así para priorizarlas. 000-allow-localhost 0001-deny-broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> <source>leave blank to autocreate</source> <translation type="obsolete">dejar en blanco para autoasignar nombre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Si marcas esta opción, esta regla tendrá prioridad sobre el resto de reglas cuando le toque evaluarla. No se comprobará ninguna regla más después de esta si coincide. Debes nombrar la regla de tal manera que se compruebe de las primeras, ya que se nombran alfabéticamente. Por ejemplo: [x] Prioritaria-000-regla-prioritaria [ ] Prioritaria-001-regla-menos-prioritaria</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Prioritaria</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Por defecto los campos de una regla NO son sensibles a mayúsculas/minúsculas, es decir, si un proceso trata de acceder a gOOgle.CoM y tienes una regla para Denegar .*google.com, la conexión será bloqueada. <br/></p><p>Si marcas esta opción y quieres bloquear EXACTAMENTE gOOgle.CoM, tendrás que especificar en el campo de la regla el dominio exacto que quieres filtrar (en este caso: gOOgle.CoM).</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>Distinguir mayúsculas/minúsculas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete">Puedes especificar múltiples puertos usando expresiones regulares: - 53, 80 o 443: ^(53|80|443)$ - 53, 443 o 5551, 5552, 5553, etc: ^(53|443|555[0-9])$</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>A esta lista de dominios</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>Aplicaciones</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Este campo sólo comprueba la ruta del ejecutable (la cual no es modificable por el usuario).<br/></p><p>Puedes usar expresiones regulares para denegar cualquier ejecución desde /tmp, por ejemplo; ^/tmp/.*$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation><html><head/><body><p>Este campo contendrá y comprobará solamente la linea de comandos tecleada por el usuario.<br/></p><p>Si el usuario sólo escribió el comando (sin la ruta absoluta), sólo aparecerá el comando:</p><p>telnet 1.2.3.4<br/></p><p>Si el usuario escribió la ruta absoluta o relativa al comando, eso es lo que aparecerá:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>De este PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation>Red</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation>A esta lista de dominios/IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation>A esta lista de rangos de red</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation>A esta lista de IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de IPs a bloquear o permitir:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Una IP por linea. Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de rangos de red a bloquear o permitir:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>Un rango de red por linea. Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de dominios a bloquear o permitir.</p><p>Los ficheros de ese directorio pueden tener cualquier extensión.</p><p><br/>El formato de cada linea es como sigue (formato hosts):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation>A esta lista de dominios (expresiones regulares)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan expresiones regulares de dominios para bloquear o permitir:</p><p>.*\.example\.com</p><p>También puedes usar un dominio literal (sin expresión regular): &quot;example.com&quot; ,comprobará whatever.example.com, whatever.example.com.localdomain, etc.</p><p>Un dominio por linea. Las lineas en blanco o que comiencen con # serán ignoradas</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Rechazar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation>Descripción...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation><html><head/><body><p>El valor de este campo es siempre la ruta absoluta al ejecutable: /path/to/binary<br/></p><p>Ejemplos:</p><p>- Sencillo: /path/to/binary</p><p>- Múltiples caminos: ^/usr/lib(64|)/firefox/firefox$</p><p>- Binarios múltiples: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Denegar/permitir ejecuciones de /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>Para ver más ejemplos, visite <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">página en la wiki</a> o pregunte en los <a href="https://github.com/evilsocket/opensnitch/discussions">Foros de debate</a>.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>Es una expresión regular</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation>es una expresión regular</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation>Interfaz de la red</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation>Más</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation>No registrar conexiones que coincidan con esta regla</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation>No registrar conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>Denegar sólo descartará la conexión</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>Si se rechaza, se cancelará la conexión y se eliminará el socket que la inició</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>Permitir autorizará la conexión</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>Eventos de red - OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">Exportar a CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Crear una nueva regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">Nombre del host - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Parar o iniciar la interceptación</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filtrar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Ejemplo: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Nodos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Reglas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="684"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">buscar regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Reglas de aplicación</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>Permanentes</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Temporales</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Dominios</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete">(doble click en un dominio para ver detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Aplicaciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Direcciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Puertos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Usuarios</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Rechazadas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Tiempo de ejecución</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Versión</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Borrar todos los eventos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Editar regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Borrar regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Borrar todos los hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Borrar todos las aplicaciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Borrar todas las direcciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Borrar todos los puertos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Borrar todos los usuarios</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(Doble click en una fila para editar una regla)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Borrar conexiones que coinciden con esta regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Todas las reglas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Rechazar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation>Normas del sistema</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Eventos</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Ayuda</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Cerrar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Deshabilitar</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished">Configuración aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished">IP origen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation>Información</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation>Fallo</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation>Advertencia</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>Las notificaciones de sistema no están disponibles, tienes que instalar python3-notify2.</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>para siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Conexión saliente</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Proceso ejecutado desde:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>este comando</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>este ejecutable</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Proceso no encontrado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>puerto {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>a {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>UID {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>a {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>a *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">a *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation>El proceso <b>Remoto</b> %s ejecutado en <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>está tratando de resolver <b>%s</b> via %s, %s puerto %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation>de este PID</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation>Nueva conexión saliente</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation>Rechazar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Error al guarda la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Aplicando configuración en %s ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>La dirección del servidor no puede estar vacía</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Error al cargar la configuración %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>Configuración aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Error al aplicar la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Error al guardar la configuración: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Aplicando configuración en {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Error al cargar la configuración {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Error al aplicar la configuración: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Aviso</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Debes seleccionar un fichero para la base de datos<br>o elegir el tipo En memoria.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>El tipo de BBDD ha cambiado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>Reinicia la GUI para que los cambios surtan efecto</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Pasa el ratón sobre los textos para mostrar la ayuda<br><br>Y no olvides visitar el wiki: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation>Sistema</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>Temas no disponibles. Instalar qt-material: pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation>Cambio del tema de la interfaz de usuario</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation>Reinicie la interfaz gráfica de usuario para aplicar el nuevo tema</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation>De acuerdo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> <source>Restart the GUI in order changes to take effect</source> <translation type="obsolete">Reinicie la interfaz gráfica de usuario para que los cambios surtan efecto</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Error al carga la información del proceso:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Error al parar de monitorizar el proceso:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>cargando...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>No hay nodos conectados.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>Regla aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Error al aplicar la regla: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>el protocolo no puede estar vacío, o desmarca la opción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Error en la expresión regular del Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>La ruta del ejecutable no puede estar vacía</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Error en la expresión regular del ejecutable</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>El comando no puede estar vacío, o desmarca la opción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Error en la expresión regular del Comando</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>El puerto destino no puede estar vacío, o desmarca la opción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Error en la expresión regular del Puerto Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>El Host destino no puede estar vacío, o desmarca la opción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Error en la expresión regular del Host de destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>La IP/Red de destino no puede estar vacía</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Error en la expresión regular de IP/Red de destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>El ID de Usuario no puede estar vacío, o desmarca la opción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Error en la expresión regular del ID de Usuario</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Error al aplicar la regla: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>El campo Listas de dominios no puede estar vacío</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>El campo Listas debe ser un directorio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Tipo de regla no soportada</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Error cargando la regla</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation>Ya hay una regla con este nombre.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation>El campo de PID no puede estar vacío</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation>Error en la expresión regular del PID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation>Selecciona al menos un campo.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation>La interfaz de red no puede estar vacía</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation>Error de la expresión regular de la interfaz de la red</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Parado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Deshabilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Interceptando</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Estás a punto de borrar esta regla. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> ¿Estás seguro?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>Eventos de red OpenSnitch {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>Eventos de red OpenSnitch de {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Guardar como CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Eliminar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Deshabilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Duplicar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Editar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Regla no encontrada por ese nombre o nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Error:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Aviso:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Estás a punto de borrar esta regla. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> <source>LastConnection</source> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>IPDestino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>HostDestino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>PuertoDestino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="175"/> <source>Addr</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> <source>Connections</source> <translation type="obsolete">Conexiones</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> <source>Dropped</source> <translation type="obsolete">Rechazadas</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Qué</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation>Aplicar a</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation>Rechazar</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>Red</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="378"/> <source>Addr</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Tiempo de ejecución</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Conexiones</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Rechazadas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Qué</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Prioritaria</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation>Nuevo nodo conectado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Descripción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Línea en cmd</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/eu_ES/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017505�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/eu_ES/opensnitch-eu_ES.ts������������������������������������������0000664�0000000�0000000�00000344301�15003540030�0023232�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="eu_ES"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation type="unfinished"></translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation type="unfinished"></translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation type="unfinished"></translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation type="unfinished"></translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished"></translation> </message> </context> </TS> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/fi_FI/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017461�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/fi_FI/opensnitch-fi_FI.ts������������������������������������������0000664�0000000�0000000�00000401113�15003540030�0023155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="fi_FI" sourcelanguage="en"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>Käyttäjä-ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Käynnistetty kohteesta</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Lähde-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>Prosessi-ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>Kohde-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Kohdeportti</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>tästä ohjelmatiedostosta</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>tästä komentorivistä</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>tästä kohdeportista</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>tältä käyttäjältä</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>tästä kohde-IP:stä</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>tästä PID:stä</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>kerran</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1t</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>uudelleenkäynnistykseen asti</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>ikuisesti</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>toiminto</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Salli</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>Palomuuri</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Palomuuri</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>Profiili</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Estä</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>Lähtevä</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>Tuleva</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation>Salli tulevat yhteydet porttiin</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation>Salli palvelu (IN)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation>Poissulje porttiin lähtevät yhteydet sieppaukselta</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation>Salli palvelu (OUT)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation>Uusi sääntö</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="421"/> <source>Close</source> <translation>Sulje</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>Palomuurisääntö</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>Solmu</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation>Ota käyttöön</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation>Kuvaus</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation>Yksinkertainen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation>Lisää uusi ehto</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation>Poista valittu ehto</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="221"/> <source>Direction</source> <translation>Suunta</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="232"/> <source>IN</source> <translation>IN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="241"/> <source>OUT</source> <translation>OUT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="250"/> <source>FORWARD</source> <translation>FORWARD</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="255"/> <source>PREROUTING</source> <translation>PREROUTING</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="260"/> <source>POSTROUTING</source> <translation>POSTROUTING</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="268"/> <source>Action</source> <translation>Toiminto</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="279"/> <source>ACCEPT</source> <translation>ACCEPT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="288"/> <source>DROP</source> <translation>DROP</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="297"/> <source>REJECT</source> <translation>REJECT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="306"/> <source>RETURN</source> <translation>RETURN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="315"/> <source>QUEUE</source> <translation>QUEUE</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="323"/> <source>DNAT</source> <translation>DNAT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="328"/> <source>SNAT</source> <translation>SNAT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="333"/> <source>REDIRECT</source> <translation>REDIRECT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="349"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation>toiminnosta (eli kohteesta) riippuen parametrien syntaksi vaihtelee. Joitakin esimerkkejä: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="432"/> <source>Clear</source> <translation>Tyhjennä</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="443"/> <source>Delete</source> <translation>Poista</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="454"/> <source>Save</source> <translation>Tallenna</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="465"/> <source>Add</source> <translation>Lisää</translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Asetukset</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Ponnahdusikkunat</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Oletusasetukset</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Jos tämä kenttä on valittuna, se valitaan, kun ponnahdusikkuna tulee näkyviin</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>Käyttäjä-ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Kohdeportti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>Kohde-IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>estä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>salli</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>hylkää</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>Ponnahdusikkunan oletustoiminto.</p><p>Kun uutta lähtevää yhteyttä ollaan muodostamassa, tämä toiminto valitaan oletusarvoisesti.</p><p><br/></p><p>Kun ponnahdusikkunassa kysytään käyttäjältä yhteyden sallimista tai kieltämistä:</p><p>1. Uudet lähtevät yhteydet kielletään.</p><p>2. Tunnetut yhteydet sallitaan tai kielletään käyttäjän määrittelemien sääntöjen perusteella.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Toiminto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>keskellä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>ylhäällä, oikealla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>alhaalla, oikealla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>ylhäällä, vasemmalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>alhaalla, vasemmalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>kerran</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1t</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>uudelleenkäynnistykseen asti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>ikuisesti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>Oletusarvoisesti kun uusi ponnahdusikkuna tulee näkyviin, yksinkertaisimmillaan voit suodattaa yhteyksiä tai sovelluksia yhden yhteyden ominaisuuden perusteella (ohjelmatiedosto, portti, IP-osoite jne.).</p><p>Vaihtoehtojen avulla voit valita useita kenttiä, joiden perusteella suodatat yhteyksiä.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Suodata yhteydet myös seuraavilla tavoilla:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>ohjelmatiedostoston mukaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>komentorivin mukaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>kohdeportin mukaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>kohde-IP:n mukaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>käyttäjä-ID:n mukaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>PID:n mukaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Oletuskohde</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Oletussijainti näytössä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>Ponnahdusikkunan oletuskesto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Kesto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>Edistyneessä näkymässä voit helposti valita useita kenttiä suodatettavia yhteyksiä varten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Näytä laajennettu näkymä oletusarvoisesti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Jos tämä on valittuna, ponnahdusikkunat näytetään, kun laajennettu näkymä on aktiivinen.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Tämä aikakatkaisu on lähtölaskenta, joka näkyy, kun ponnahdusikkuna näytetään.</p><p>Jos ponnahdusikkunaan ei vastata, käytetään oletusasetuksia.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Oletusaikakatkaisu</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation>Poista ponnahdusikkunat käytöstä ja näytä vain ilmoitus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Käyttöliittymä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation>Työpöytäilmoitukset</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation>Käytä järjestelmän ilmoituksia</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation>Käytä Qt-ilmoituksia</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation>Testaa</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Tapahtumavälilehden sarakkeet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Aika</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Sääntö</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Solmu</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protokolla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Kohde</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Prosessi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation>Komentorivi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation>Teema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation>Järjestelmä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation>Kieli</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation>Säännöt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation>Kun tämä vaihtoehto on valittuna, valitun keston sääntöjä ei lisätä käyttöliittymän väliaikaisten sääntöjen luetteloon. Väliaikaiset säännöt ovat edelleen voimassa, ja voit käyttää niitä, kun sinua pyydetään sallimaan/kieltämään uusi yhteys.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation>Älä tallenna/poista sääntöjä kestolta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>miltään väliaikaisilta säännöiltä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation>30s tai vähemmältä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation>5m tai vähemmältä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation>15m tai vähemmältä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation>30m tai vähemmältä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation>1t tai vähemmältä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Solmut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Prosessin monitorointimekanismi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Lokitiedosto lokien kirjoittamista varten.<br/></p><p>/dev/stdout tulostaa lokit vakiolähdölle.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Logitiedosto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Oletuskesto otetaan käyttöön, kun käyttöliittymää ei ole kytketty.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Oletuskesto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Sovella asetuksia kaikkiin solmuihin</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Vakiotoiminto suoritetaan, kun käyttöliittymää ei ole yhdistetty.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Oletustoiminto, kun käyttöliittymän yhteys on katkaistu</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>Isäntänimi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>uudelleenkäynnistykseen asti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>aina</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Solmun osoite.</p><p>Esimerkintä: unix:///tmp/osui.sock (unix:// on pakollinen, jos kyseessä on Unix-soketti)</p><p>Se voi olla myös IP-osoite portin kanssa: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Osoite</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation><html><head/><body><p>Jos valittuna, OpenSnitch pyytää sinua sallimaan tai kieltämään yhteydet, joihin ei ole liitetty PID:iä, useista syistä, useimmiten huonojen yksien takia.</p><p>Ponnahdusikkuna sisältää vain tietoja verkkoyhteydestä.</p><p>Jossain tilanteissa nämä yhteydet ovat kuitenkin kelvollisia yhteyksiä, kuten esimerkiksi luodessasi VPN:ää WireGuardin avulla.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Vianmääritä virheellisiä yhteyksiä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Versio</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Oletuslogitaso</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Tietokanta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>Muistissa</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Tiedostossa</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Tietokantatyyppi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Valitse</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation>minuuttia</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation>Tapahtumien puhdistusväli minuuteissa</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation>päivää</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation>Tapahtumien enimmäissäilytys päivissä</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Sulje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Toteuta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Tallenna</translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Prosessin tiedot</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>ladataan...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: ladataan...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>muistitilastot: ladataan...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Tila</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Avoimet tiedostot</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>I/O-tilastot</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Muistikartoitetut tiedostot</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Pino</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Ympäristömuuttujat</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Sovelluksen PID:it</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Aloita tai pysäytä tämän prosessin monitorointi</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>SUlje</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Sääntö</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Toiminto</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Kesto</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>kerran</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>uudelleenkäynnistykseen asti</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>aina</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>Esto vain sivuuttaa yhteyden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Estä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>Hylkäys pudottaa yhteyden ja tappaa sen aloittaneen liitännän</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Hylkää</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>Salli sallii yhteyden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Salli</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Ota käyttöön</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Jos valintaruutu on valittuna, tämä sääntö on etusijalla muihin sääntöihin nähden. Muita sääntöjä ei tarkisteta tämän säännön jälkeen. Sinun on nimettävä sääntö siten, että se tarkistetaan ensimmäisenä, koska säännöt tarkistetaan aakkosjärjestyksessä. Esimerkiksi: [x] Prioriteetti - 000-prioriteettisääntö [ ] Prioriteetti - 001-alhaisempi prioriteettisääntö</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Prioriteettisääntö</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>Säännöt tarkistetaan aakkosjärjestyksessä, joten voit nimetä ne sen mukaan ja asettaa ne tärkeysjärjestykseen. 000-allow-localhost 001-deny-broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation>Nimi</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Solmu</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Sovella sääntöä kaikkiin solmuihin</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>Sovellukset</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation><html><head/><body><p>Tämän kentän arvo on aina suoritettavan tiedoston absoluuttinen polku: /path/to/binary<br/></p><p> Esimerkkejä:</p><p>- Simple: /</p><p>- Useita polkuja: ^/usr/lib(64|)/firefox/firefox$</p><p>- Useita binäärejä: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Kielletään/sallitaan suoritukset /tmp:stä:</p><p>^/(var/|)tmp/.*$<br/></p><p> Lisää esimerkkejä löydät <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki-sivulta</a> tai kysy <a href="https://github.com/evilsocket/opensnitch/discussions">keskustelufoorumeilla</a>.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>Onko säännöllinen lauseke</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>Tältä käyttäjä-ID:ltä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Tästä komentorivistä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation><html><head/><body><p>Tämä kenttä sisältää käyttäjän suorittaman komentorivin ja vastaa sitä.<br/></p><p> Jos käyttäjä kirjoitti komennon, vain komento näkyy:</p><p>telnet 1.2.3.4<br/></p><p> Jos käyttäjä kirjoitti komennon absoluuttisen tai suhteellisen polun, se näkyy:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../../usr/bin/telnet 1.2.3.4.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>Tästä PID:istä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="466"/> <source>From this executable</source> <translation>Tästä ohjelmatiedostosta</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="473"/> <source>is regular expression</source> <translation>on säännöllinen lauseke</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="485"/> <source>Network</source> <translation>Verkko</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="520"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Vain TCP, UDP tai UDPLITE ovat sallittuja</p><p>Voit käyttää regexp:iä, esim: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="560"/> <source>ICMP</source> <translation>ICMP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="565"/> <source>ICMP6</source> <translation>ICMP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="570"/> <source>SCTP</source> <translation>SCTP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="575"/> <source>SCTP6</source> <translation>SCTP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="586"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>Pilkut tai välilyönnit eivät ole sallittuja useiden toimialueiden määrittämisessä. Käytä sen sijaan säännöllisiä lausekkeita: .*(opensnitch|duckduckgo).com". .*\.google.com tai yksittäinen verkkotunnus: www.gnu.org - se vastaa vain www.gnu.org, eikä ftp.gnu.org, eikä www2.gnu.org, ... gnu.org - vain gnu.org, www.gnu.org, ftp.gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/> <source>To this IP / Network</source> <translation>Tähän IP-osoitteeseen / verkkoon</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="627"/> <source>Protocol</source> <translation>Protokolla</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Voit määrittää yhden IP-osoitteen: - 192.168.1.1 tai säännöllisen lausekkeen: - 192\.168\.1\.[0-9]+ useita IP-osoitteita: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Voit myös määrittää aliverkon: - 192.168.1.0/24 Huomautus: Pilkut tai välilyönnit eivät saa erottaa IP-osoitteita tai verkkoja toisistaan.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="659"/> <source>LAN</source> <translation>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="664"/> <source>MULTICAST</source> <translation>MULTICAST</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="669"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="674"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="679"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="689"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="694"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="699"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="704"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="709"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="714"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="719"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="724"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="729"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="737"/> <source>From this IP / Network</source> <translation>Tästä IP-osoitteesta / verkosta</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="744"/> <source>To this host</source> <translation>Tälle isännälle</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/> <source>Network interface</source> <translation>Verkkoliitäntä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="866"/> <source>From this port</source> <translation>Tästä portista</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation><html><head/><body><p>Voit määrittää useita portteja käyttämällä säännöllisiä lausekkeita:</p><p>- 53, 80 tai 443:</p><p>^(53|80|443)$</p><p><br/></p> <p> - 53, 443 tai 5551, 5552, 5553, jne:</p><p>^(53|443|555[0-9])$.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="896"/> <source>To this port</source> <translation>Tähän porttiin</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="926"/> <source>List of domains/IPs</source> <translation>Luettelo verkkotunnuksista/IP-osoitteista</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>To this list of network ranges</source> <translation>Tähän verkkoalueiden luetteloon</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="939"/> <source>To this list of IPs</source> <translation>Tähän IP-osoitteiden luetteloon</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="965"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Valitse hakemisto, jossa on estettävien tai sallittujen IP-osoitteiden luettelon sisältäviä tiedostoja:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>jne.</p><p>Yksi IP-osoite per rivi. Tyhjät tai #-alkuiset rivit jätetään huomiotta.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="974"/> <source>To this list of domains</source> <translation>Tähän verkkotunnusten luetteloon</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1000"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Valitse hakemisto, jossa on estettävien tai sallittujen verkkoalueiden luettelon sisältäviä tiedostoja:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>jne.<br/></p><p> Yksi verkkoalue per rivi. Tyhjät tai #-alkuiset rivit jätetään huomiotta.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1028"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Valitse hakemisto, jossa on luetteloita estettävistä tai sallittavista verkkotunnuksista.</p><p>Laita kyseiseen hakemistoon minkä tahansa tiedostopäätteen omaavia tiedostoja, jotka sisältävät luetteloita verkkotunnuksista.</p><p><br/> Luettelon jokaisen merkinnän muoto on seuraava (hosts-muodossa):</p><p>127.0.0.1 www.domain.com</p><p>tai </p><p>0.0.0.0.0 www.domain.com</p><p>Tyhjiä rivejä tai rivejä, jotka alkavat merkinnällä #, ei huomioida.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1043"/> <source>To this list of domains (regular expressions)</source> <translation>Tähän verkkotunnusten luetteloon (säännölliset lausekkeet)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1070"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Valitse hakemisto, jossa on tiedostoja, jotka sisältävät säännöllisiä lausekkeita estettävistä tai sallittavista verkkotunnuksista:</p><p>.*\.example\.com</p><p>Voit myös käyttää verkkotunnusta sellaisenaan: &quot;example.com&quot;, jolloin se vastaa whatever.example.com, whatever.example.com.localdomain jne.</p><p>Yksi verkkotunnus riviä kohti. Tyhjät tai #-alkuiset rivit jätetään huomiotta.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1080"/> <source>More</source> <translation>Lisää</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Oletusarvoisesti sääntöjen kentässä ei oteta huomioon isoja ja pieniä kirjaimia, eli jos prosessi yrittää käyttää gOOgle.CoM:ää ja sinulla on sääntö Deny .*google.com, yhteys estetään.<br/></p><p> Jos ruksaat tämän ruudun, sinun on määritettävä tarkka merkkijono (verkkotunnus, suoritettava ohjelma, komentorivi), jonka haluat suodattaa.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1089"/> <source>Case-sensitive</source> <translation>Kirjainkoolla on merkitystä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1096"/> <source>Don't log connections that match this rule</source> <translation>Älä logita yhteyksiä, jotka vastaavat tätä sääntöä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1099"/> <source>Don't log connections</source> <translation>Älä logita yhteyksiä</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1145"/> <source>Description...</source> <translation>Kuvaus...</translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch -verkkotilastot</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Suodatin</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Salli</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Estä</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Hylkää</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Esim.: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Poista kaikki kaapatut tapahtumat</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Luo uusi sääntö</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Tila</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Aloita tai lopeta kaappaus</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Tapahtumat</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Solmut</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation>Poista tämä solmu</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation>Näytä tämän solmun asetukset</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation>Tämän solmun kuuntelun aloittaminen tai lopettaminen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Säännöt</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Sovellussäännöt</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>Pysyvä</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Väliaikainen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation>Järjestelmän säännöt</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Kaikki sovellukset</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>ota käyttöön</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Muokkaa sääntöä</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Poista sääntö</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Isännät</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Sovellukset</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Osoitteet</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Portit</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Käyttäjät</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Yhteydet</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Pudotetut</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Käynnissäoloaika</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Versio</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Tilastot</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Ota käyttöön</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Poista käytöstä</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Apua</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Sulje</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation>Asetukset toteutettu.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation>Virhe: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation>Toteutetaan muutoksia...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation>Virhe INPUT-ketjun käytännön saamisessa</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation>Virhe OUTPUT-ketjun käytännön saamisessa</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation>Jotta voimme määrittää palomuurisääntöjä käyttöliittymästä, meidän on käytettävä 'nftables'-ohjelmaa 'iptables'-ohjelman sijasta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation>Otetaan käyttöön palomuuria...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation>Otetaan palomuuria pois käytöstä...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>Kohdeportti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>Lähdeportti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>Kohde-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation>Lähde-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation>Tuloliitäntä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation>Lähtöliitäntä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation>Aseta conntrack-merkki</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation>Kohdista conntrack-merkki</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation>Kohdista conntrack-tila(t)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation>Aseta merkki pakettiin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation>Kohdista pakettitiedot</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation>Kaistanleveyskiintiöt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation>Nopeusrajoita yhteyksiä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation> Tuetut formaatit: - 23 - Alueet: 80-1024 - Useita portteja: 80,443,8080 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation> Tuetut formaatit: - 1.2.3.4 - IP-alueet: 1.2.3.100-1.2.3.200 - Verkkoalueet: 1.2.3.4/24 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation>Kohdista tuloliitäntä. Säännölliset lausekkeet eivät ole sallittuja.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation>Sovita lähtöliitäntä. Säännölliset lausekkeet eivät ole sallittuja.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation>Asettaa yhteyden conntrack-merkki desimaalimuodossa.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation>Kohdista yhteyden conntrack-merkki, desimaalimuodossa.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation>Kohdista conntrack-tilat. Tuetut formaatit: - Yksinkertainen: new - Useita tiloja pilkulla erotettuna: related,new </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation> Match-paketin metatiedot. Arvon on oltava desimaalimuodossa, paitsi "l4proto"-vaihtoehdon tapauksessa. l4proto voi olla esimerkiksi pienellä alkukirjaimella kirjoitettu merkkijono: tcp udp icmp, jne Jos protokollan tai lproton arvo on desimaalinen, se käyttää sitä koodina, joka on protokollan koodina. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation>Asettaa paketille merkki, joka vastaa määritettyjä ehtoja. Arvo on desimaalimuodossa.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation> Kohdista ICMP-koodit. Tuetut muodot: - Yksinkertainen: echo-request - Useita pilkulla erotettuna: echo-request,echo-reply </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation> Kohista ICMPv6-koodit. Tuetut muodot: - Yksinkertainen: echo-request - Useita pilkulla erotettuna: echo-request,echo-reply </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation>Tulostaa viestin, kun tämä sääntö vastaa pakettia.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation> Sovelletaan kiintiöitä yhteyksiin. Esimerkiksi kun: - Sovelletaan määriteltyä toimintoa (DROP), esimerkiksi: "kiintiö yli 10 megatavua". - "kiintiö enintään 10 megatavua" -> sovelletaan määriteltyä toimintoa (ACCEPT). Arvon on oltava muotoa: VALUE/UNITS, esimerkiksi: - 10mbytes, 1/gbytes, jne </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation> Rajoita yhteyksiä. Esimerkiksi kun: - Sovelletaan määriteltyä toimintoa (DROP, ACCEPT jne.). (Kun yhteyksiä on yli 10 Mt minuutissa, sovelletaan toimintoa). - "rajoitus enintään 10 megatavua/tunti" -> sovelletaan määriteltyä toimintoa (ACCEPT). Arvon on oltava muotoa: VALUE/UNITS/TIME, esimerkiksi: - 10/mbytes/minute, 1/gbytes/hour, jne </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation>Protobuf-versiosi ei ole yhteensopiva, sinun on asennettava protobuf 3.8.0 tai uudempi versio. (pip3 install --ignore-installed protobuf==3.8.0)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation>Sääntö poistettu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation>Sääntö lisätty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation>Voit käyttää ',' tai '-' -merkkejä määrittääksesi useita portteja/IP-osoitteita tai alueita/arvoja:<br><br>ports: 22 tai 22,443 tai 50000-60000<br>IP:t: 192.168.1.1 tai 192.168.1.30-192.168.1.130<br>arvot: echo-reply,echo-request<br>arvot: new,established,related</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation>Poistetaan sääntöä, odota</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation>Virhe säännön päivittämisessä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation>Lisäätään sääntöä, odota</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation><valitse lausuma></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation>num</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation>kohteeseen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation>Yhtä suuri</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation>Ei yhtäläinen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation>Suurempi tai yhtä suuri kuin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation>Suurempi kuin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation>Pienempi tai yhtä suuri kuin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation>Vähemmän kuin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation>Palomuurisääntö</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation>Yksinkertainen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation>Edistynyt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation>Tätä sääntöä ei vielä tueta.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation>Sulje palvelu pois</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation>Salli saapuvat yhteydet valittuun porttiin.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation>Salli lähtevät yhteydet valittuun porttiin.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation>valitse lausuma.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation>arvo ei voi olla 0 tai tyhjä.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation>arvomuoto on 1024/kbytes (tai bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation>arvomuoto on 1024 kbytes/sekunti (tai bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation>rajoitus ei kelpaa, käytä: bytes, kbytes, mbytes tai gbytes.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation>aikaraja ei ole voimassa, käytä: second, minute, hour tai day</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation>portti ei kelpaa.</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation>Tiedot</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation>Virheet</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation>Varoitukset</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>Järjestelmäilmoitukset eivät ole käytettävissä, sinun on asennettava python3-notify2.</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation>Avaa</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Salli</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Estä</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation>Uusi lähtevä yhteys</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>on yhdistämässä <b>%s</b> kohteen %s portissa %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>uudelleenkäynnistykseen asti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>ikuisesti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation>Hylkää</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Lähtevä yhteys</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Prosessi käynnistetty kohteesta:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>tästä ohjelmatiedostosta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>tästä komentorivistä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>porttiin {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>kohteeseen {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>käyttäjältä {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation>tästä PID:istä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>kohteeseen {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>kohteeseen *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation><b>Etä</b>prosessi %s on käynnissä kohteessa <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation>yhdistää kohteeseen <b>%s</b>, %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>yrittää selvittää <b>%s</b>%s, %s portti %d kautta</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Varoitus</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>Uudelleenkäynnistä käyttöliittymä uudelleen, jotta muutokset tulevat voimaan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation>Solmuja ei ole yhdistetty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation>Järjestelmän oletus</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation>Järjestelmä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>Teemat eivät ole käytettävissä. Asenna qt-material: pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>Palvelimen osoite ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Virhe asetuksen {0} lataamisessa</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Poikkeus asetusten tallentamisessa: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>Tietokantatyyppi muutettu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Sinun on valittava tiedosto tietokannalle<br>tai valittava tyypiksi "Muistissa".</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation>Kieli muuutettu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation>Käyttöliittymäteema muutettu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation>Uudelleenkäynnistä käyttöliittymä, jotta uusi teema tulee voimaan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Toteutetaan asetuksia {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation>Ok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation>Virhe solmun asetusten tallennuksessa {0}: {1}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>Asetukset toteutettu.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Virhe asetusten toteuttamisessa: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Vie hiiri tekstien päälle näyttääksesi ohjeen.<br><br>Älä unohda käydä wikissä: <a href="{0}">{0}</a></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Virhe prosessin tietojen lataamisessa:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Virhe prosessin monitoroinnin pysäyttämisessä:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>ladataan...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>Solmuja ei ole yhdistetty.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation>Tällä nimellä on jo sääntö.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>Sovellettu sääntö.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Virhe säännön soveltamisessa: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Virhe säännön lataamisessa</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>protokolla ei voi olla tyhjä, tai poista valintaruutu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Protokollan regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>prosessipolku ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Prosessin polun regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>komentorivi ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Komentorivin regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation>Verkkoliitäntä ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation>Verkkoliitännän regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation>Lähdeportti ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation>Lähdeportin regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>Kohdeportti ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Kohdeportin regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>Kohdeisäntä ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Kohdeisännän regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation>Lähde-IP/-verkko ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation>Lähde-IP:n regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>Kohde-IP/-verkko ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Kohde-IP:n regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>Käyttäjä-ID ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Käyttäjä-ID:n regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation>PID-kenttä ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation>PID-kentän regexp-virhe</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>Listat-kenttä ei voi olla tyhjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>Listat-kentän on oltava hakemisto</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation>Valitse vähintään yksi kenttä.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Sääntö ei tuettu</b></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation>VAROITUS</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation>Uusi solmu kytketty</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Mikä</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>Osumia</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>Verkon nimi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Aika</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Solmu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Toiminto</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kohde</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protokolla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Prosessi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Sääntö</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nimi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Osoite</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Tila</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Isäntänimi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Käynnissäoloaika</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Versio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Säännöt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kesto</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kuvaus</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Käytössä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ensisijaisuus</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Osumia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Komentorivi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kohde-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kohdeisäntä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kohdeportti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Käyttäjä-ID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Viimeinen yhteys</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Ei käynnissä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Poissa käytöstä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Käynnissä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation>Vientisäännöt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation>Tuontisäännöt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation>Vie tapahtumat CSV-tiedostoon</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation>Lopeta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Yhteydet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Pudotetut</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Mikä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch-verkkotilastot {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch-verkkotilastot {0}:lle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation>Yksityiskohdat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation>Säännöt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation>Uusi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation>Toiminto</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Poista käytöstä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Ota käyttöön</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Poista</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Muokkaa</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation>Hae kohteeseen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation>Vie</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Salli</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Estä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation>Hylkää</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Aina</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Uudelleenkäynnistykseen asti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Kaksoiskappaleet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation>Leikepöydälle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation>Levylle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Oletko varma?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation>Valitse hakemisto, johon säännöt viedään</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Olet poistamassa tätä sääntöä. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation> Olet poistamassa tätä merkintää. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Sääntöä ei löydy kyseisellä nimellä ja solmulla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Virhe:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Varoitus:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation> Olet poistamassa tätä solmua. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation><b>Virhe poistaessa solmua</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Olet poistamassa tätä sääntöä. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation>Virhe vietäessä sääntöjä</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation>Valitse hakemisto, jossa on tuotavia sääntöjä (JSON-tiedostot)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation>Säännöt tuotu hyvin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Tallenna CSV:nä</translation> </message> </context> </TS> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/fr_FR/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017503�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/fr_FR/opensnitch-fr_FR.ts������������������������������������������0000664�0000000�0000000�00000461451�15003540030�0023234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="fr"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>ID utilisateur</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Exécuté depuis</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>IP source</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>ID processus</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>IP destination</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>interface destination</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>depuis cet exécutable</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>depuis cette ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>vers cette interface</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>cet utilisateur</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>vers cette IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>une seule fois</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation type="unfinished">30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation type="unfinished">5mn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation type="unfinished">15mn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation type="unfinished">30mn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation type="unfinished">1h</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>définitivement</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation type="unfinished">Refuser</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Permettre</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished">Activer</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation type="unfinished">Effacer</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished">Enregistrer</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Préférences</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>IU</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>attente par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>attente dialogue par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>attente par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Acción por defecto de la ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Acción por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>cible par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>centré</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>en haut à droite</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>en bas à gauche</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>en haut à gauche</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>en bas à gauche</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posición por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>par l'exécutable</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>par la ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>par l'interface de destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>par l'IP de destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>par cet utilisateur</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>une seule fois</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation type="unfinished">30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation type="unfinished">5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation type="unfinished">15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation type="unfinished">30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation type="unfinished">1h</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>définitivement</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Pas de dialogue, montrer juste une alerte</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>noeuds</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>méthode de surveillance des processus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>La durée par défaut concerne le cas où aucun utilisateur n'est connecté.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Adresse du noeud.</p><p>Default: unix:///tmp/osui.sock (unix:// requise si c'est un socket Unix)</p><p>Peut aussi être une adresse IP avec son port: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>détail noté dans le log par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>L'action par défaut se déclenche s'il n'y a pas d'utilisateur connecté.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>fichier dans lequel enregistrer le log<br/></p><p>/dev/stdout imprime les logs dans le standard output.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Fichier log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. La ventana emergente sólo contendrá información relativa a la conexión. Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexiones desconocidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>nom d'hôte (host)</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>toujours</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>appliquer la configuration à tous les noeuds</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Base de données</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>En mémoire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Fichier</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Fermer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Appliquer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Enregistrer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>type de base de données</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Choisir</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posición en pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="102"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Montrer par défaut la vue détaillée</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Action</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Sélectionner pour que les dialogues s'ouvrent avec le détail avancé.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Durée</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>Par défaut un nouveau dialogue en version simple propose de filtrer les connexions ou les applications sur une propriété de la connexion (exécutable, port, IP, etc).</p><p>Avec ces options, vous pouvez choisir plusieurs critères pour filtrer les connexions.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Filtrer aussi les connexion par :</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="362"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>ID utilisateur</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>interface (port) de destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>IP de destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Ctte durée est l'attente par défaut lorsqu'un dialogue est présenté.</p><p>Sans réponse au dialogue, les options par défaut sont appliquées.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>La vue avancée permet de choisir facileent plusieurs critères pour filtrer les connexions</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>cocher pour que ce champ soit préselectionné lorsqu'un dialogue est affiché</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>Action par défaut du dialogue.</p><p>Lorsqu'un nouveau dialogue est affiché, cette action sera présélectionnée, et donc appliquée s'il n'y a pas de réponse après le délai par défaut.</p><p><br/></p><p>Pendant que le dialogue demande si on autorise ou non la connexion:</p><p>1. toute autre nouvelle demande de connexion est refusée.</p><p>2. les connexions déjà connues sont autorisée ou rejetées selon les règles définies par l'utilisateur.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Action par défaut lorsque l'interface graphique utilisateur n'est pas connectée</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Noter (debug) les connexions invalides</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Dialogues</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Options par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Position par défaut sur l'écran</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>toute règle temporaire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Lorsque cette option est choisie, les règles de la durée sélectionnée ne sont pas ajoutées à la liste des règles temporaires présentées dans l'interface graphique utilisateur.</p><p><br/></p><p>Les règles temporaires sont cependant toujours valides et peuvent être utilisées lors d'une demande d'autorisation /refus de connexion.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="490"/> <source>Don't save rules of duration</source> <translation type="obsolete">Ne pas enregistrer les règles de cette durée :</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source>Show events columns</source> <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Temps</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protocole</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Processus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Règle</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Noeud</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>cochez ceci pour recevoir une demande d'autorisation/refus sur les connexions qui n'ont pas de processus (PID) associé, généralement parce que la connexion est en mauvais état.</p><p>Le dialogue ne contiendra alors que l'information sur la connexion.</p><p>Il y a cependant des scénarios pour lesquels ces demandes sont valides, comme par exemple l'établissement d'un VPN utilisant wireguard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>colonnes évènements</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Détail processus</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>chargement...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>chargement CWD...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>chargement statistiques mémoire...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Etat</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Fichiers ouverts</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>Statistiques entrées/sorties</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Fichiers mappés en mémoire</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Pile</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Variables d'environnement</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>PIDs application</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Démarrer ou arrêter la surveillance de ce processus</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Fermer</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Règle</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Noeud</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Appliquer la règle à tous les noeuds</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Depuis cette ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>Depuis cet exécutable</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Action</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/chemin/vers/executable, .*/bin/executable[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>vers cette IP / ce réseau</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>une seule fois</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation type="obsolete">30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation type="obsolete">5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation type="obsolete">15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation type="obsolete">30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation type="obsolete">1h</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>toujours</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>vers cette interface (port)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>depuis cet utilisateur (ID)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>ni virgules ni espaces ne sont autorisés pour spécifier de multiples domaines. Utiliser à la place des 'expressions régulières' (RegExp): .*(opensnitch|duckduckgo).com .*\.google.com ou bien un seul domaine: www.gnu.org - correspond uniquement à www.gnu.org, mais pas ftp.gnu.org, ou www2.gnu.org, ... gnu.org - correspond uniquement à gnu.org, mais pas www.gnu.org, ou ftp.gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Seuls TCP, UDP ou UDPLITE sont permis</p><p>On peut employer des expressions régulières (regexp), par ex.: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>On peut spécifier une IP unique: - 192.168.1.1 ou une "expression régulière": - 192\.168\.1\.[0-9]+ pluseurs IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ On peut aussi spécifier un sous-réseau: - 192.168.1.0/24 Note : on ne peut pas utiliser virgules ou espaces pour séparer plusieurs IP ou réseaux.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> <source>LAN</source> <translation type="obsolete">LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation type="obsolete">127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation type="obsolete">192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation type="obsolete">192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation type="obsolete">192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation type="obsolete">192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation type="obsolete">169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation type="obsolete">172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation type="obsolete">10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation type="obsolete">::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation type="obsolete">fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation type="obsolete">ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation type="obsolete">fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation type="obsolete">fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Durée</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protocole</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>Vers cet hôte</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Nom</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Activer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>Les règles étant appliquées par ordre aplhabétique, on peut leur donner priorité grâce à leur nom. 000-allow-localhost 001-deny-broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> <source>leave blank to autocreate</source> <translation type="obsolete">ne pas remplir va créer automatiquement</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Cocher pour que cette règle ait priorité sur toutes les autres. Aucune autre règle ne sera appliquée après celle-ci. Vous devez nommer la règle de façon qu'elle soit testée en premier, par ordre alphabétique. Par exemple: [x] Priority - 000-règle-prioritaire [ ] Priority - 001-règle-moins-prioritaire</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Règle prioritaire</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Par défaut, le champ des règles n'est pas sensible à la casse. Par exemple si un processus tente d'atteindre gOOgle.CoM alors que vous avez une règle interdisant .*google.com, la connexion gOOgle.CoM sera bloquée.<br/></p><p>Si vous cochez ceci, vous devrez spécifier la chaîne exacte (domaine, exécutable, ligne de commande) que vous voulez filtrer.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>sensible à la casse</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Vous pouvez spécifier plusieurs ports d'interface avec des "expessions régulières":</p><p><br/></p><p>- 53, 80 ou 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 ou 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>Vers cette liste de domaines</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Choisir un dossier contenant des listes de domaines à autoriser ou interdire.</p><p>Y mettre des fichiers contenant des listes de domaines, avec n'importe quelle extension.</p><p><br/>Le format de chaque entrée (hist format) est comme suit:</p><p>127.0.0.1 www.domain.com</p><p>ou </p><p>0.0.0.0 www.domain.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation type="unfinished">Applications</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>Statistiques réseau Opensnitch</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">Enregistrer au format CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">contrôle-S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Créer une nouvelle règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Etat</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Démarre ou stoppe l'interception</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Evènements</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filtre</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Ex.: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Noeuds</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double cliquer sur la colonne Addr pour voir les détails d'un noeud)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Règles</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>activer</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="684"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">rechercher par nom de règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Règle d'applications</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>Permanent</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Temporaire</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Hôtes</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double clic pour voir les détails d'un élément)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Applications</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Adresses</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Ports (interfaces)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Utilisateurs</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Connexions</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Abandonnée</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>durée active (uptime)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Effacer tous les évènments interceptés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Editer règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Effacer règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Effacer tous les hôtes interceptés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Effacer toutes les applications interceptées</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Effacer toutes les adresses interceptées</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Effacer tous les ports interceptés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Effacer tous les utilisateurs interceptés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double clic sur une ligne pour voir les détails d'une règle)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Effacer les connexions qui déclenchaient cette règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Toutes les applications</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Statistiques</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Aide</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Fermer</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Activer</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Désactiver</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished">Configuration appliquée.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished">IP source</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation type="unfinished">Alerte</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>indéfiniment</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Connexion sortante</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Processus lancé par :</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>depuis cette ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>depuis cet exécutable</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Proceso no encontrado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>vers le port {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>vers {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>de l'utilisateur {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>vers {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>vers *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">vers *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation>processus <b>Distant</b> %s tournant sur <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>se connecte à <b>%s</b> sur %s port %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>tente de résoudre (connecter) <b>%s</b> via %s, %s port %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Error al guarda la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Aplicando configuración en %s ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>L'adresse du serveur ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Error al cargar la configuración %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>Configuration appliquée.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Error al aplicar la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Config enregistrant les exceptions : {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Applique la configuration à {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Erreur au chargement configuration {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Erreur en tentant d'appliquer la configution : {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Alerte</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Vous devez sélectionner un fichier pour la base de données<br>ou choisir le type "en mémoire".</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>type de base de données changé</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>Prendra effet au redémarrage de l'interface graphique</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Glisser la souris sur les textes pour afficher l'aide<br><br>N'hésitez pas à visiter le Wiki : <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Erreur au chargement d'information sur le processus:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Erreur en stoppant le processus de monitoring:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>chargement...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>Aucun noeud n'est connecté.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>Règle appliquée.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Error al aplicar la regla: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>le protocole ne peut être vide, ou bien le désélectionner</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Erreur à l'interprétation du RegExp protocole</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>le chemin vers le processus ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>erreur RegExp dans le chemin vers le processus</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>la ligne de commande ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Erreur RegExp dans la ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>l'interface (port) destination ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>ereur RegExp sur l'interface (port) de destination</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>l'hôte de destination ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>erreur RegExp sur l'hôte de destination</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>l'IP / réseau de destination ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>erreur RegExp sur l'IP destination</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>l'ID utilisateur ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>erreur RegExp sur l'ID utilisateur</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Erreur en appliquant la règle : {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>le champ "listes" ne peut être vide</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>le champ Listes doit être un répertoire</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>RRègle non supportée</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Erreur au chargement de la règle</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Inactif</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>désactivé</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Actif</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Vous êtes sur le point d'effacer cette règle. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Êtes-vous sûr?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>Statistiques réseau OpenSnitch {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>Statistique réseau OpenSnitch pour {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation type="unfinished">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Enregistrer en CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Effacer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Désactiver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Activer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Dupliquer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Editer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Pas trouvé de règle pour ces nom et noeud</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Erreur:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Alerte :</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Toujours</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Vous êtes sur le point d'effacer cette règle. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> <source>LastConnection</source> <translation type="obsolete">Última Conexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nom</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Etat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nom d'hôte</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Règles</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Temps</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Action</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Durée</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Noeud</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Activé</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Connexions</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protocole</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Processus</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Destination</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Règle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ID utilisateur</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DernièreConnexion</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstHost</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstPort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> <source>Uptime</source> <translation type="obsolete">durée active (uptime)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> <source>Connections</source> <translation type="obsolete">Connexions</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> <source>Dropped</source> <translation type="obsolete">Abandonnée</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">durée active (uptime)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Connexions</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Abandonnée</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/hu_HU/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017515�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/hu_HU/opensnitch-hu_HU.ts������������������������������������������0000664�0000000�0000000�00000463102�15003540030�0023253�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="hu_HU"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="150"/> <source>Chromium Web Browser</source> <translation type="obsolete">Chromium webböngészÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="179"/> <source><html><head/><body><p>/opt/google/chrome/bin/chrome --something abc --more-long def --for-word-wrapping</p></body></html></source> <translation type="obsolete"><html><head/><body><p>/opt/google/chrome/bin/chrome --valami ábécé --hosszabb-ideig def --szócsomagoláshoz</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="226"/> <source>(/path/to/bin/chromium)</source> <translation type="obsolete">(/út/a/bináris/Chromiumhoz)</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>ettÅ‘l a futtatható fájltól</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>errÅ‘l a parancssorról</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>ezt célkikötÅ‘t</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>ezt a felhasználót</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>ezt a cél IP címet</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>egyszer</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30 másodperc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5 perc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15 perc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30 perc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1 óra</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>örökre</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>Felhasználói azonosító</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Futtatható fájl innen:</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>Szövegcímke</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Forrás IP-cím</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>Folyamatazonosító</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>Cél IP-címe</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>CélkikötÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="271"/> <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source> <translation type="obsolete">A Chromium webböngészÅ‘ csatlakozni akar a www.evilsocket.net webhelyhez a 443-as TCP porton. És talán a www.goodsocket.net webhelyhez a 344-es TCP porton</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>ebbÅ‘l a folyamatazonosítóból</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>művelet</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>Tűzfal</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Tűzfal</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>BejövÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>KimenÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>Profil</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation>BejövÅ‘ kapcsolatok engedélyezése egy kikötÅ‘höz</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation>Szolgáltatás engedélyezése (BE)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation>A kikötÅ‘höz tartó kimenÅ‘ kapcsolatok kizárása az elfogásból</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation>Szolgáltatás engedélyezése (KI)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation>Új szabály</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="453"/> <source>xxx</source> <translation type="obsolete">xxx</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation>Bezárás</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>Tűzfalszabály</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>Csomópont</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> <source>All</source> <translation type="obsolete">Összes</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation>Leírás</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation>Egyszerű</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation>Új feltétel hozzáadása</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation>Kiválasztott feltétel eltávolítása</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation>Irány</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation>BE</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation>KI</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation>Művelet</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation>ELFOGADÃS</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation>KIDOBÃS</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation>ELUTASÃTÃS</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation>VISSZATÉRÉS</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation>Kiürítés</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation>Törlés</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation>Mentés</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation>Hozzáadás</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Beállítások</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Felhasználói felület</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Az elÅ‘ugró ablakok alapértelmezett beállításai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Felugró ablakok alapértelmezett helye a képernyÅ‘n</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Alapértelmezés szerint a haladó nézet megjelenítése</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>egyszer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30 másodperc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5 perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15 perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30 perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1 óra</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>örökre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Felugró ablak alapértelmezett művelete</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Művelet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Alapértelmezett cél</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Ha be van jelölve, az elÅ‘ugró ablakok aktív haladó nézettel jelennek meg.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>megtagadás</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>futtatható fájl szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>parancssor szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>célkikötÅ‘ szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>rendeltetési hely IP címe szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>felhasználói azonosító szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>középre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>jobb felsÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>jobb alsó</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>bal felsÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>bal alsó</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>ElÅ‘ugró ablak alapértelmezett idÅ‘tartama</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>IdÅ‘tartam</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>Alapértelmezés szerint, amikor egy új elÅ‘ugró ablak jelenik meg, a legegyszerűbb formájában képes lesz a kapcsolatok vagy alkalmazások szűrésére a kapcsolat egy tulajdonságával (futtatható fájl, kikötÅ‘, IP-cím stb.).</p><p>Ezekkel az opciókkal több mezÅ‘t is választhat, amelyekhez a kapcsolatokat szűrni kívánja.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Szűrje a csatlakozásokat az alábbiak szerint is:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>Felhasználói azonosító</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>CélkikötÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>Cél IP-címe</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Tiltsa le a elÅ‘ugró elemet, csak riasztást jelenítsen meg</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Ez az idÅ‘korlát az a visszaszámlálás, amelyet akkor láthat, amikor egy felbukkanó párbeszédpanel jelenik meg.</p><p>Ha nem válaszol a felbukkanó párbeszédpanelre, akkor az alapértelmezett beállítások kerülnek alkalmazásra.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Alapértelmezett idÅ‘túllépés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Csomópontok</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Folyamatfigyelés módszer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Naplófájl a naplók írásához.<br/></p><p>A /dev/stdout naplókat nyomtat a normál kimenetre.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Naplófájl</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Az alapértelmezett idÅ‘tartam akkor kerül végrehajtásra, ha nincs csatlakoztatva felhasználói felület.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Alapértelmezett idÅ‘tartam</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Beállítások alkalmazása az összes csomópontra</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Az alapértelmezett művelet akkor kerül végrehajtásra, ha nincs csatlakoztatva felhasználói felület.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Alapértelmezett művelet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>Ãllomásnév</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="671"/> <source>proc</source> <translation type="obsolete">proc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="676"/> <source>ebpf</source> <translation type="obsolete">ebpf</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>audit</source> <translation type="obsolete">audit</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="686"/> <source>ftrace</source> <translation type="obsolete">ftrace</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>újraindításáig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>mindig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>A csomópont címe.</p><p>Alapértelmezett: unix:///tmp/osui.sock (Az „unix://†kötelezÅ‘, ha Unix szoftvercsatorna)</p><p>Lehet IP-cím is a kikötÅ‘vel: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Cím</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Ha be van jelölve, az OpenSnitch több okból is meg fogja engedni vagy megtagadja azokat a kapcsolatokat, amelyek nem rendelkeznek társított folyamatazonosítóval.</p><p>A felbukkanó párbeszédpanel csak a hálózati kapcsolatról tartalmaz információkat.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Ismeretlen kapcsolatok elfogása</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Változat</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="778"/> <source>DEBUG</source> <translation type="obsolete">HIBAKERESÉS</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="783"/> <source>INFO</source> <translation type="obsolete">TÃJÉKOTTATÃS</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="788"/> <source>IMPORTANT</source> <translation type="obsolete">FONTOS</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="793"/> <source>WARNING</source> <translation type="obsolete">FIGYELMEZTETÉS</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="798"/> <source>ERROR</source> <translation type="obsolete">HIBA</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="803"/> <source>FATAL</source> <translation type="obsolete">VÉGZETES</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Alapértelmezett naplószint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Adatbázis</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Adatbázistípus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Kijelölés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>Memóriabeli</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Fájl</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Bezárás</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Alkalmazás</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Mentés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>A haladó nézet lehetÅ‘vé teszi több mezÅ‘ egyszerű kiválasztását a kapcsolatok szűréséhez</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Ha be van jelölve, akkor ez a mezÅ‘ lesz kiválasztva, amikor egy elÅ‘ugró jelenik meg</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>ElÅ‘ugró ablak alapértelmezett művelete.</p><p>Amikor új kimenÅ‘ kapcsolat jön létre, ez a művelet alapértelmezés szerint ki lesz választva, tehát ha az idÅ‘túllépés aktiválódik, akkor ez az opció lesz alkalmazva.</p><p><br/></p><p>Miközben egy elÅ‘ugró ablak kéri a felhasználót, hogy engedélyezze vagy tagadja meg a kapcsolatot:</p><p>1. megtagadják az új kimenÅ‘ kapcsolatokat.</p><p>2. az ismert kapcsolatok a felhasználó által meghatározott szabályok alapján engedélyezhetÅ‘k vagy megtagadhatók.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Alapértelmezett művelet a grafikus felhasználói felület leválasztása esetén</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Érvénytelen kapcsolatok hibakeresése</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>ElÅ‘ugró ablakok</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Alapértelmezett beállítások</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Alapértelmezett hely a képernyÅ‘n</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>bármilyen ideiglenes szabályt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Ha ezt az opciót választja, a kiválasztott idÅ‘tartam szabályai nem kerülnek hozzáadásra a grafikus felhasználói felület ideiglenes szabályainak listájához.</p><p><br/></p><p>Az ideiglenes szabályok továbbra is érvényesek, és használhatja Å‘ket, amikor a rendszer kéri az új kapcsolat engedélyezését/elutasítását.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="490"/> <source>Don't save rules of duration</source> <translation type="obsolete">Ne mentse az idÅ‘tartam szabályait</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>IdÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Cél</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Folyamat</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Szabály</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Csomópont</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Ha be van jelölve, az OpenSnitch felszólítja Önt, hogy engedélyezze vagy utasítsa el azokat a kapcsolatokat, amelyek nem rendelkeznek aszocizált folyamatazonosítóval, több okból, fÅ‘leg a rossz állapotú kapcsolatok miatt.</p><p>Az elÅ‘ugró párbeszédablak csak a hálózati kapcsolatra vonatkozó adatokat tartalmazza.</p><p>Vannak azonban olyan esetek, amikor ezek érvényes kapcsolatok, például amikor virtuális magánhálózatot hoznak létre a WireGuard használatával.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Események lap oszlopai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>folyamatazonosító által</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation>ElÅ‘ugró ablakok letiltása, csak értesítés megjelenítése</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation>Asztali értesítések</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation>Rendszerértesítések használata</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation>Qt-értesítések használata</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation>Tesztelés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation><html><head/><body><p>Ha be van jelölve, az OpenSnitch felkéri, hogy engedélyezze vagy tiltsa le azokat a kapcsolatokat, amelyekhez nem tartozik folyamatazonosító, több okból is, fÅ‘leg a rossz állapotú kapcsolatok miatt.</p><p>A felugró párbeszédpanel csak a hálózati kapcsolatra vonatkozó információkat tartalmaz.</p><p>Vannak olyan esetek, amikor ezek érvényes kapcsolatok, például virtuális magánhálózat WireGuard segítségével történÅ‘ létesítésekor.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation>perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation>Eseménytisztítások közötti percek száma</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation>nap</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation>Események megtartására nyitva álló napok száma</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>elutasítás</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation>Rendszer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation>Parancssor</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation>Téma</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation>Szabályok</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation>Ha ez a lehetÅ‘ség be van jelölve, a kiválasztott idÅ‘tartamra vonatkozó szabályok nem lesznek hozzáadva az ideiglenes szabályok listájához a grafikus felhasználói felületen. Az ideiglenes szabályok továbbra is érvényesek maradnak, és használhatja Å‘ket, amikor a rendszer kéri, hogy engedélyezze/megtagadja az új kapcsolatot.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation>Ne mentse/törölje az idÅ‘tartamra vonatkozó szabályokat:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation>30 másodperc vagy kevesebb</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation>5 perc vagy kevesebb</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation>15 perc vagy kevesebb</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation>30 perc vagy kevesebb</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation>1 óra vagy kevesebb</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Folyamat részletei</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>betöltés…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: betöltés…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>memória statisztika: betöltés…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Ãllapot</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Fájlok megnyitása</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>I/O statisztika</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Memóriába ágyazott fájlok</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Verem</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Környezeti változók</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Alkalmazás folyamatazonosítók</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Folyamatfigyelés indítsa/leállítása</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Bezárás</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Szabály</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Csomópont</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Alkalmazzon szabályt minden csomópontra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>Erre az IP-címre vagy a hálózatra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/útvonal/a/futtathatóhoz, .*/bináris/végrehajtható[0-9\.]+$, …</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Művelet</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>Erre a kikötÅ‘re</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>Ehhez a tartománylistához</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Megadhat egyetlen IP-címet: - 192.168.1.1 vagy reguláris kifejezés: - 192\.168\.1\.[0-9]+ több IP-cím: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Megadhat alhálózatot is: - 192.168.1.0/24 Megjegyzés: VesszÅ‘kkel vagy szóközökkel nem szabad elválasztani az IP-címeket vagy a hálózatokat.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> <source>LAN</source> <translation type="obsolete">Helyi hálózat</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation type="obsolete">127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation type="obsolete">192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation type="obsolete">192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation type="obsolete">192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation type="obsolete">192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation type="obsolete">169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation type="obsolete">172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation type="obsolete">10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation type="obsolete">::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation type="obsolete">fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation type="obsolete">ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation type="obsolete">fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation type="obsolete">fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Több kikötÅ‘ megadható reguláris kifejezések használatával:</p><p><br/></p><p>- 53, 80 vagy 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 vagy 5551, 5552, 5553, stb:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>egyszer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation type="obsolete">30 másodperc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation type="obsolete">5 perc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation type="obsolete">15 perc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation type="obsolete">30 perc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation type="obsolete">1 óra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>mindig</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>VesszÅ‘k vagy szóközök nem engedélyezhetnek több tartomány megadását. Használjon helyette reguláris kifejezéseket: .*(opensnitch|duckduckgo).com .*\.google.com vagy egyetlen tartomány: www.gnu.org - csak a www.gnu.org, nem az ftp.gnu.org, a www2.gnu.org, … gnu.org - csak a gnu.org-nak fog megfelelni, nem a www.gnu.org-nak, nem az ftp.gnu.org-nak, …</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.tartomány.hu, .*\.tartomány.hu</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>Erre az állomásra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>IdÅ‘tartam</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Csak TCP, UDP vagy UDPLITE engedélyezett</p><p>Használhatja a szabályos kifejezést, azaz: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>EbbÅ‘l a futtatható fájlból</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>EbbÅ‘l a parancssorból</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>EbbÅ‘l a felhasználói azonosítóból</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Válasszon egy címtárat a letiltáshoz vagy engedélyezéshez szükséges tartománylistákkal.</p><p>Helyezze be a címtár fájlokat bármilyen kiterjesztéssel, amely tartalmazza a tartományok listáit.</p><p><br/>A lista minden bejegyzésének formátuma a következÅ‘ (állomás formátum): </p><p>127.0.0.1 www.tartomány.hu</p><p>vagy </p><p>0.0.0.0 www.tartomány.hu</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Név</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>A szabályokat ábécé sorrendben ellenÅ‘rzik, így azok prioritása szerint ennek megfelelÅ‘en nevezheti meg Å‘ket. 000-helyi-állomás-engedélyezése 001-közvetítés-tagadása …</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> <source>leave blank to autocreate</source> <translation type="obsolete">hagyja üresen az önműködÅ‘ létrehozáshoz</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Ha be van jelölve, akkor ez a szabály elsÅ‘bbséget élvez a többi szabálygal szemben. Ez után más szabályokat nem fogunk ellenÅ‘rizni. A szabályt úgy kell megneveznie, hogy elÅ‘ször ellenÅ‘rizni fogják, mert betűrendben ellenÅ‘rzik. Például: [x] ElsÅ‘bbség - 000-elsÅ‘bbségi-szabály [ ] ElsÅ‘bbség - 001-kevésbé-elsÅ‘bbségi-szabály</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>ElsÅ‘bbségi szabály</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Alapértelmezés szerint a szabályok mezÅ‘je nem különbözteti meg a kis- és nagybetűket, azaz ha egy folyamat megpróbálja elérni a gOOgle.CoM tartományt, és van egy szabályod, amelyet meg kell tagadni a .*google.com tartomány, a kapcsolat letiltva lesz.<br/></p><p>Ha bejelöli ezt a jelölÅ‘négyzetet, meg kell adnia azt a pontos karakterláncot (tartomány, futtatható fájl, parancssor), amelyet szűrni szeretne.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>Kis- és nagybetűk felismerése</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>Alkalmazások</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation><html><head/><body><p>Ez a mezÅ‘ tartalmazza a felhasználó által végrehajtott parancssort, és megegyezik vele.<br/></p><p>Ha a felhasználó beírta a parancsot, csak a parancsot megjelenik:</p><p>telnet 1.2.3.4<br/></p><p>Ha a felhasználó beírta a parancs abszolút vagy relatív elérési útját, akkor ez fog megjelenni:</p><p> >/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>EbbÅ‘l a folyamatazonosítóból</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation>Hálózat</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation>Tartományok/IP-címek listája</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation>Ehhez a hálózati tartományok listájához</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation>Ehhez az IP-címlistához</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezendÅ‘ IP-címek listáját tartalmazó fájlokkal:</p><p>1.2.3.4.5</p><p>1.2.3.4. 6</p><p>.</p><p>stb.</p><p>Soronként egy IP-cím. Az üres sorokat vagy a # karakterrel kezdÅ‘dÅ‘ sorokat figyelmen kívül hagyja.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezni kívánt hálózati tartományok listáját tartalmazó fájlokkal:</p><p>1.2.3.0/24</p><p>80.34.56.0 /20</p><p>.</p><p>stb.<br/></p><p>Egy hálózati tartomány soronként. Az üres sorokat vagy a # karakterrel kezdÅ‘dÅ‘ sorokat figyelmen kívül hagyja.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezni kívánt tartományok listájával.</p><p>A könyvtárba helyezzen be olyan fájlokat, amelyek a tartomány listáit tartalmazzák.</p><p><br/>A lista minden egyes bejegyzésének formátuma a következÅ‘ (gazdaformátum):</p><p>127.0.0.1 www.tartomány.com</p><p>vagy </p><p>0.0.0.0 www.tartomány.com</p><p>Az üres sorokat vagy a # karakterrel kezdÅ‘dÅ‘ sorokat figyelmen kívül hagyja.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation>Ehhez a tartománylistához (szabályos kifejezések)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezendÅ‘ tartományok szabványos kifejezéseit tartalmazó fájlokkal:</p><p>.*\.example\.com</p><p> Használhat olyan tartományt is, amilyen: &quot;example.com&quot;, és egyezik a bármi.példa.com, bármi.példa.com.helyitartomány stb. oldallal.</p><p>Soronként egy tartomány. Az üres sorokat vagy a # karakterrel kezdÅ‘dÅ‘ sorokat figyelmen kívül hagyja.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Elutasítás</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation>Leírás…</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation><html><head/><body><p>E mezÅ‘ értéke mindig a végrehajtható fájl abszolút elérési útja: /útvonal/a/binárishoz<br/></p><p>Példák:</p><p>- Egyszerű: /útvonal/a/binárishoz</p><p>- Több elérési út: ^/usr/lib(64|)/firefox/firefox$</p><p>- Több bináris fájl: ^( /usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- A /tmp fájl végrehajtásának megtagadása/engedélyezése:</p><p>^/(var/|)tmp/.*$<br/></p><p>További példákért keresse fel a <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki oldalon</a>, vagy érdeklÅ‘djön a <a href="https://github.com/evilsocket/opensnitch/discussions">vitafórumokon</a>.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>Szabályos kifejezés</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation>szabályos kifejezés</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation>Hálózati felület</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation>Több</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation>Ne naplózza azokat a kapcsolatokat, amelyek megfelelnek ennek a szabálynak</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation>Ne naplózza a kapcsolatokat</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>Megtagadás csak elveti a kapcsolatot</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>Elutasítás megszakítja a kapcsolatot, és leállítja azt a szoftvercsatornát, amelyik kezdeményezte</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>Engedélyezés lehetÅ‘vé teszi a kapcsolatot</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> <source>Name (leave blank to autocreate)</source> <translation type="obsolete">Név (üres hagyása az automatikus létrehozáshoz)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation>ICMP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation>ICMP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation>SCTP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation>SCTP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> <source>Color</source> <translation type="obsolete">Szín</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch hálózati statisztika</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">Mentés CSV formátumban…</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Új szabály létrehozása</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">állomásnév - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Ãllapot</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Adatelérés indítása/leállítása</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Események</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>SzűrÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Például: Firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Az összes elfogott esemény törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Csomópontok</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán a Cím oszlopra a csomópont részleteinek megtekintéséhez)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Szabályok</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Szabály szerkesztése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Szabály törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán egy sorra a szabály részleteinek megtekintéséhez)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">szabálynév keresése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Alkalmazási szabályok</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>Ãllandó</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Ideiglenes</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Gazdagépek</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán az elem részleteinek megtekintéséhez)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Az összes elfogott gazdagép törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Alkalmazások</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Az összes elfogott alkalmazás törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Címek</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Az összes elfogott cím törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>KikötÅ‘k</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Az összes elfogott port törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Felhasználók</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Az összes elfogott felhasználó törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Kapcsolatok</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Elvetve</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Hasznos üzemidÅ‘</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Változat</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Kapcsolat törlése amelyek megfelelnek ennek a szabálynak</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Minden alkalmazás</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Elutasítás</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation>Rendszer szabályai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation>Csomópont törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation>Csomópont-beállítások megjelenítése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation>Csomópont adatelérés indítása/leállítása</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Statisztika</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Letiltás</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Súgó</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Bezárás</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation>Beállítás alkalmazva.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation>Hiba: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation>Módosítások alkalmazása…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation>Hiba történt a BEMENET láncszabályzat lekérésekor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation>Hiba történt a KIMENET láncszabályzat lekérésekor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation>A tűzfalszabályok grafikus felhasználói felületrÅ‘l történÅ‘ beállításához az „iptables†helyett az „nftablesâ€-t kell használni</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation>Tűzfal engedélyezése…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation>Tűzfal letiltása…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>CélkikötÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>ForráskikötÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>Cél IP-cím</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation>Forrás IP-cím</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation>Bemeneti felület</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation>Kimeneti felület</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation>conntrack-jelölés beállítása</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation>conntrack-jelölés egyezése</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation>conntrack-egyezés állapot(ok)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation>Jelölés beállítása a csomagon</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation>Csomagadatok egyeztetése</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation>Sávszélesség-kvóták</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation>Sebességkorlát csatlakozások</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation>A protobuf verziója nem kompatibilis, telepítenie kell a protobuf 3.8.0 vagy újabb verzióját (pip3 install --ignore-installed protobuf==3.8.0)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation>Szabály törölve</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation>Szabály hozzáadva</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation>A ',' vagy '-' karakterekkel több kikötÅ‘k/IP-címet vagy tartományt/értéket adhat meg:<br><br>kikötÅ‘k: 22 vagy 22,443 vagy 50000-60000<br>IP-címek: 192.168.1.1 vagy 192.168 .1.30-192.168.1.130<br>Értékek: echo-reply,echo-request<br>Értékek: new,established,related</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation>Szabály törlése, kérjük, várjon</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation>Hiba történt a szabály frissítésekor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation>Szabály hozzáadása, kérjük, várjon</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation><válasszon kijelentést></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation>EgyenlÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation>Nem egyenlÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation>EgyenlÅ‘ vagy nagyobb, mint</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation>Nagyobb, mint</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation>EgyenlÅ‘ vagy kisebb, mint</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation>Kisebb, mint</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation>Tűzfalszabály</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation>Egyszerű</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation>Haladó</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation>Ez a szabály még nem támogatott.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation>Szolgáltatás kizárása</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation>BejövÅ‘ kapcsolatok engedélyezése a kiválasztott kikötÅ‘höz.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation>KimenÅ‘ kapcsolatok engedélyezése a kiválasztott kikötÅ‘höz.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation>válasszon kijelentést.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation>az érték nem lehet 0 vagy üres.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation>az érték formátuma 1024/kbájt (vagy bájt, mbájt, gbájt)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation>az érték formátuma 1024/kbájt/másodperc (vagy bájt, mbájt, gbájt)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation>a sebességkorlát nem érvényes, használja: bájt, kbájt, mbájt vagy gbájt.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation>idÅ‘korlát nem érvényes, használja: másodperc, perc, óra vagy nap</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation>kikötÅ‘ nem érvényes.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation>Adat</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation>Hiba</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation>Figyelmeztetés</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>A rendszerértesítések nem érhetÅ‘k el, telepítenie kell a python3-notify2-t.</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>örökre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>KimenÅ‘ kapcsolat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Folyamat innen indult:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>ettÅ‘l a végrehajtható fájlból</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>errÅ‘l a parancssorról</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>kikötÅ‘ig: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>eddig: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>a(z) {0} felhasználótól</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>eddig: {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>eddig: *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">eddig: *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation>A(z) %s <b>távoli</b> folyamat fut a(z) <b>%s</b>-n</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>csatlakozik <b>%s</b>-hoz a %s-kikötÅ‘n %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>megpróbálja megoldani a(z) <b>%s</b> problémát a(z) %s segítségével, %s-kikötÅ‘ %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation>ebbÅ‘l a folyamatazonosítóból</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation>Új kimenÅ‘ kapcsolat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation>Elutasítás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation>csatlakozik a következÅ‘höz: <b>%s</b>, %s</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Beállítás mentése kivétele: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Figyelmeztetés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Válasszon egy fájlt az adatbázishoz<br>vagy válassza a „Memóriabeli†típust.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>DB típusa megváltozott</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>Indítsa újra a grafikus felhasználói felületet, hogy a hatások életbe léphessenek</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Beállítások alkalmazása: {0}…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>Kiszolgáló címe nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Hiba történt a(z) {0}-beállítás betöltésekor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>Beállítás alkalmazva.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Hiba a beállítás alkalmazásakor: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>A súgó megjelenítéséhez vigye az egeret a szövegek fölé<br><br>Emlékezzen meglátogatni a wikit: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation>Rendszer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>A témák nem állnak rendelkezésre. Telepítse a qt-material-t: pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation>A felhasználói felület témája megváltozott</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation>Indítsa újra a grafikus felhasználói felületet az új téma alkalmazásához</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation>Rendben</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> <source>Restart the GUI in order changes to take effect</source> <translation type="obsolete">Indítsa újra a grafikus felhasználói felületet, hogy a változtatások életbe lépjenek</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation>Nincsenek csomópontok összekapcsolva</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation>Kivétel menti a(z) {0} csomópont beállítását: {1}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Hiba a folyamatadatok betöltésekor:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Hiba a megfigyelési folyamat leállításakor:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>betöltés folyamatban…</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>Nincsenek csomópontok csatlakoztatva.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>A szabály alkalmazva.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Hiba a szabály alkalmazásakor: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Hiba történt a szabály betöltésekor</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>a protokoll nem lehet üres, és nem szüntetheti meg a kijelölést</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Protokoll szabályos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>folyamat útvonal nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Folyamat útvonala szabályos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>parancssor nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Parancssor szabályos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>CélkikötÅ‘ nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>CélkikötÅ‘ szabványos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>Célállomás nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Célállomás szabályos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>Cél IP-cím/hálózat nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Cél IP-cím szabályos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>Felhasználói azonosító nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Felhasználói azonosító szabályos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>ListamezÅ‘ nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>ListmezÅ‘k könyvtárnak kell lennie</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>A szabály nem támogatott</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation>Már van egy szabály ezzel a névvel.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation>Folyamatazonosító mezÅ‘ nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation>Folyamatazonosító mezÅ‘ szabványos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation>Jelöljön ki legalább egy mezÅ‘t.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation>Hálózati felület nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation>Hálózati felület szabályos kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Név</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Cím</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">Ãllapot</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> <source>Hostname</source> <translation type="obsolete">Ãllomásnév</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">Változat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Szabályok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">IdÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Művelet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">IdÅ‘tartam</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Csomópont</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Engedélyezve</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>Találatok száma</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protokoll</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Nem fut</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Letiltva</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Futtatás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>{0} OpenSnitch hálózati statisztika</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch hálózati statisztikák a következÅ‘höz: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Hiba:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Figyelmeztetés:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Mindig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Újraindításig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Letiltás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Másolás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Szerkesztés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Törlés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> A szabály törlésére készül. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Biztos benne?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>A szabály nem található ezen a néven és csomóponton</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> A szabály törlésére készül. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Mentés CSV-fájlként</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Szabály</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Név</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Név</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Cím</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Ãllapot</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Ãllomásnév</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Változat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Szabályok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">IdÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Művelet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">IdÅ‘tartam</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Csomópont</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Engedélyezve</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Találatok száma</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protokoll</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Szabály</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Név</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Cím</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ãllapot</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ãllomásnév</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Változat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Szabályok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>IdÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Művelet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>IdÅ‘tartam</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Csomópont</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Engedélyezve</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Találatok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Folyamat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Cél</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Szabály</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Felhasználóazonosító</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>LegutóbbiCsatlakozás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Argumentumok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>CélIPcíme</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Célállomásneve</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>CélkikötÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> <source>LastConnection</source> <translation type="obsolete">LegutóbbiCsatlakozás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> <source>Uptime</source> <translation type="obsolete">Hasznos üzemidÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> <source>Connections</source> <translation type="obsolete">Kapcsolatok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> <source>Dropped</source> <translation type="obsolete">Elvetve</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Mi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation>Alkalmazás a következÅ‘re</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation>Elutasítás</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>Hálózatnév</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hasznos üzemidÅ‘</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kapcsolatok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Elvetve</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Mi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Sorrend</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation>Új csomópont csatlakoztatva</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Leírás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Parancssor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation>Exportszabályok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation>Importszabályok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation>Események exportálása CSV-fájlként</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation>Kilépés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation>Exportálás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation>A vágólapra</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation>Lemezre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation>Exportszabályok könyvtár kiválasztása</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation> A bejegyzés törlésére készül. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation> A csomópont törlésre készül. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation><b>Hiba történt a csomópont törlésekor</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation>Hiba történt a szabályok exportálásakor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation>Importszabályok (JSON-fájlok) könyvtár kiválasztása</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation>Szabályok sikeresen importálva</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished">FIGYELMEZTETÉS</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> </TS> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/ja_JP/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017470�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/ja_JP/opensnitch-ja_JP.ts������������������������������������������0000664�0000000�0000000�00000427140�15003540030�0023203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ja_JP"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation type="unfinished"><html><head/><body><p><span style=" font-weight:600;">実行元</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation type="unfinished">é€ä¿¡å…ƒã®IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation type="unfinished">プロセスID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation type="unfinished">宛先IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation type="unfinished">宛先ãƒãƒ¼ãƒˆ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation type="unfinished">ã“ã®å®Ÿè¡Œãƒ•ァイルを</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation type="unfinished">ã“ã®ã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³ã‚’</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation type="unfinished">ã“ã®å®›å…ˆãƒãƒ¼ãƒˆã«å¯¾ã—ã¦</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation type="unfinished">ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation type="unfinished">ã“ã®å®›å…ˆIPã«å¯¾ã—ã¦</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation type="unfinished">一度ã®ã¿</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation type="unfinished">30ç§’é–“</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation type="unfinished">5分間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation type="unfinished">15分間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation type="unfinished">30分間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation type="unfinished">1時間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation type="unfinished">å†èµ·å‹•ã™ã‚‹ã¾ã§</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation type="unfinished">永久ã«</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation type="unfinished">æ‹’å¦ã™ã‚‹</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation type="unfinished">許å¯ã™ã‚‹</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation type="unfinished">アクション</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation type="unfinished">設定</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation type="unfinished">UI</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation type="unfinished">ダイアログã®è¡¨ç¤ºæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation type="unfinished">ãƒãƒƒãƒ—アップ時ã«é¸æŠžã•れるè¦å®šã®ãƒ«ãƒ¼ãƒ«ã®æœ‰åŠ¹æœŸé–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation type="unfinished">既定ã®ãƒ«ãƒ¼ãƒ«æœ‰åŠ¹æœŸé–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">ãƒãƒƒãƒ—アップ時ã«é¸æŠžã•れるè¦å®šã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">既定ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation type="unfinished">既定ã®ã‚¿ãƒ¼ã‚²ãƒƒãƒˆ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation type="unfinished">中央</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation type="unfinished">å³ä¸Šéƒ¨</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation type="unfinished">å³ä¸‹éƒ¨</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation type="unfinished">左上部</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation type="unfinished">左下部</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">既定ã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚°è¡¨ç¤ºä½ç½®</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation type="unfinished">実行ファイル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation type="unfinished">コマンドライン</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation type="unfinished">宛先ãƒãƒ¼ãƒˆ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation type="unfinished">宛先IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation type="unfinished">一度ã®ã¿</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation type="unfinished">30ç§’é–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation type="unfinished">5分間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation type="unfinished">15分間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation type="unfinished">30分間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation type="unfinished">1時間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation type="unfinished">å†èµ·å‹•ã™ã‚‹ã¾ã§</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation type="unfinished">永久ã«</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation type="unfinished">æ‹’å¦</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation type="unfinished">許å¯</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">ãƒãƒƒãƒ—アップを無効ã«ã—ã¦é€šçŸ¥ã®ã¿è¡¨ç¤º</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation type="unfinished">プロセス監視方å¼</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation type="unfinished"><html><head/><body><p>è¦å®šã®ãƒ«ãƒ¼ãƒ«æœ‰åŠ¹æœŸé–“ã¯ã€UIãŒæŽ¥ç¶šã•れã¦ã„ãªã„ã¨ãã«ä½¿ç”¨ã•れã¾ã™ã€‚</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation type="unfinished"><html><head/><body><p>ノードã®ã‚¢ãƒ‰ãƒ¬ã‚¹</p><p>標準: unix:///tmp/osui.sock (Unixソケットã®å ´åˆã¯unix://ãŒå¿…é ˆ)</p><p>ã“ã®ã‚ˆã†ã«IPアドレスã¨ãƒãƒ¼ãƒˆã‚’指定ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation type="unfinished">アドレス</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation type="unfinished">既定ã®ãƒ­ã‚°ãƒ¬ãƒ™ãƒ«</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation type="unfinished">ãƒãƒ¼ã‚¸ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation type="unfinished"><html><head/><body><p>è¦å®šã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯ã€UIãŒæŽ¥ç¶šã•れã¦ã„ãªã„ã¨ãã«ä½¿ç”¨ã•れã¾ã™ã€‚</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="671"/> <source>proc</source> <translation type="obsolete">proc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>audit</source> <translation type="obsolete">audit</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="686"/> <source>ftrace</source> <translation type="obsolete">ftrace</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation type="unfinished"><html><head/><body><p>ファイルã«ãƒ­ã‚°ã‚’記録ã—ã¾ã™<br/></p><p>/dev/stdoutã«ã™ã‚‹ã¨æ¨™æº–出力ã«ãƒ­ã‚°ã‚’出力ã—ã¾ã™</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation type="unfinished">ログファイル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="778"/> <source>DEBUG</source> <translation type="obsolete">DEBUG</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="783"/> <source>INFO</source> <translation type="obsolete">INFO</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="788"/> <source>IMPORTANT</source> <translation type="obsolete">IMPORTANT</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="793"/> <source>WARNING</source> <translation type="obsolete">WARNING</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="798"/> <source>ERROR</source> <translation type="obsolete">ERROR</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="803"/> <source>FATAL</source> <translation type="obsolete">FATAL</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>有効ã«ã—ãŸå ´åˆã€opensnitchã¯ã€é–¢é€£ã—ãŸPIDã‚’æŒãŸãªã„接続を許å¯ã™ã‚‹ã‹æ‹’å¦ã™ã‚‹ã‹ã‚’確èªã—ã¾ã™ã€‚</p><p>ãƒãƒƒãƒ—アップダイアログã«ã¯ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æŽ¥ç¶šã«é–¢ã™ã‚‹æƒ…å ±ã®ã¿ãŒè¡¨ç¤ºã•れã¾ã™ã€‚</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">䏿˜Žãªãƒ—ロセスを検証</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation type="unfinished">ホストå</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation type="unfinished">unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation type="unfinished">å†èµ·å‹•ã™ã‚‹ã¾ã§</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation type="unfinished">常ã«</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation type="unfinished">/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation type="unfinished">/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation type="unfinished">å…¨ã¦ã®ãƒŽãƒ¼ãƒ‰ã«è¨­å®šã‚’åæ˜ </translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation type="unfinished">データベース</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation type="unfinished">データベース方å¼</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation type="unfinished">å‚ç…§</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation type="unfinished">メモリ内</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation type="unfinished">ファイル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation type="unfinished">é–‰ã˜ã‚‹</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation type="unfinished">é©ç”¨</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation type="unfinished">ä¿å­˜</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">ãƒãƒƒãƒ—アップã®è¦å®šã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">è¦å®šã®ãƒãƒƒãƒ—アップ表示ä½ç½®</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation type="unfinished">詳細表示ã§ã¯ã€æŽ¥ç¶šã‚’フィルタリングã™ã‚‹ãŸã‚ã«è¤‡æ•°ã®ãƒ•ィールドを簡å˜ã«é¸æŠžã§ãã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation type="unfinished">標準ã§è©³ç´°è¡¨ç¤ºã‚’有効ã«ã™ã‚‹</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation type="unfinished">アクション</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation type="unfinished"><html><head/><body><p>有効ã«ã™ã‚‹ã¨ã€è©³ç´°è¡¨ç¤ºãŒã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªçŠ¶æ…‹ã§ãƒãƒƒãƒ—アップãŒè¡¨ç¤ºã•れã¾ã™ã€‚</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation type="unfinished">ãƒ«ãƒ¼ãƒ«ã®æœ‰åŠ¹æœŸé–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation type="unfinished"><html><head/><body><p>ãƒãƒƒãƒ—アップã®è¡¨ç¤ºæ™‚ã€æ¨™æº–ã§ã¯æŽ¥ç¶šã®1ã¤ã®ãƒ—ロパティ (実行å¯èƒ½ãƒ•ァイルã€ãƒãƒ¼ãƒˆã€IP ãªã©) ã«ã‚ˆã£ã¦æŽ¥ç¶šã¾ãŸã¯ã‚¢ãƒ—リケーションをフィルタリングã§ãã¾ã™ã€‚</p><p>ã“れらã®ã‚ªãƒ—ションを使用ã™ã‚‹ã¨ã€æŽ¥ç¶šã‚’フィルタリングã™ã‚‹éš›ã€è¤‡æ•°ã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã‚’é¸æŠžã§ãã¾ã™ã€‚</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>次を使用ã—ã¦é€šä¿¡ã‚’フィルタ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation type="unfinished">有効ã«ã™ã‚‹ã¨ã€ãƒãƒƒãƒ—アップãŒè¡¨ç¤ºã•れãŸã¨ãã«ã€ã“ã®ãƒ•ィールドãŒé¸æŠžã•れã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation type="unfinished">宛先ãƒãƒ¼ãƒˆ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation type="unfinished">宛先IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation type="unfinished"><html><head/><body><p>ã“ã®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã¯ã€ãƒãƒƒãƒ—アップダイアログã®è¡¨ç¤ºæ™‚é–“ã®ã‚«ã‚¦ãƒ³ãƒˆãƒ€ã‚¦ãƒ³ã§ã™ã€‚</p><p>ãƒãƒƒãƒ—アップã«å›žç­”ã—ãªã„å ´åˆã€ã“ã®ã‚ªãƒ—ションãŒé©ç”¨ã•れã¾ã™ã€‚</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="676"/> <source>ebpf</source> <translation type="obsolete">ebpf</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation type="unfinished"><html><head/><body><p>ãƒãƒƒãƒ—アップã®è¦å®šã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation type="unfinished">GUI未接続時ã®è¦å®šã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation type="unfinished">ç„¡åŠ¹ãªæŽ¥ç¶šã‚’ãƒ‡ãƒãƒƒã‚°</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation type="unfinished">ãƒãƒƒãƒ—アップ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation type="unfinished">è¦å®šã®ã‚ªãƒ—ション</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation type="unfinished">è¦å®šã®è¡¨ç¤ºä½ç½®</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation type="unfinished">å…¨ã¦ã®ä¸€æ™‚ルール</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="490"/> <source>Don't save rules of duration</source> <translation type="obsolete">ãƒ«ãƒ¼ãƒ«ã®æœ‰åŠ¹æœŸé–“ã‚’ä¿æŒã—ãªã„</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation type="unfinished">時間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation type="unfinished">宛先</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation type="unfinished">プロトコル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation type="unfinished">プロセス</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation type="unfinished">イベントタブã®é …ç›®</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation type="unfinished">プロセス情報</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation type="unfinished">読ã¿è¾¼ã¿ä¸­...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation type="unfinished">CWD:-読ã¿è¾¼ã¿ä¸­...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation type="unfinished">メモリ状態: 読ã¿è¾¼ã¿ä¸­...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation type="unfinished">状態</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation type="unfinished">ファイルアクセス</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation type="unfinished">入出力ã®çµ±è¨ˆ</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation type="unfinished">メモリ内データ</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation type="unfinished">スタック</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation type="unfinished">環境変数</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation type="unfinished">プロセスID</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation type="unfinished">プロセスã®ç›£è¦–ã‚’é–‹å§‹/åœæ­¢</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation type="unfinished">é–‰ã˜ã‚‹</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation type="unfinished">å…¨ã¦ã®ãƒŽãƒ¼ãƒ‰ã«ãƒ«ãƒ¼ãƒ«ã‚’åæ˜ </translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation type="unfinished">IP/ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation type="unfinished">アクション</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation type="unfinished">ãƒãƒ¼ãƒˆ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation type="unfinished">ドメインリスト</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> <source>LAN</source> <translation type="obsolete">LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation type="unfinished">一度ã®ã¿</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation type="obsolete">30ç§’é–“</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation type="obsolete">5分間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation type="obsolete">15分間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation type="obsolete">30分間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation type="obsolete">1時間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation type="unfinished">å†èµ·å‹•ã™ã‚‹ã¾ã§</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation type="unfinished">常ã«</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation type="unfinished">ホスト</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation type="unfinished">有効期間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation type="unfinished">TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation type="unfinished">プロトコル</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation type="unfinished">実行ファイル</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation type="unfinished">æ‹’å¦</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation type="unfinished">許å¯</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation type="unfinished">コマンドライン</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">åå‰</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation type="unfinished">有効</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> <source>leave blank to autocreate</source> <translation type="obsolete">空ã«ã™ã‚‹ã¨è‡ªå‹•生æˆã•れã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation type="unfinished">優先ルール</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation type="unfinished">大文字/å°æ–‡å­—を区別</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation type="unfinished">OpenSnitchãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ¢ãƒ‹ã‚¿ãƒ¼</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">CSVファイルã«ä¿å­˜</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation type="unfinished">ルールを新è¦ä½œæˆ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation type="unfinished"><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">ホストå - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation type="unfinished">状態</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation type="unfinished">-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation type="unfinished">サービスを開始/åœæ­¢</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation type="unfinished">イベント</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation type="unfinished">絞り込ã¿</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation type="unfinished">許å¯ä¸­</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation type="unfinished">æ‹’å¦ä¸­</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation type="unfinished">例:firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation type="unfinished">50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation type="unfinished">100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation type="unfinished">200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation type="unfinished">300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation type="unfinished">記録ã—ãŸå…¨ã¦ã®ã‚¤ãƒ™ãƒ³ãƒˆå±¥æ­´ã‚’消去</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックã§ãƒŽãƒ¼ãƒ‰ã®è©³ç´°ã‚’確èªã§ãã¾ã™)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation type="unfinished">有効</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation type="unfinished">ルールを編集</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation type="unfinished">ルールを削除</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="674"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックã§ãƒ«ãƒ¼ãƒ«ã®è©³ç´°ã‚’確èªã§ãã¾ã™)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">ルールåを検索</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation type="unfinished">アプリケーションã®ãƒ«ãƒ¼ãƒ«</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation type="unfinished">永久ルール</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation type="unfinished">一時ルール</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation type="unfinished">ホスト</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックã§è©³ç´°ã‚’確èªã§ãã¾ã™)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">記録ã—ãŸå…¨ã¦ã®ãƒ›ã‚¹ãƒˆã‚’消去</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation type="unfinished">アプリケーション</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">記録ã—ãŸå…¨ã¦ã®ã‚¢ãƒ—リケーション履歴を消去</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation type="unfinished">アドレス</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">記録ã—ãŸå…¨ã¦ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’消去</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation type="unfinished">ãƒãƒ¼ãƒˆ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">記録ã—ãŸå…¨ã¦ã®ãƒãƒ¼ãƒˆã‚’消去</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation type="unfinished">ユーザー</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">記録ã—ãŸå…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å±¥æ­´ã‚’消去</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation type="unfinished">通éŽ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation type="unfinished">ブロック</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation type="unfinished">実行時間</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation type="unfinished">ãƒãƒ¼ã‚¸ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックã™ã‚‹ã¨ãƒ«ãƒ¼ãƒ«ã®è©³ç´°ãŒç¢ºèªã§ãã¾ã™)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">ã“ã®ãƒ«ãƒ¼ãƒ«ã«ãƒžãƒƒãƒã—ãŸæŽ¥ç¶šã‚’å‰Šé™¤ã—ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation type="unfinished">å…¨ã¦ã®ã‚¢ãƒ—リケーション</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation type="unfinished">ダッシュボードを開ã</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation type="unfinished">ヘルプ</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation type="unfinished">終了</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation type="unfinished">有効化</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation type="unfinished">無効化</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation type="unfinished"></translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation type="unfinished">å†èµ·å‹•ã™ã‚‹ã¾ã§</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation type="unfinished">永久ã«</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation type="unfinished">許å¯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation type="unfinished">æ‹’å¦</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation type="unfinished">外部ã¸ã®æŽ¥ç¶š</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation type="unfinished">プロセスã®å®Ÿè¡Œå…ƒ:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation type="unfinished">次ã®å®Ÿè¡Œãƒ•ァイルを</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation type="unfinished">次ã®ã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³ã‚’</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation type="unfinished"><b>リモート</b>プロセス %s 㯠<b>%s</b> ã§å®Ÿè¡Œä¸­ã§ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation type="unfinished">㯠<b>%s</b> ã® %s ãƒãƒ¼ãƒˆ %d ç•ªã«æŽ¥ç¶šã—ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation type="unfinished">ã¯<b>%s</b> ã‚’ %sã® %s ãƒãƒ¼ãƒˆ %dã§è§£æ±ºã—よã†ã¨ã—ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation type="unfinished">警告</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation type="unfinished">データベースã®ä¿å­˜ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã™ã‚‹ã‹ã€æ–¹å¼ã€Œãƒ¡ãƒ¢ãƒªå†…ã€ã‚’é¸æŠžã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation type="unfinished">データベース方å¼ãŒå¤‰æ›´ã•れã¾ã—ãŸ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation type="unfinished">GUIã‚’å†èµ·å‹•å¾Œåæ˜ ã•れã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation type="unfinished">サーãƒãƒ¼ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯ç©ºç™½ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation type="unfinished">æ§‹æˆã¯å映ã•れã¾ã—ãŸã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation type="unfinished"><b>プロセス情報ã®èª­ã¿è¾¼ã¿ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation type="unfinished"><b>プロセス監視ã®åœæ­¢ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation type="unfinished">読ã¿è¾¼ã¿ä¸­...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation type="unfinished">接続ã—ã¦ã„るノードãŒã‚りã¾ã›ã‚“。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation type="unfinished">ルールãŒå映ã•れã¾ã—ãŸã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation type="unfinished">ルールã®å映ã«å¤±æ•—ã—ã¾ã—ãŸ: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation type="unfinished">プロトコルを指定ã™ã‚‹ã‹ãƒã‚§ãƒƒã‚¯ã‚’外ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation type="unfinished">ãƒ—ãƒ­ãƒˆã‚³ãƒ«ã®æ­£è¦è¡¨ç¾è¨˜æ³•ãŒèª¤ã£ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation type="unfinished">プロセスã®ãƒ‘スを指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation type="unfinished">ãƒ—ãƒ­ã‚»ã‚¹ãƒ‘ã‚¹ã®æ­£è¦è¡¨ç¾è¨˜æ³•ãŒèª¤ã£ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation type="unfinished">コマンドラインを指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation type="unfinished">ã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³ã®æ­£è¦è¡¨ç¾è¨˜æ³•ãŒèª¤ã£ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation type="unfinished">宛先ãƒãƒ¼ãƒˆã‚’指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation type="unfinished">宛先ãƒãƒ¼ãƒˆã®æ­£è¦è¡¨ç¾è¨˜æ³•ãŒèª¤ã£ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation type="unfinished">宛先ホストを指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation type="unfinished">å®›å…ˆãƒ›ã‚¹ãƒˆã®æ­£è¦è¡¨ç¾è¨˜æ³•ãŒèª¤ã£ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation type="unfinished">宛先IP/ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation type="unfinished">宛先IPã®æ­£è¦è¡¨ç¾è¨˜æ³•ãŒèª¤ã£ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation type="unfinished">ユーザーIDを指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation type="unfinished">ユーザーIDã®æ­£è¦è¡¨ç¾è¨˜æ³•ãŒèª¤ã£ã¦ã„ã¾ã™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation type="unfinished">リスト項目を指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation type="unfinished">リスト項目ã«ã¯å¿…ãšãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’指定ã—ã¦ãã ã•ã„</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation type="unfinished"><b>ルールをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation type="unfinished"><b>ルールã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">åå‰</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">アドレス</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">状態</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> <source>Hostname</source> <translation type="obsolete">ホストå</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">ãƒãƒ¼ã‚¸ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">時間</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">アクション</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">有効期間</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">ノード</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">有効</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation type="unfinished">回数</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">プロトコル</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation type="unfinished">åœæ­¢ä¸­</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation type="unfinished">無効</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation type="unfinished">実行中</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation type="unfinished">OpenSnitch ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ¢ãƒ‹ã‚¿ãƒ¼ {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation type="unfinished">無効化</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation type="unfinished">有効化</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation type="unfinished">複製</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation type="unfinished">編集</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation type="unfinished">削除</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation type="unfinished"> ã“ã®ãƒ«ãƒ¼ãƒ«ã‚’消去ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚ </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation type="unfinished"> 宜ã—ã„ã§ã™ã‹ï¼Ÿ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation type="unfinished">該当ã™ã‚‹ãƒ«ãƒ¼ãƒ«ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation type="unfinished">CSVファイルã«ä¿å­˜</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"><b>エラーr:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation type="unfinished">警告:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation type="unfinished">許å¯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation type="unfinished">æ‹’å¦</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation type="unfinished">常ã«</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation type="unfinished">å†èµ·å‹•ã™ã‚‹ã¾ã§</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation type="unfinished"> ã“ã®ãƒ«ãƒ¼ãƒ«ã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚ </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">åå‰</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">åå‰</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">アドレス</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">状態</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ホストå</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ãƒãƒ¼ã‚¸ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ルール</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">時間</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">アクション</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">有効期間</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ノード</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">有効</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">回数</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">プロトコル</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">åå‰</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">アドレス</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">状態</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">ホストå</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">ãƒãƒ¼ã‚¸ãƒ§ãƒ³</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">時間</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">アクション</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">有効期間</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">有効</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">回数</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">プロトコル</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">プロセス</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">宛先</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">最終接続</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">引数</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">宛先IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">宛先ホスト</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">宛先ãƒãƒ¼ãƒˆ</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished">WARNING</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> </TS> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/lt_LT/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017523�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/lt_LT/opensnitch-lt_LT.ts������������������������������������������0000664�0000000�0000000�00000447511�15003540030�0023275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="lt"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>Vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Ä®vykdyta iÅ¡</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>TekstoEtiketÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Å altinio IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>Proceso ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>Paskirties IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Paskirties prievadas</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>iÅ¡ Å¡io vykdomojo failo</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>iÅ¡ Å¡ios komandinÄ—s eilutÄ—s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>Å¡is paskirties prievadas</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>Å¡is vartotojas</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>Å¡is paskirties ip</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>kartÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30 sek.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1 val.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>amžinai</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Drausti</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished">Ä®jungti</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation type="unfinished">IÅ¡rinti</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished">IÅ¡saugoti</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Nuostatos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Vartotojo sÄ…saja</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Numatytasis laiko limitas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>IššokanÄiojo lango numatytoji trukmÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Numatytoji trukmÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Acción por defecto de la ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Acción por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Numatytasis tikslas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>centre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>virÅ¡uje deÅ¡inÄ—je</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>apaÄioje deÅ¡inÄ—je</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>virÅ¡uje kairÄ—je</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>apaÄioje kairÄ—je</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posición por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>pagal vykdomÄ…jį failÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>pagal komandinÄ™ eilutÄ™</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>pagal paskirties prievadÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>pagal paskirties ip</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>pagal vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>kartÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30 sek.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1 val.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>amžinai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>drausti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>leisti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">IÅ¡jungti iššokanÄius langus, rodyti tik įspÄ—jimÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Mazgai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Proceso stebÄ—jimo metodas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Numatytoji trukmÄ— bus taikoma, kai nÄ—ra prijungtos vartotojo sÄ…sajos.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Mazgo adresas </p><p>Numatytoji reikÅ¡mÄ—: unix:///tmp/osui.sock (unix:// privaloma, jei tai Unix lizdas) </p><p>Tai taip pat gali bÅ«ti IP adresas su prievadu: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Adresas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Numatytasis registravimo žurnale lygis</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Versija</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Numatytasis veiksmas bus atliekamas, kai nÄ—ra prijungtos vartotojo sÄ…sajos.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Žurnalo failas, į kurį įraÅ¡omi žurnalo įraÅ¡ai.<br/></p><p>/dev/stdout spausdins žurnalus į standartinÄ™ iÅ¡vestį.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Žurnalo failas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. La ventana emergente sólo contendrá información relativa a la conexión. Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexiones desconocidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>Kompiuterio vardas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>visada</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Taikyti konfigÅ«racijÄ… visiems mazgams</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Duomenų bazÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>Atmintyje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Failas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Uždaryti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Taikyti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>IÅ¡saugoti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Duomenų bazÄ—s tipas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Pasirinkti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posición en pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="102"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Rodyti iÅ¡plÄ—stinį rodinį automatiÅ¡kai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Veiksmas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Jei pažymÄ—ta, iššokantys langai bus rodomi su aktyviu iÅ¡plÄ—stiniu rodiniu.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>TrukmÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Taip pat filtruoti prisijungimus pagal:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="362"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>Vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Paskirties prievadas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>Paskirties IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Å is laiko limitas yra atgalinis laikmatis, kurį matote, kai rodomas iššokantis dialogo langas.</p><p>Jei į iššokantį dialogo langÄ… neatsakoma, taikomos numatytosios parinktys.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>IÅ¡plÄ—stinis vaizdas leidžia lengvai pasirinkti kelis laukus, kad bÅ«tų galima filtruoti prisijungimus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Jei pažymÄ—ta, Å¡is laukas bus pasirinktas, kai bus rodomas iššokantis langas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>IššokanÄio lango numatytasis veiksmas.</p><p>Kai ruoÅ¡iamasi užmegzti naujÄ… iÅ¡einantį ryšį, Å¡is veiksmas bus pasirinktas pagal numatytuosius nustatymus, todÄ—l, jei suveiks laiko limitas, bus taikoma Å¡i parinktis.</p><p><br/></p><p>Kai iššokanÄiame lange praÅ¡oma leisti arba neleisti prisijungti: </p><p>1. Nauji iÅ¡einantys ryÅ¡iai neleidžiami.</p><p>2. Žinomi ryÅ¡iai leidžiami arba neleidžiami pagal vartotojo nustatytas taisykles.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Default action when the GUI is disconnected</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Debug invalid connections</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Iššokantys langai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Numatytosios parinktys</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Numatytoji padÄ—tis ekrane</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>bet kokios laikinos taisyklÄ—s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="478"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="481"/> <source>Don't save rules of duration</source> <translation type="obsolete">Don't save rules of duration</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source>Show events columns</source> <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Laikas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Paskirties vieta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protokolas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Procesas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>TaisyklÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Mazgas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Events tab columns</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Proceso informacija</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>įkeliama…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: loading...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>mem stats: loading...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>BÅ«sena</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Atidaryti failus</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>I/O Statistics</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Memory mapped files</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Stack</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Aplinkos kintamieji</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Application pids</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>PradÄ—ti arba sustabdyti Å¡io proceso stebÄ—jimÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Uždaryti</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>TaisyklÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Mazgas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Taikyti taisyklÄ™ visiems mazgams</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>IÅ¡ Å¡ios komandinÄ—s eilutÄ—s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>IÅ¡ Å¡io vykdomojo failo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Veiksmas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>Ä® šį IP / tinklÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>kartÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/> <source>30s</source> <translation type="obsolete">30 sek.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> <source>5m</source> <translation type="obsolete">5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> <source>15m</source> <translation type="obsolete">15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> <source>30m</source> <translation type="obsolete">30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> <source>1h</source> <translation type="obsolete">1 val.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>visada</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>Ä® šį prievadÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>IÅ¡ Å¡io vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>Kelių domenų nurodyti kableliais ar tarpais neleidžiama. Vietoj jų naudokite reguliariÄ…sias iÅ¡raiÅ¡kas: .*(opensnitch|duckduckgo).com .*\.google.com arba vienÄ… domenÄ…: www.gnu.org - atitiks tik www.gnu.org, nei ftp.gnu.org, nei www2.gnu.org, ... gnu.org - atitiks tik gnu.org, www.gnu.org, ftp.gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domenas.org, .*\.domenas.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Leidžiama naudoti tik TCP, UDP arba UDPLITE</p><p>Galite naudoti regexp, t. y.: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Galite nurodyti vienÄ… IP adresÄ…: - 192.168.1.1 arba reguliariÄ… iÅ¡raiÅ¡kÄ…: - 192\.168\.1\.[0-9]+ kelių IP adresų: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Taip pat galite nurodyti potinklį: - 192.168.1.0/24 Pastaba: kableliais ar tarpais atskirti IP ar tinklų negalima.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>LAN</source> <translation type="obsolete">LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> <source>127.0.0.0/8</source> <translation type="obsolete">127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>192.168.0.0/24</source> <translation type="obsolete">192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> <source>192.168.1.0/24</source> <translation type="obsolete">192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> <source>192.168.2.0/24</source> <translation type="obsolete">192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> <source>192.168.0.0/16</source> <translation type="obsolete">192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> <source>169.254.0.0/16</source> <translation type="obsolete">169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> <source>172.16.0.0/12</source> <translation type="obsolete">172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> <source>10.0.0.0/8</source> <translation type="obsolete">10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> <source>::1/128</source> <translation type="obsolete">::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> <source>fc00::/7</source> <translation type="obsolete">fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> <source>ff00::/8</source> <translation type="obsolete">ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>fe80::/10</source> <translation type="obsolete">fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>fd00::/8</source> <translation type="obsolete">fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>TrukmÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protokolas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>To this host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Drausti</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Pavadinimas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Ä®jungti</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>TaisyklÄ—s tikrinamos abÄ—cÄ—lÄ—s tvarka, todÄ—l galite jas atitinkamai pavadinÄ™ nustatyti jų prioritetus. 000-allow-localhost 001-deny-broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/> <source>leave blank to autocreate</source> <translation type="obsolete">palikite tuÅ¡ÄiÄ…, kad sukurtumÄ—te automatiÅ¡kai</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Jei pažymÄ—ta, Å¡i taisyklÄ— bus virÅ¡esnÄ— už kitas taisykles. Po Å¡ios taisyklÄ—s nebus tikrinamos jokios kitos taisyklÄ—s. TaisyklÄ™ turite pavadinti taip, kad ji bÅ«tų tikrinama pirma, nes taisyklÄ—s tikrinamos abÄ—cÄ—lÄ—s tvarka. Pavyzdžiui: [x] Prioritetas - 000-prioritetinÄ—-taisyklÄ— [ ] Prioritetas - 001-mažesnio prioriteto taisyklÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>PrioritetinÄ— taisyklÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Pagal numatytuosius nustatymus taisyklių lauke neribojamos didžiosios raidÄ—s, t. y., jei procesas bando prisijungti prie gOOgle.CoM, o jÅ«s turite taisyklÄ™ Deny .*google.com, prisijungimas bus užblokuotas.<br/></p><p>Jei pažymÄ—site šį laukelį, turÄ—site nurodyti tikslų (domenÄ…, vykdomÄ…jį failÄ…, komandinÄ™ eilutÄ™), kuriÄ… norite filtruoti.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>Case-sensitive</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>Ä® šį domenų sÄ…rašą</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Pasirinkite katalogÄ… su blokuojamų arba leidžiamų domenų sÄ…raÅ¡ais.</p><p>Ä® tÄ… katalogÄ… įdÄ—kite failus su bet kokiu plÄ—tiniu, kuriuose yra domenų sÄ…raÅ¡ai.</p><p><br/>Kiekvieno sÄ…raÅ¡o įraÅ¡o formatas yra toks (pagrindinio kompiuterio formatas): </p><p>127.0.0.0.1 www.domain.com</p><p>arba </p><p>0.0.0.0.0 www.domenas.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation type="unfinished">Programos</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch tinklo statistika</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="284"/> <source>Save to CSV</source> <translation type="obsolete">Ä®raÅ¡yti į CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="294"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Sukurti naujÄ… taisyklÄ™</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">pagrindinio kompiuterio vardas - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Statusas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>PradÄ—ti arba sustabdyti perÄ—mimÄ…</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Ä®vykiai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filtras</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation type="unfinished">Atmesti</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Pvz.: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Mazgai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>TaisyklÄ—s</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>įjungti</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="684"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Application rules</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>NuolatinÄ—</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Laikina</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(dukart spustelÄ—kite, kad peržiÅ«rÄ—tumÄ—te detaliÄ… informacijÄ… apie elementÄ…)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Programos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Adresai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Prievadai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Vartotojai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Connections</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Dropped</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Veikimo laikas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Versija</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation type="unfinished">IÅ¡trinti visus perimtus įvykius</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Redaguoti taisyklÄ™</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>IÅ¡trinti taisyklÄ™</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(dukart spustelÄ—kite, kad peržiÅ«rÄ—tumÄ—te detaliÄ… informacijÄ… apie taisyklÄ™)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Visos programos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Statistika</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Pagalba</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Uždaryti</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Ä®jungti</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>IÅ¡jungti</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished">KonfigÅ«racija pritaikyta.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished">Å altinio IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation type="unfinished">Ä®spÄ—jimas</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Drausti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>amžinai</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Outgoing connection</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Procesas paleistas iÅ¡:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>iÅ¡ Å¡ios komandinÄ—s eilutÄ—s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>iÅ¡ Å¡io vykdomojo failo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Proceso no encontrado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>į prievadÄ… {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>į {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>iÅ¡ vartotojo {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>į {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>į *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">į *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation><b>Nuotolinis</b> procesas %s veikia <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>jungiasi prie <b>%s</b> per %s prievadÄ… %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>bando iÅ¡sprÄ™sti <b>%s</b> per %s, %s prievadÄ… %d</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Error al guarda la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Aplicando configuración en %s ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>Serverio adresas negali bÅ«ti tuÅ¡Äias</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Error al cargar la configuración %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>KonfigÅ«racija pritaikyta.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Error al aplicar la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>IÅ¡imtis iÅ¡saugant konfigÅ«racijÄ…: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Taikoma konfigÅ«racija {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Ä®keliant {0} konfigÅ«racijÄ… įvyko klaida</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Taikant konfigÅ«racijÄ… įvyko klaida: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Ä®spÄ—jimas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Turite pasirinkti duomenų bazÄ—s failÄ…<br>arba pasirinkite tipÄ… „Atmintyje“.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>DB tipas pakeistas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>Restart the GUI in order effects to take effect</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Užveskite pelÄ—s žymeklį virÅ¡ teksto, kad bÅ«tų rodoma pagalba<br><br>NepamirÅ¡kite apsilankyti wiki: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Klaida įkeliant proceso informacijÄ…:</b><br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Sustabdant stebÄ—jimo procesÄ… įvyko klaida:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>įkeliama…</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>NÄ—ra prijungtų mazgų.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>TaisyklÄ— pritaikyta.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Error al aplicar la regla: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>protokolas negali bÅ«ti tuÅ¡Äias arba panaikinkite jo žymÄ—jimÄ…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation type="unfinished">Protokolo regexp klaida</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>proceso kelias negali bÅ«ti tuÅ¡Äias</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Process path regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>komandinÄ— eilutÄ— negali bÅ«ti tuÅ¡Äia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Command line regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>Paskirties prievadas negali bÅ«ti tuÅ¡Äias</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Dst port regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>Dest host can not be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Dst host regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>Dest IP/Network can not be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Dst IP regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>Vartotojo ID negali bÅ«ti tuÅ¡Äias</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>User ID regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Klaida taikant taisyklÄ™: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>SÄ…rašų laukas negali bÅ«ti tuÅ¡Äias</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>Lists field must be a directory</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>TaisyklÄ— nepalaikoma</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Klaida įkeliant taisyklÄ™</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Neveikia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>IÅ¡jungta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Veikia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Ketinate iÅ¡trinti Å¡iÄ… taisyklÄ™. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Ar esate tuo tikras?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch tinklo statistika {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>{0} OpenSnitch tinklo statistika</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation type="unfinished">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>IÅ¡saugoti kaip CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>IÅ¡rinti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>IÅ¡jungti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Ä®jungti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Duplicate</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Redaguoti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>TaisyklÄ— pagal šį pavadinimÄ… ir mazgÄ… nerasta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Klaida:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Ä®spÄ—jimas:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Drausti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Visada</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Ketinate iÅ¡trinti Å¡iÄ… taisyklÄ™. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <translation type="obsolete">Última Conexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Pavadinimas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Adresas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>BÅ«sena</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Versija</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>TaisyklÄ—s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Laikas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Veiksmas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>TrukmÄ—</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Mazgas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ä®jungta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hits</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protokolas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Procesas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Paskirties vieta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>TaisyklÄ—</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>PaskutinisPrisijungimas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>PaskIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstHost</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>PaskPrievadas</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Veikimo laikas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Connections</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Dropped</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/nb_NO/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017500�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/nb_NO/opensnitch-nb_NO.ts������������������������������������������0000664�0000000�0000000�00000432605�15003540030�0023225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="nb_NO"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>Bruker-ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Kjørt fra</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Kilde-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>Prosess-ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>MÃ¥l-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>MÃ¥lport</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>fra denne kjørbare fil</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>fra denne kommandolinje</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>denne mÃ¥lport</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>denne bruker</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>denne mÃ¥l-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>en gang</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>for alltid</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Nekt</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Tillat</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>til omstart</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>fra denne PID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>handling</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1t</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>Brannmur</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Brannmur</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>InngÃ¥ende</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>UtgÃ¥ende</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>Profil</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation>Ny regel</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation>Lukk</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>Brannmurregel</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>Node</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished">Aktiver</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished">Beskrivelse</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation>Retning</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation>INN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation>UT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation>Handling</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation>DROPP</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation>AVVIS</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation>Slett</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished">Lagre</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation>Legg til</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Innstillinger</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Grensesnitt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Forvalgt utløpstid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>Forvalgt varighet for sprettoppvindu</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Forvalgt varighet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Acción por defecto de la ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Acción por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Forvalgt mÃ¥l</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>midtstilt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>øvre høyre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>nedre høyre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>øvre venstre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>nedre venstre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posición por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>etter kjørbar fil</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>etter kommandolinje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>etter mÃ¥lport</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>etter mÃ¥l-IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>etter bruker-id</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>en gang</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>for alltid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>nekt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>tillat</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Deshabilitar ventanas emergentes, sólo mostrar alerta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Noder</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>ProsessovervÃ¥kingsmetode</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Forvalgt varighet tar effekt nÃ¥r det ikke er noe brukergrensesnitt tilkoblet.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Adressen til noden.</p><p>Forvalgt: unix:///tmp/osui.sock (unix:// er pÃ¥krevd hvis det er en Unix-socket)</p><p>Det kan ogsÃ¥ være en IP-adresse med port: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Forvalgt loggnivÃ¥</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Versjon</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Forvalgt handling nÃ¥r det ikke er noe brukergrensesnitt tilkoblet.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Loggfiler Ã¥ skrive logger til.<br/></p><p>/dev/stdout skriver logger til standard-ut.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Loggfil</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. La ventana emergente sólo contendrá información relativa a la conexión. Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexiones desconocidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>HostName</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>frem til omstart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>alltid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Anvend oppsett for alle noder</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Database</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>I minnet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Fil</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Lukk</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Anvend</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Lagre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>frem til omstart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Databasetype</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Velg</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posición en pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="102"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Vis avansert fremvisning som forvalg</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Handling</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Varighet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Filtrer forbindelser ogsÃ¥ etter:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="362"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>Bruker-ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>MÃ¥lport</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>MÃ¥l-IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Forvalgt handling nÃ¥r det grafiske brukergrensesnittet er frakoblet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Feilsøk ugyldige forbindelser</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Forvalgte innstillinger</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Forvalgt posisjon pÃ¥ skjermen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>alle midertidige regler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source>Show events columns</source> <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Tid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>MÃ¥l</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Prosess</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1t</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation>Regler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation>1t eller mindre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation type="unfinished"></translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Fra denne kommandolinjen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>Fra denne kjørbare filen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation type="unfinished">en gang</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>Fra denne bruker-ID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Nekt</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Tillat</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation type="unfinished">Aktiver</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Prioritetsregel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Este campo sólo comprueba la ruta del ejecutable (la cual no es modificable por el usuario).<br/></p><p>Puedes usar expresiones regulares para denegar cualquier ejecución desde /tmp, por ejemplo; ^/tmp/.*$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>Fra denne PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation>Nettverk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation>Liste med domener/IP-nummer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation>Til denne listen med nettverkomrÃ¥der</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation>Til denne listen med IP-adresser</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation>Til denne listen med domener (regulæruttrykk)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Avvis</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> <source>Color</source> <translation type="obsolete">Farge</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch nettverkstatistikk</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">Lagre til CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Lag ny regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">vertsnavn - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Hendelser</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filter</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Tillat</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Nekt</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>F.eks.: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Noder</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Regler</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>aktiver</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="684"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">buscar regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Midlertidig</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Verter</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete">(doble click en un dominio para ver detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Adresser</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Porter</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Brukere</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Forbindelser</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Droppet</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Oppetid</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Versjon</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Endre regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Slett regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Borrar todos los hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Borrar todos las aplicaciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Borrar todas las direcciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Borrar todos los puertos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Borrar todos los usuarios</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(Doble click en una fila para editar una regla)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Borrar conexiones que coinciden con esta regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Avvis</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation type="unfinished">2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Statistikk</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Hjelp</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Lukk</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Aktiver</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Deaktiver</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation>Feil: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation>Tar i bruk endringer...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation>Aktiverer brannmur...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation>Deaktiverer brannmur...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>MÃ¥lport</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>Kildeport</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>MÃ¥l-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished">Kilde-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation>Brannmurregel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation type="unfinished">Aviso</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Tillat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Nekt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>for alltid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>UtgÃ¥ende forbindelse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Prosess startet fra:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>fra denne kommandolinjen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation type="unfinished">fra denne kjørbare fil</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Proceso no encontrado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>frem til omstart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>til port {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>til {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>fra bruker {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>til {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>til *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">a *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>oppretter forbindelse til <b>%s</b> pÃ¥ %s port %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation type="unfinished">fra denne PID</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation type="unfinished">Rechazar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation>oppretter forbindelse til <b>%s</b>, %s</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Error al guarda la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Aplicando configuración en %s ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Error al cargar la configuración %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Error al aplicar la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation type="unfinished"></translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Error al aplicar la regla: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Feil med mÃ¥lvert-regulæruttrykk</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>MÃ¥l-IP/nettverk kan ikke være blank</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Feil med regulæruttrykk for mÃ¥l-IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>Bruker-ID kan ikke være blank</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Feil med regulæruttrykk for bruker-ID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Feil ved anvending av regel: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Regelen støttes ikke</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Feil ved regellasting</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation>Det er allerede en regel med dette navnet.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation>PID-feltet kan ikke være blankt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation>Feil med regulæruttrykk for PID-felt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation>Velg minst ett felt.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Kjører ikke</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Deaktivert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Kjører</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Du er i ferd med Ã¥ slette denne regelen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Er du sikker?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>Treff</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Lagre som CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Slett</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Deaktiver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Aktiver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Rediger</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Feil:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Advarsel:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Tillat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation type="unfinished">Nekt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Alltid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Du er i ferd med Ã¥ slette denne regelen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> <source>LastConnection</source> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Navn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Vertsnavn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Versjon</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Tid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Handling</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Varighet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Aktivert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Treff</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Prosess</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>MÃ¥l</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>BrukerID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>SisteForbindelse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>MÃ¥lIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>MÃ¥lVert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>MÃ¥lPort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="175"/> <source>Addr</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> <source>Connections</source> <translation type="obsolete">Conexiones</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> <source>Dropped</source> <translation type="obsolete">Rechazadas</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Hva</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation>Anvend pÃ¥</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation>Avvis</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>Nettverknavn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="378"/> <source>Addr</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Oppetid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Forbindelser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Droppet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hva</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Prioritet</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation>Koblet opp ny node</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Beskrivelse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kommandolinje</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation>Eksporter regler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation>Importer regler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation>Eksporter hendelser til CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation>Avslutt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation>Eksporter</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation>Til utklippstavle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation>Til disk</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> ���������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/nl_NL/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017507�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/nl_NL/opensnitch-nl_NL.ts������������������������������������������0000664�0000000�0000000�00000463124�15003540030�0023243�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="nl"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>Gebruikers-id</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Wordt uitgevoerd op</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>TekstLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Bron-ip</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>Proces-id</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>Bestemmings-ip</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>dit uitvoerbare bestand</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>deze opdrachtregel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>deze bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>deze gebruiker</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>deze bestemmings-ip</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>eenmalig</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>oneindig</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Weigeren</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Toestaan</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>tot herstart</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>deze PID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>actie</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30 sec.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1 uur</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>Firewall</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>Inkomend</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>Uitgaand</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>Profiel</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation>Sta inkomende verbindingen op een bepaalde poort toe</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation>Dienst toestaan (IN)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation>Weiger uitgaande verbindingen op een bepaalde poort</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation>Dienst toestaan (UIT)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation>Nieuwe regel</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="453"/> <source>xxx</source> <translation type="obsolete">xxx</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation>Sluiten</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>Firewallregel</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>Knooppunt</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> <source>All</source> <translation type="obsolete">Alles</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation>Inschakelen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation>Beschrijving</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation>Eenvoudig</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation>Voorwaarde toevoegen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation>Voorwaarde verwijderen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation>Richting</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation>IN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation>UIT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation>Actie</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation>TOESTAAN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation>AFWIJZEN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation>WEIGEREN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation>TERUGSTUREN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation>Wissen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation>Verwijderen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation>Opslaan</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation>Toevoegen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Instellingen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Vormgeving</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Stadaard time-out</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>De standaard meldingsduur</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Standaardduur</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Acción por defecto de la ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Acción por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Standaarddoel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>Midden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>Rechtsboven</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>Rechtsonder</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>Linksboven</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>Linksonder</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posición por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>Uitvoerbaar bestand</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>Opdrachtregel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>Bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>Bestemmings-ip</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>Gebruikers-id</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>Eenmalig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>Oneindig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>Weigeren</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>Toestaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Deshabilitar ventanas emergentes, sólo mostrar alerta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Knooppunten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Procesmonitormethode</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>De standaardduur die wordt gebruikt als er geen grafisch programma is.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Het adres van het knooppunt.</p><p>Standaard: unix:///tmp/osui.sock (unix:// is vereist bij gebruik van een Unix-socket)</p><p>Dit kan ook een ip-adres met poort zijn: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Adres</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Standaard logniveau</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Versie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>De standaardactie die wordt uitgevoerd als er geen grafisch programma is.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Het logboek waarin logregels worden genoteerd.<br/></p><p>/dev/stdout voegt regels toe aan de standaarduitvoer.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Logboek</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. La ventana emergente sólo contendrá información relativa a la conexión. Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexiones desconocidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>Hostnaam</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>Tot herstart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>Altijd</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Instellingen toepassen op alle knooppunten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Databank</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>In geheugen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Bestand</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Sluiten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Toepassen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Opslaan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>Tot herstart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Soort databank</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Kiezen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posición en pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="102"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Altijd uitgebreide meldingen tonen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Actie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Kruis aan om uitgebreide meldingen te tonen.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Duur</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>Als een eenvoudige melding wordt getoond, dan kunt u verbindingen of programma's filteren op basis van een bepaalde eigenschap (uitvoerbaar bestand, poort, ip-adres, etc.).</p><p>Met deze opties heeft u keuze uit meerdere mogelijkheden om verbindingen te filteren.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Verbindingen tevens filteren op:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="362"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>Gebruikers-id</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>Bestemmings-ip</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Deze time-out telt af tot het moment waarop u een melding te zien krijgt.</p><p>Als er geen keuze wordt gemaakt, dan worden de standaardopties gebruikt.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>In de uitgebreide weergave kunt u eenvoudig meerdere keuzes maken om verbindingen te filteren</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Kruis aan om dit veld te kiezen als er een melding wordt getoond</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>De standaard meldingsactie.</p><p>Als er een nieuwe uitgaande verbinding wordt opgezet, dan wordt standaard deze actie uitgevoerd als de time-out optreedt.</p><p><br/></p><p>Als een melding vraagt om een verbinding toe te staan of te weigeren:</p><p>1. nieuwe uitgaande verbindingen worden geweigerd;</p><p>2. bekende verbindingen worden toegestaan of geweigerd op basis van zelf-opgegeven regels.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Standaardactie bij geen grafisch programma</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Fouten van slechte verbindingen opsporen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Meldingen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Standaardopties</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Standaard meldingspositie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>Iedere tijdelijke regel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Cuando esta opción está seleccionada, las reglas de la duración elegida no se añadirán a la lista de reglas temporales en la GUI.</p><p><br/></p><p>Las reglas temporales seguirán siendo válidas, y puedes usarlas cuando se pregunte para permitir o denegar una nueva conexión.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="490"/> <source>Don't save rules of duration</source> <translation type="obsolete">No guardar reglas de duración</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source>Show events columns</source> <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Tijdstip</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Bestemming</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Proces</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Knooppunt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Gebeurteniskolommen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>PID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation>Interactieve meldingen uitschakelen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation>Meldingen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation>Systeemmeldingen gebruiken</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation>Qt-meldingen gebruiken</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation>Uitproberen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation><html><head/><body><p>Kruis aan om OpenSnitch te laten vragen of u verbindingen zonder PID wilt toestaan of weigeren. Dit kan bijvoorbeeld handig zijn bij verbindingen met een slechte status.</p><p>Het pop-upvenster bevat alleen informatie over de networkverbinding.</p><p>In sommige gevallen zijn deze verbindingen echter niet slecht, bijvoorbeeld bij het opzetten van een vpn met behulp van WireGuard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation>minuten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation>Aantal minuten tussen gebeurtenisverwijderingen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation>dagen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation>Aantal te behouden gebeurtenisdagen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>Afwijzen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation>Systeem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation>Opdrachtregel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation>Thema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30 sec.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1 uur</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation>Regels</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation>Kruis aan om de regels van de gekozen duur niet toe te voegen aan de lijst met tijdelijke regels. De tijdelijke regels blijven echter geldig en kunnen worden gebruikt indien om een actie gevraagd wordt.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation>Regelduur niet onthouden/verwijderen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation>30 sec. of korter</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation>5 min. of korter</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation>15 min. of korter</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation>30 min. of korter</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation>1 uur of korter</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Procesinformatie</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>Bezig met laden…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: bezig met laden…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>Geheugenstatistieken: bezig met laden…</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Geopende bestanden</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>I/O-statistieken</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Bestanden in geheugen</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Stapel</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Omgevingsvariabelen</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Programma-pid's</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Procesmonitoring starten/stoppen</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Sluiten</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Knooppunt</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Regel toepassen op alle knooppunten</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Van deze opdrachtregel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>Van dit uitvoerbare bestand</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Actie</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>Naar dit ip-adres/netwerk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>Eenmalig</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>Altijd</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>Naar deze poort</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>Van deze gebruikers-id</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>Komma's of spaties om meerdere domeinnamen op te geven zijn niet toegestaan. Gebruik in plaats daarvan reguliere uitdrukking: .*(opensnitch|duckduckgo).com .*\.google.com of een losse domeinnaam: www.gnu.org - komt alleen overeen met www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - komt alleen overeen met gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domein.org, .*\.domein.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Alleen tcp, udp en udplite toegestaan</p><p>U kunt reguliere uitdrukkingen gebruiken, zoals ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>Tcp</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>U kunt een los ip-adres opgeven: - 192.168.1.1 of een reguliere uitdrukking: - 192\.168\.1\.[0-9]+ meerdere ip-adressen: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Een subnet is ook mogelijk: - 192.168.1.0/24 Let op: komma's en spaties om adressen en netwerken te scheiden zijn niet toegestaan.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Duur</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>Naar deze host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Weigeren</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Toestaan</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Nombre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Inschakelen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>De regels worden op alfabetische volgorde uitgevoerd, dus geef ze een naam die prioriteit aanduidt. 000-allow-localhost 001-deny-broadcast …</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> <source>leave blank to autocreate</source> <translation type="obsolete">dejar en blanco para autoasignar nombre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Kruis aan om deze regel boven de rest te laten gaan. Na deze regels worden geen nieuwe meer aangekruist. Let op: regels worden in alfabetische volgorde uitgevoerd. Voorbeeld: [x] Prioriteit - 000-priority-rule [ ] Prioriteit - 001-less-priority-rule</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Prioriteitsregel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Standaard zijn regels hoofdlettergevoelig. Als een proces dus verbinding wil maken met gOOgle.CoM en u een regel hebt opgegeven om .*google.com te weigeren, dan wordt de verbinding geblokkeerd.<br/></p><p>Als u deze optie aankruist, dan dient u de exacte tekenreeks (domeinnaam, uitvoerbaar bestand, opdrachtregel) die u wilt filteren op te geven.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>Hoofdlettergevoelig</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>U kunt met behulp van reguliere uitdrukkingen meerdere poorten opgeven:</p><p><br/></p><p>- 53, 80 of 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 of 5551, 5552, 5553, etc.:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>Tot herstart</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>Naar deze lijst met domeinnamen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>Programma's</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Este campo sólo comprueba la ruta del ejecutable (la cual no es modificable por el usuario).<br/></p><p>Puedes usar expresiones regulares para denegar cualquier ejecución desde /tmp, por ejemplo; ^/tmp/.*$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation><html><head/><body><p>Dit veld bevat de door de gebruiker uitgevoerde opdrachtregel.<br/></p><p>Als de gebruiker een opdracht invoert, dan wordt alleen de opdracht getoond:</p><p>telnet 1.2.3.4<br/></p><p>Als de gebruiker een directe of relatieve opdrachtlocatie opgeeft, dan wordt het volgende getoond:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>Van deze PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation>Netwerk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation>Lijst met domeinen/ip's</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation>Naar deze lijst met netwerkreeksen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation>Naar deze lijst met ip-adressen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Kies een map met bestanden die een lijst met toe te stane of te weigeren ip-adressen bevat:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Eén ip-adres per regel. Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Kies een map met bestanden die een lijst met toe te stane of te weigeren netwerkreeksen bevat:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p></p>.</p><p>etc.<br/></p><p>Eén reeks per regel. Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Kies een map met lijsten met toe te stane of te weigeren domeinnamen.</p><p>Voorzie de map van bestanden met welke extensie dan ook.</p><p><br/>De opmaak van elke regel is als volgt (hostsopmaak):</p><p>127.0.0.1 www.domein.nl</p><p>of </p><p>0.0.0.0 www.domein.nl</p><p>Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation>Naar deze lijst met domeinnamen (reguliere uitdrukkingen)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Kies een map met reguliere-uitdrukkingsbestanden met toe te stane of te weigeren domeinnamen.</p><p>.*\.voorbeeld\.nl.</p><p>U kunt ook een volledige domeinnaam gebruiken: &quot;voorbeeld.nl&quot; , die vervolgens overeenkomt met iets.voorbeeld.nl, iets.voorbeeld.nl.localdomain, etc.</p><p>Eén domeinnaam per regel. Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Afwijzen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation>Beschrijving…</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation><html><head/><body><p>De waarde van dit veld is altijd de directe locatie naar het uitvoerbare bestand: /locatie/van/bestand<br/></p><p>Voorbeelden:</p><p>- Eenvoudig: /locatie/van/bestand</p><p>- Meerdere locaties: ^/usr/lib(64|)/firefox/firefox$</p><p>- Meerdere bestanden: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Weiger-/Toestaanacties met behulp van /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>Bekijk meer voorbeelden op onze <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wikipagina</a> of vraag hulp ons <a href="https://github.com/evilsocket/opensnitch/discussions">forum</a>.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>Is een reguliere uitdrukking</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation>Is een reguliere uitdrukking</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation>Netwerkinterface</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation>Overig</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation>Leg met deze regel overeenkomende verbindingen niet vast in het logboek</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation>Verbindingen niet vastleggen in logboek</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>Weigeren negeert de verbinding</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>Afwijzen wijst de verbinding af en sluit de socket in kwestie af</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>Toestaan staat de verbinding toe</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> <source>Name (leave blank to autocreate)</source> <translation type="obsolete">Naam (laat leeg om automatisch aan te maken)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation>Icmp</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation>Icmp6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation>Sctp</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation>Sctp6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> <source>Color</source> <translation type="obsolete">Kleur</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch-netwerkstatistieken</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">Exportar a CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Nieuwe regel opstellen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">Hostnaam - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Onderscheppen aan/uit</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Gebeurtenissen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filter</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Toestaan</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Weigeren</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Bijv. firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Knooppunten</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Regels</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>Inschakelen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="684"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">buscar regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Programmaregels</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>Permanent</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Tijdelijk</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete">(doble click en un dominio para ver detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Programma's</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Adressen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Poorten</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Gebruikers</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Verbindingen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Afgewezen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Uptime</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Versie</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Alle onderschepte gebeurtenissen wissen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Regel bewerken</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Regel verwijderen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Borrar todos los hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Borrar todos las aplicaciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Borrar todas las direcciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Borrar todos los puertos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Borrar todos los usuarios</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(Doble click en una fila para editar una regla)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Borrar conexiones que coinciden con esta regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Alle programma's</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Afwijzen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation>Systeemregels</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation>Knooppunt verwijderen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation>Knooppuntvoorkeuren openen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation>Onderscheppen van knooppunt aan/uit</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Statistieken</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Hulp</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Sluiten</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Inschakelen</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Uitschakelen</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation>De instellingen zijn toegepast.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation>Foutmelding: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation>Bezig met toepassen van wijzigingen…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation>Foutmelding tijdens opvragen van INVOERbeleid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation>Foutmelding tijdens opvragen van UITVOERbeleid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation>De firewallregels kunnen in het grafische programma alléén worden ingesteld met ‘nftables’ en niet met ‘iptables’</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation>Bezig met inschakelen van firewall…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation>Bezig met uitschakelen van firewall…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>Bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>Bronpoort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>Bestemmings-ip</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation>Bron-ip</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation>Invoerinterface</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation>Uitvoerinterface</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation>Conntrackmarkering instellen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation>Conntrackmarkering overeen laten komen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation>Conntrackstatus(sen) overeen laten komen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation>Pakket markeren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation>Pakketinformatie markeren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation>Bandbreedtequota</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation>Verbindingen met beperkt gebruik</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation>Uw protobuf-version is incompatibel. Installeer protobuf 3.8.0 of nieuwer (pip3 install --ignore-installed protobuf==3.8.0)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation>De regel is verwijderd</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation>De regel is toegevoegd</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation>U kunt gebruikmaken van ‘,’ of ‘-’ om meerdere poorten/ip-adressen of reeksen/waarden op te geven:<br><br>Poorten: 22 of 22,443 of 50000-60000<br>Ip-adressen: 192.168.1.1 of 192.168.1.30-192.168.1.130<br>Waarden: echo-reply,echo-request<br>Waarden: new,established,related</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation>Bezig met verwijderen van regel…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation>De regel kan niet worden bijgewerkt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation>Bezig met toevoegen van regel…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation><Kies een uitdrukking></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation>Gelijk</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation>Niet gelijk</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation>Groter dan of gelijk aan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation>Groter dan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation>Kleiner dan of gelijk aan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation>Kleiner dan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation>Firewallregel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation>Eenvoudig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation>Uitgebreid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation>Deze regel wordt nog niet ondersteund.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation>Dienst uitsluiten</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation>Sta inkomende verbindingen toe op de gekozen poort.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation>Sta uitgaande verbindingen toe op de gekozen poort.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation>Kies een uitdrukking.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation>De waarde mag niet 0 of blanco zijn.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation>De waarde-opmaak is 1024/kbytes (of bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation>De waarde-opmaak is 1024/kbytes/second (of bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation>De beperking is ongeldig - gebruik bytes, mbytes of gbytes.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation>De beperking is ongeldig - gebruik second, minute, hour of day</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation>De poort is ongeldig.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation>Informatie</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation>Foutmelding</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation>Waarschuwing</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>Systeemmeldingen zijn niet beschikbaar - installeer python3-notify2.</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Toestaan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Weigeren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>Oneindig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Uitgaande verbinding</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Proces gestart via:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>deze opdrachtregel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>dit uitvoerbare bestand</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Proceso no encontrado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>Tot herstart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>naar poort {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>naar {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>naar gebruiker {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>naar {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>naar *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">a *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation><b>Extern</b> proces %s actief op <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>maakt verbinding met <b>%s</b> op %s poort %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>tracht <b>%s</b> te herleiden via %s, %s poort %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation>van deze PID</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation>Nieuwe uitgaande verbinding</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation>Afwijzen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation>maakt verbinding met <b>%s</b>, %s</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Error al guarda la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Aplicando configuración en %s ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>Het serveradres mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Error al cargar la configuración %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>De instellingen zijn toegepast.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Error al aplicar la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Uitzondering tijdens opslaan van instellingen: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Bezig met toepassen van instellingen op {0}…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Foutmelding tijdens laden van {0}-instellingen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Foutmelding tijdens toepassen van instellingen: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Waarschuwing</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Kies een databankbestand<br>of ‘In geheugen’.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>Het soort DB is gewijzigd</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>Herstart het programma om de wijzigingen toe te passen.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Houd de cursor boven teksten om hulpballonnen te tonen<br><br>Bekijk ook de wiki: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation>Systeem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>Er zijn geen thema's beschikbaar. Installeer qt-material: pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation>Het thema is gewijzigd</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation>Herstart het programma om het nieuwe thema toe te passen.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation>Oké</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> <source>Restart the GUI in order changes to take effect</source> <translation type="obsolete">Herstart het programma om de wijzigingen toe te passen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation>Er zijn geen knooppunten verbonden</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation>Uitzondering tijdens opslaan van knooppuntinstellingen {0}: {1}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Foutmelding tijdens laden van procesinformatie:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Foutmelding tijdens stoppen van monitorproces:</b> <br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>Bezig met laden…</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>Er zijn geen knooppunten verbonden.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>De regel is toegepast.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Error al aplicar la regla: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>Het protocol mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Reguliere-uitdrukkingsfout in protocol</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>De proceslocatie mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Reguliere-uitdrukkingsfout in proceslocatie</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>De opdrachtregel mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Reguliere-uitdrukkingsfout in opdrachtregel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>De bestemmingspoort mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Reguliere-uitdrukkingsfout in bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>De bestemmingspoort mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Reguliere-uitdrukkingsfout in bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>De bestemmings-ip/-netwerk mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Reguliere-uitdrukkingsfout in bestemmings-ip/-netwerk</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>De gebruikers-id mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Reguliere-uitdrukkingsfout in gebruikers-id</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>De regel kan niet worden toegepast: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>Het lijstveld mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>Het lijstveld dient een map te zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Deze regel wordt niet ondersteund</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>De regel kan niet worden geladen</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation>Er is al een regel met deze naam.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation>Het PID-veld mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation>Reguliere-uitdrukkingsfout in PID-veld</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation>Kies minimaal één veld.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation>De netwerkinterface mag niet blanco zijn</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation>Reguliere-uitdrukkingsfout in netwerkinterface</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Inactief</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Uitgeschakeld</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Actief</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> U staat op het punt om deze regel te verwijderen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Weet u het zeker?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch-netwerkstatistieken {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch-netwerkstatistieken van {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>Tikken</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Opslaan als csv-bestand</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Verwijderen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Uitschakelen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Inschakelen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Klonen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Bewerken</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Er is geen regel met die naam en dat knooppunt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Foutmelding:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Waarschuwing:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Toestaan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Weigeren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Altijd</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Tot herstart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> U staat op het punt om deze regel te verwijderen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> <source>LastConnection</source> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Naam</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Adres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hostnaam</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Versie</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regels</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Tijdstip</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Actie</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Duur</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Knooppunt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ingeschakeld</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Tikken</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Proces</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Bestemming</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Gebruikers-id</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>RecentsteVerbinding</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Bestemmings-ip</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Bestemmingshost</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Bestemmingspoort</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="175"/> <source>Addr</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> <source>Connections</source> <translation type="obsolete">Conexiones</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> <source>Dropped</source> <translation type="obsolete">Rechazadas</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Wat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation>Toepassen op</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation>Afwijzen</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>Netwerknaam</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="378"/> <source>Addr</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Uptime</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Verbindingen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Afgewezen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Wat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Voorkomen</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation>Nieuw knooppunt verbonden</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Beschrijving</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Opdrachtregel</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation>Regels exporteren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation>Regels importeren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation>Gebeurtenissen exporteren naar csv-bestand</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation>Afsluiten</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation>Exporteren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation>Naar klembord</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation>Naar schijf</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation>Kies een exportmap</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation> U staat op het punt om dit item te verwijderen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation> U staat op het punt om dit knooppunt te verwijderen. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation><b>Het knooppunt kan niet worden verwijderd</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation>De regels kunnen niet worden geëxporteerd</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation>Kies een map met te importeren regels (json-bestanden)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation>De regels zijn geïmporteerd</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/pt_BR/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017513�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/pt_BR/opensnitch-pt_BR.ts������������������������������������������0000664�0000000�0000000�00000474263�15003540030�0023261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="pt_BR"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>ID do usuário</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Executado de</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>IP de origem</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>ID de processo</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>IP de destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Porta Dst</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="226"/> <source>(/path/to/bin/chromium)</source> <translation type="obsolete">(/caminho/para/bin/chromium)</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="271"/> <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source> <translation type="obsolete">O navegador da Web Chromium deseja se conectar a www.evilsocket.net na porta tcp 443. E talvez a www.goodsocket.net na porta 344</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>a partir deste executável</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>a partir desta linha de comando</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>esta porta de destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>este usuário</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>este ip de destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>uma vez</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1h</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">para esta sessão</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>para sempre</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>a partir desse PID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>ação</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>Firewall</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>De entrada</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>De saída</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>Perfil</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation>Permitir conexões de entrada para uma porta</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation>Permitir serviço (Entrada)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="398"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation>Excluir conexões de saída para uma porta de serem interceptadas</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="407"/> <source>Allow service (OUT)</source> <translation>Permitir serviço (Saída)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="427"/> <source>New rule</source> <translation>Nova regra</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="421"/> <source>Close</source> <translation>Fechar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>Regra do firewall</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>Node</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation>Descrição</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation>Simple</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation>Adicionar nova condição</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation>Remover condição selecionada</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="221"/> <source>Direction</source> <translation>Direção</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="232"/> <source>IN</source> <translation>Entrada</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="241"/> <source>OUT</source> <translation>Saída</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="268"/> <source>Action</source> <translation>Ação</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="279"/> <source>ACCEPT</source> <translation>ACEITAR</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="288"/> <source>DROP</source> <translation>DERRUBAR</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="297"/> <source>REJECT</source> <translation>REJEITAR</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="306"/> <source>RETURN</source> <translation>RETORNAR</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="432"/> <source>Clear</source> <translation>LImpar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="443"/> <source>Delete</source> <translation>Deletar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="454"/> <source>Save</source> <translation>Salvar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="465"/> <source>Add</source> <translation>Adicionar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="250"/> <source>FORWARD</source> <translation>AVANÇAR</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="255"/> <source>PREROUTING</source> <translation>PRÉ-ENCAMINHAMENTO</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="260"/> <source>POSTROUTING</source> <translation>PÓS-ROTEAMENTO</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="315"/> <source>QUEUE</source> <translation>FILA</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="323"/> <source>DNAT</source> <translation>DNAT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="328"/> <source>SNAT</source> <translation>SNAT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="333"/> <source>REDIRECT</source> <translation>REDIRECIONAR</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="349"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation>dependendo da Ação (ou seja: alvo), a sintaxe dos parâmetros irá variar. Alguns exemplos: FILA -> num 0 (ou 1, 2, ...) REDIRECIONAMENTO, TPROXY, DNAT, SNAT, MASCARADO: para :22 para 192.168.1.254:8080 para 192.168.1.254 para 1024-2048 (mascarado)</translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Preferências</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source>UI</source> <translation>UI</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Este tempo limite é a contagem regressiva que você vê quando uma caixa de diálogo pop-up é exibida.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="469"/> <source>Default timeout</source> <translation>Tempo limite padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>Duração padrão do pop-up</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1140"/> <source>Default duration</source> <translation>Duração padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Ação padrão de pop-up</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Ação padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Alvo padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>centro</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>superior direito</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>inferior direito</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>superior esquerdo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>inferior esquerdo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posição padrão da caixa de diálogo de prompt na tela</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>por executável</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>por linha de comando</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>por porta de destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>por ip de destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>por id de usuário</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1148"/> <source>once</source> <translation>uma vez</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1h</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">para esta sessão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>para sempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1116"/> <source>deny</source> <translation>negar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1125"/> <source>allow</source> <translation>permitir</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="411"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Desativar pop-ups, exibir apenas um alerta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="950"/> <source>Nodes</source> <translation>Nodes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1183"/> <source>Process monitor method</source> <translation>Método de monitoramento de processo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1137"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>A duração padrão ocorrerá quando não houver interface do usuário conectada.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1077"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Endereço do node</p><p>Padrão: unix:///tmp/osui.sock (unix:// é obrigatório se for um soquete Unix)</p><p>Também pode ser um endereço IP com a porta: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1080"/> <source>Address</source> <translation>Endereço</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1301"/> <source>Default log level</source> <translation>Nível de registro padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1010"/> <source>Version</source> <translation>Versão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1099"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>A ação padrão ocorrerá quando não houver interface do usuário conectada.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>audit</source> <translation type="obsolete">auditar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1234"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Arquivo de log para gravar logs.<br/></p><p>/dev/stdout irá imprimir registros na saída padrão.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Log file</source> <translation>Arquivo de log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="788"/> <source>IMPORTANT</source> <translation type="obsolete">IMPORTANTE</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="793"/> <source>WARNING</source> <translation type="obsolete">ADVERTÊNCIA</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="798"/> <source>ERROR</source> <translation type="obsolete">ERRO</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Se marcado, o opensnitch solicitará que você permita ou negue conexões que não tenham um PID associado, devido a vários motivos.&lt;/p&gt;&lt;p&gt;A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexões desconhecidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="962"/> <source>HostName</source> <translation>HostName</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1091"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1153"/> <source>until restart</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1158"/> <source>always</source> <translation>sempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1312"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1317"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1052"/> <source>Apply configuration to all nodes</source> <translation>Aplicar configuração a todos os nodes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1353"/> <source>Database</source> <translation>Base de dados</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="630"/> <source>Database name</source> <translation type="obsolete">Nome do banco de dados</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1388"/> <source>In memory</source> <translation>Na memória</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1393"/> <source>File</source> <translation>Arquivo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>/path/to/the/file.db</source> <translation type="obsolete">/caminho/para/o/arquivo.db</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1696"/> <source>Close</source> <translation>Fechar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1707"/> <source>Apply</source> <translation>Aplicar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1718"/> <source>Save</source> <translation>Salvar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1407"/> <source>Database type</source> <translation>Tipo de banco de dados</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1414"/> <source>Select</source> <translation>Selecionar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Configure the</source> <translation type="obsolete">Configure o</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opções padrão de pop-ups</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posição padrão dos pop-ups na tela</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="105"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>A visualização avançada permite que você aplique mais filtros em uma conexão</p><p>quando um pop-up aparece.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Mostrar visualização avançada por padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="815"/> <source>Action</source> <translation>Ação</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Se marcado, os pop-ups serão exibidos com a visualização avançada ativa.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Duração</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>Por padrão, quando um novo pop-up aparece, em sua forma mais simples, você será capaz de filtrar conexões ou aplicativos por uma propriedade da conexão (executável, porta, IP, etc).</p><p>Com essas opções, você pode escolher vários campos para filtrar conexões para.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Filtre as conexões também por:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="365"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Se marcado, este campo será verificado quando um pop-up for exibido</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>ID do usuário</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Porta de destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>IP de destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Este tempo limite é a contagem regressiva que você vê quando uma caixa de diálogo pop-up é exibida.</p><p>Se o pop-up não for respondido, as opções padrão serão aplicadas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>A visualização avançada permite que você selecione facilmente vários campos para filtrar conexões</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Se marcado, este campo será selecionado quando um pop-up for exibido</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>Ação padrão de pop-up.</p><p>Quando uma nova conexão de saída está prestes a ser estabelecida, esta ação será selecionada por padrão, então se o tempo limite disparar, esta é a opção que será aplicada.</p><p><br/></p><p>Enquanto um pop-up pede ao usuário para permitir ou negar uma conexão:</p><p>1. novas conexões de saída são negadas.</p><p>2. conexões conhecidas são permitidas ou negadas com base nas regras definidas pelo usuário.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>Default action when the GUI is disconnected</source> <translation>Ação padrão quando a GUI é desconectada</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1169"/> <source>Debug invalid connections</source> <translation>Depurar conexões inválidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Pop-ups</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Opções padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Posição padrão na tela</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="896"/> <source>any temporary rules</source> <translation>quaisquer regras temporárias</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Quando esta opção é selecionada, as regras da duração selecionada não serão adicionadas à lista de regras temporárias na GUI.</p><p><br/></p><p>As regras temporárias ainda serão válidas e você pode usá-las quando solicitado a permitir/negar uma nova conexão.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="490"/> <source>Don't save rules of duration</source> <translation type="obsolete">Não salve regras de duração</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <source>Time</source> <translation>Tempo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="831"/> <source>Destination</source> <translation>Destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="847"/> <source>Process</source> <translation>Processo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <source>Rule</source> <translation>Regra</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="783"/> <source>Node</source> <translation>Node</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Se marcado, o opensnitch solicitará que você permita ou negue conexões que não tenham um PID asocciado, devido a vários motivos, principalmente devido a conexões de mau estado.&lt;/p&gt;&lt;p&gt;A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.&lt;/p&gt;&lt;p&gt;Existem alguns cenários em que essas conexões são válidas, como ao estabelecer uma VPN usando wireguard.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="712"/> <source>Events tab columns</source> <translation>Colunas da guia de eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>por PID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1166"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation><html><head/><body><p>Se marcado, o OpenSnitch solicitará que você permita ou negue conexões que não tenham um PID associado, devido a vários motivos, principalmente devido a conexões ruins.</p><p>A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.</p><p>Existem alguns cenários em que essas conexões são válidas, como ao estabelecer uma VPN usando o WireGuard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="476"/> <source>Disable pop-ups, only display a notification</source> <translation>Desativar pop-ups, exibir apenas uma notificação</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="616"/> <source>Desktop notifications</source> <translation>Notificações da área de trabalho</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="646"/> <source>Use system notifications</source> <translation>Usar notificações do sistema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="662"/> <source>Use Qt notifications</source> <translation>Usar notificações do Qt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="691"/> <source>Test</source> <translation>Testar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1501"/> <source>minutes</source> <translation>minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1533"/> <source>Minutes between events purges</source> <translation>Minutos entre expurgos de eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1559"/> <source>days</source> <translation>dias</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1572"/> <source>Maximum days of events to keep</source> <translation>Máximo de dias de eventos para manter</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>rejeitar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="563"/> <source>System</source> <translation>Sistema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="857"/> <source>Command line</source> <translation>Linha de comando</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Theme</source> <translation>Tema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="875"/> <source>Rules</source> <translation>Regras</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="883"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation>Quando esta opção é selecionada, as regras da duração selecionada não serão adicionadas à lista de regras temporárias na GUI. As regras temporárias ainda serão válidas e você poderá usá-las quando solicitado a permitir/negar uma nova conexão.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="888"/> <source>Don't save/Delete rules of duration</source> <translation>Não salvar/excluir regras de duração</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="906"/> <source>30s or less</source> <translation>30s ou menos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="911"/> <source>5m or less</source> <translation>5m ou menos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="916"/> <source>15m or less</source> <translation>15m ou menos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>30m or less</source> <translation>30m ou menos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="926"/> <source>1h or less</source> <translation>1h ou menos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="509"/> <source>Language</source> <translation>Idioma</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1071"/> <source>General</source> <translation>Geral</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="536"/> <source>4MiB</source> <translation>4MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="541"/> <source>8MiB</source> <translation>8MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="546"/> <source>16MiB</source> <translation>16MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="551"/> <source>32MiB</source> <translation>32MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="584"/> <source>Maximum size for receiving messages from nodes. Default 4MB</source> <translation>Tamanho máximo para receber mensagens de nós. Padrão 4 MB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="587"/> <source>Max gRPC channel size</source> <translation>Tamanho máximo do canal gRPC</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="594"/> <source>By default the GUI is started when login</source> <translation>Por padrão, a GUI é iniciada quando o login é feito</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="597"/> <source>Autostart the GUI upon login</source> <translation>Iniciar automaticamente a GUI após o login</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="628"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1221"/> <source>Logging</source> <translation>Registrando</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1244"/> <source><html><head/><body><p>If checked, OpenSnitch will log timestamp microseconds.</p></body></html></source> <translation><html><head/><body><p>Se marcado, o OpenSnitch registrará microssegundos de registro de data e hora.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1247"/> <source>Log timestamp microseconds</source> <translation>Registrar microssegundos de carimbo de data/hora</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1291"/> <source><html><head/><body><p>If checked, OpenSnitch will use the UTC timezone for timestamps.</p></body></html></source> <translation><html><head/><body><p>Se marcado, o OpenSnitch usará o fuso horário UTC para timestamps.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>Log UTC timestamps</source> <translation>Registrar carimbos de data/hora UTC</translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Detalhes do processo</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>carregando...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: carregando...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>estatísticas mem: carregando...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Abrir arquivos</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>Estatísticas de I/O</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Arquivos mapeados na memória</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Pilha</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Variáveis de ambiente</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Aplicação pids</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Inicie ou pare de monitorar este processo</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Fechar</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Regra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Node</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Aplicar regra a todos os nodes</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Para esta linha de comando</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="466"/> <source>From this executable</source> <translation>Para este executável</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Ação</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/caminho/para/o/executavel, .*/bin/executable[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/> <source>To this IP / Network</source> <translation>Para este IP / Rede</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>uma vez</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="864"/> <source>30s</source> <translation type="obsolete">30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="869"/> <source>5m</source> <translation type="obsolete">5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="874"/> <source>15m</source> <translation type="obsolete">15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="879"/> <source>30m</source> <translation type="obsolete">30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/> <source>1h</source> <translation type="obsolete">1h</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>sempre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="896"/> <source>To this port</source> <translation>Para esta porta</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>Para este ID de usuário</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="586"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>Vírgulas ou espaços não podem especificar vários domínios. Em vez disso, use expressões regulares: .*(opensnitch|duckduckgo).com .*\.google.com ou um único domínio: www.gnu.org - só vai filtrar www.gnu.org, não filtrará ftp.gnu.org, nem www2.gnu.org, ... gnu.org - só vai filtrar gnu.org, não filtrará www.gnu.org, nem ftp.gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.dominio.org, .*\.dominio.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="520"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Apenas TCP, UDP ou UDPLITE são permitidos</p><p>Você pode usar expressão regulares, ou seja: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="255"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="260"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="265"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="270"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="275"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Você pode especificar um único IP: - 192.168.1.1 ou uma expressão regular: - 192\.168\.1\.[0-9]+ vários IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Você também pode especificar uma sub-rede: - 192.168.1.0/24 Nota: Vírgulas ou espaços não são permitidos para separar IPs ou redes.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="659"/> <source>LAN</source> <translation>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="669"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="674"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="679"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="689"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="694"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="699"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="704"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="709"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="714"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="719"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="724"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="729"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Duração</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="627"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="744"/> <source>To this host</source> <translation>Para este host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation>Nome</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>As regras são verificadas em ordem alfabética, para que você possa nomeá-las de acordo para priorizá-las. 000-permitir-localhost 001-negar-transmissão ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> <source>leave blank to autocreate</source> <translation type="obsolete">deixe em branco para criar automaticamente</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>Se marcada, esta regra terá precedência sobre o resto das regras. Nenhuma outra regra será verificada após esta. Você deve nomear a regra de forma que ela seja verificada primeiro, porque eles são verificados em ordem alfabética. Por exemplo: [x] Prioridade - 000-regra-prioritaria [ ] Prioridade - 001-regra-menos-prioritaria</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Regra de prioridade</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Por padrão, o campo das regras não diferencia maiúsculas de minúsculas, ou seja, se um processo tentar acessar gOOgle.CoM e você tiver uma regra para Negar. *Google.com, a conexão será bloqueada.<br/></p><p>Se você marcar esta caixa, deverá especificar a string exata (domínio, executável, linha de comando) que deseja filtrar.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1089"/> <source>Case-sensitive</source> <translation>Sensível a maiúsculas e minúsculas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Você pode especificar várias portas usando expressões regulares:</p><p><br/></p><p>- 53, 80 o 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 o 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="974"/> <source>To this list of domains</source> <translation>Para esta lista de domínios</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Selecione um diretório com listas de domínios para bloquear ou permitir.</p><p>Coloque dentro desse diretório arquivos com qualquer extensão que contenha listas de domínios.</p><p><br/>O formato de cada entrada de uma lista é o seguinte (formato de hosts):</p><p>127.0.0.1 www.dominio.com</p><p>ou </p><p>0.0.0.0 www.dominio.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>Aplicativos</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Este campo irá corresponder apenas ao caminho do executável. Não é modificável pelo usuário.<br/></p><p>Você pode usar expressões regulares para negar execuções de /tmp, por exemplo:<br/></p><p>^/tmp/.*$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation><html><head/><body><p>Este campo irá conter e corresponder à linha de comando que foi executada pelo usuário.<br/></p><p>Se o usuário digitou o comando, apenas o comando aparecerá:</p><p>telnet 1.2.3.4<br/></p><p>Se o usuário digitou o caminho absoluto ou relativo para o comando, é isso que aparecerá:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>A partir deste PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="485"/> <source>Network</source> <translation>Rede</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="926"/> <source>List of domains/IPs</source> <translation>Lista de domínios/IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>To this list of network ranges</source> <translation>Para esta lista de intervalos de rede</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="939"/> <source>To this list of IPs</source> <translation>Para esta lista de IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="965"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecione um diretório com arquivos contendo uma lista de IPs para bloquear ou permitir:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Um IP por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1000"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecione um diretório com arquivos contendo uma lista de intervalos de rede para bloquear ou permitir:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>Um intervalo de rede por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1028"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecione um diretório com listas de domínios para bloquear ou permitir.</p><p>Coloque dentro desse diretório arquivos com qualquer extensão contendo listas de domínios.</p><p><br/>O formato de cada entrada de uma lista é o seguinte (formato de hosts):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1043"/> <source>To this list of domains (regular expressions)</source> <translation>Para esta lista de domínios (expressões regulares)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1070"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Selecione um diretório com arquivos contendo expressões regulares de domínios para bloquear ou permitir:</p><p>.*\.example\.com</p><p>Você também pode usar um domínio como: &quot;example.com&quot; , e vai combinar whatever.example.com, whatever.example.com.localdomain, etc.</p><p>Um domínio por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Rejeitar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1145"/> <source>Description...</source> <translation>Descrição...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation><html><head/><body><p>O valor deste campo é sempre o caminho absoluto para o executável: /caminho/do/binário<br/></p><p>Exemplos:</p><p>- Simples: /caminho/do/binário</p><p>- Vários caminhos: ^/usr/lib(64|)/firefox/firefox$</p><p>- Vários binários: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Negar/Permitir execuções a partir de /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>Para mais exemplos, visite a <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">página wiki</a> ou pergunte nos <a href="https://github.com/evilsocket/opensnitch/discussions">fóruns de discussão</a>.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>É expressão regular</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="473"/> <source>is regular expression</source> <translation>é expressão regular</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/> <source>Network interface</source> <translation>Interface de rede</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1080"/> <source>More</source> <translation>Mais</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1096"/> <source>Don't log connections that match this rule</source> <translation>Não registre conexões que correspondam a esta regra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1099"/> <source>Don't log connections</source> <translation>Não registre conexões</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>Negar apenas descartará a conexão</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>Rejeitar derrubará a conexão e matará o soquete que a iniciou</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>Permitir permitirá a conexão</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="560"/> <source>ICMP</source> <translation>ICMP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="565"/> <source>ICMP6</source> <translation>ICMP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="570"/> <source>SCTP</source> <translation>SCTP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="575"/> <source>SCTP6</source> <translation>SCTP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="737"/> <source>From this IP / Network</source> <translation>A partir deste IP / Rede</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="866"/> <source>From this port</source> <translation>A partir desta porta</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation><html><head/><body><p>Você pode especificar várias portas usando expressões regulares:</p><p>- 53, 80 ou 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="664"/> <source>MULTICAST</source> <translation>MULTICAST</translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>Estatísticas da rede OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="287"/> <source>Save to CSV</source> <translation type="obsolete">Salvar em CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="297"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Crie uma nova regra</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1814"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Iniciar ou parar a interceptação</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filtro</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Ex.: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="829"/> <source>Nodes</source> <translation>Nodes</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes na coluna endereço para ver os detalhes de um node)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1721"/> <source>Rules</source> <translation>Regras</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="998"/> <source>enable</source> <translation>habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="674"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes na coluna Nome para ver os detalhes de uma regra)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">nome da regra de pesquisa</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="785"/> <source>Application rules</source> <translation>Regras de aplicação</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="939"/> <source>Permanent</source> <translation>Permanente</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="948"/> <source>Temporary</source> <translation>Temporário</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1069"/> <source>Hosts</source> <translation>Hosts</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes para ver os detalhes de um item)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1162"/> <source>Applications</source> <translation>Aplicativos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1278"/> <source>Addresses</source> <translation>Endereços</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Ports</source> <translation>Portas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1458"/> <source>Users</source> <translation>Usuários</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1565"/> <source>Connections</source> <translation>Conexões</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1617"/> <source>Dropped</source> <translation>Dropado</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1669"/> <source>Uptime</source> <translation>Tempo de atividade</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1788"/> <source>Version</source> <translation>Versão</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Excluir todos os eventos interceptados</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1028"/> <source>Edit rule</source> <translation>Editar regra</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1045"/> <source>Delete rule</source> <translation>Excluir regra</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Excluir todos os hosts interceptadas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Excluir todos os aplicativos interceptadas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Excluir todos os endereços interceptados</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Excluir todas as portas interceptadas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Excluir todos os usuários interceptados</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes em uma linha para ver os detalhes de uma regra)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="915"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Exclua conexões que correspondam a esta regra</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="930"/> <source>All applications</source> <translation>Todos os aplicativos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Rejeitar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="780"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="957"/> <source>System rules</source> <translation>Regras do sistema</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="640"/> <source>Delete this node</source> <translation>Excluir este node</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="656"/> <source>Show the preferences of this node</source> <translation>Mostrar as preferências deste node</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="672"/> <source>Start or stop interception of this node</source> <translation>Iniciar ou parar a interceptação deste node</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Statistics</source> <translation>Estatísticas</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Help</source> <translation>Ajuda</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="52"/> <source>Close</source> <translation>Fechar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Disable</source> <translation>Desabilitar</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="96"/> <source>Configuration applied.</source> <translation>Configuração aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="405"/> <source>Error: {0}</source> <translation>Error: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="198"/> <source>Applying changes...</source> <translation>Aplicando alterações...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="235"/> <source>Error getting INPUT chain policy</source> <translation>Erro ao obter a política da cadeia de ENTRADA</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="242"/> <source>Error getting OUTPUT chain policy</source> <translation>Erro ao obter a política de cadeia de SAÃDA</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="295"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation>Para configurar regras de firewall a partir da GUI, precisamos usar 'nftables' em vez de 'iptables'</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="309"/> <source>Enabling firewall...</source> <translation>Habilitando firewall...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="311"/> <source>Disabling firewall...</source> <translation>Desabilitando firewall...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>Porta de destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>Porta de origem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>IP de destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation>IP de origem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation>Interface de entrada</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation>Interface de saída</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation>Definir marca de controle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation>Correspondência de marca de controle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation>Corresponde ao(s) estado(s) de controle de correspondência</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation>Definir marca no pacote</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation>Correspondência de informações do pacote</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation>Cotas de largura de banda</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation>Limite de taxa de conexões</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="374"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation>Sua versão do protobuf é incompatível, você precisa instalar o protobuf 3.8.0 ou superior (pip3 install --ignore-installed protobuf==3.8.0)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="398"/> <source>Rule deleted</source> <translation>Regra deletada</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="402"/> <source>Rule added</source> <translation>Regra adicionada</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="424"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation>Você pode usar ',' ou '-' para especificar várias portas/IPs ou intervalos/valores:<br><br>ports: 22 ou 22,443 ou 50000-60000<br>IPs: 192.168.1.1 ou 192.168.1.30-192.168.1.130<br>Valores: eco-resposta,eco-pedido<br>Valores: novo,estabelecido,relacionado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="444"/> <source>Deleting rule, wait</source> <translation>Excluindo regra, aguarde</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="447"/> <source>Error updating rule</source> <translation>Erro ao atualizar regra</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="493"/> <source>Adding rule, wait</source> <translation>Adicionando regra, aguarde</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="502"/> <source><select a statement></source> <translation><selecione uma declaração></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="803"/> <source>Equal</source> <translation>Igual</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="804"/> <source>Not equal</source> <translation>Não igual</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="805"/> <source>Greater or equal than</source> <translation>Maior ou igual a</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="806"/> <source>Greater than</source> <translation>Maior que</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="807"/> <source>Less or equal than</source> <translation>Menor ou igual a</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="808"/> <source>Less than</source> <translation>Menor que</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1491"/> <source>Firewall rule</source> <translation>Regra do firewall</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1028"/> <source>Simple</source> <translation>Simple</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1033"/> <source>Advanced</source> <translation>Avançado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1154"/> <source>This rule is not supported yet.</source> <translation>Esta regra ainda não é suportada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1219"/> <source>Exclude service</source> <translation>Excluir serviço</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1231"/> <source>Allow inbound connections to the selected port.</source> <translation>Permitir conexões de entrada para a porta selecionada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1233"/> <source>Allow outbound connections to the selected port.</source> <translation>Permitir conexões de saída para a porta selecionada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1309"/> <source>select a statement.</source> <translation>selecione uma declaração.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1325"/> <source>value cannot be 0 or empty.</source> <translation>o valor não pode ser 0 ou vazio.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1337"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation>o formato do valor é 1024/kbytes (ou bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1351"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation>o formato do valor é 1024/kbytes/segundo (ou bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1354"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation>limite de taxa inválido, use: bytes, kbytes, mbytes ou gbytes.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1356"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation>limite de tempo inválido, use: segundo, minuto, hora ou dia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1423"/> <source>port not valid.</source> <translation>porta inválida.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation> Formatos suportados: - Simple: 23 - Gamas: 80-1024 - Múltiplas portas: 80,443,8080 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation> Formatos suportados: - Simple: 1.2.3.4 - Intervalos de IP: 1.2.3.100-1.2.3.200 - Intervalos de rede: 1.2.3.4/24 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation>Interface de entrada de correspondência. Expressões regulares não permitidas.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation>Interface de saída de correspondência. Expressões regulares não permitidas.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation>Defina uma marca de controle na conexão, no formato decimal.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation>Corresponde a uma marca de controle da conexão, no formato decimal.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation>Corresponde aos estados de controle. Formatos suportados: - Simple: novo - Vários estados separados por vírgulas: relacionado,novo </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation> Corresponde às metainformações do pacote. O valor deve estar no formato decimal, exceto na opção "l4proto". Para l4proto pode ser uma string em letras minúsculas, por exemplo: tcp udp icmp, etc Se o valor for decimal para protocolo ou lproto, ele o usará como código desse protocolo. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation>Defina uma marca no pacote que corresponda às condições especificadas. O valor está no formato decimal.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation> Corresponde aos códigos ICMP. Formatos suportados: - Simple: eco-pedido - Múltiplos separados por vírgulas: eco-pedido, eco-resposta </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation> Corresponde aos códigos ICMPv6. Formatos suportados: - Simple: Formatos suportados - Múltiplos separados por vírgulas: eco-pedido, eco-resposta </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation>Imprima uma mensagem quando esta regra corresponder a um pacote.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation> Aplicar cotas em conexões. Por exemplo quando: - "cota acima de 10/mbytes" -> aplicar a Ação definida (DERRUBAR) - "cota até 10/mbytes" -> aplicar a Ação definida (ACEITAR) O valor deve estar no formato: VALOR/UNIDADES, por exemplo: - 10mbytes, 1/gbytes, etc </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation> Aplicar limites nas conexões. Por exemplo quando: - "limite acima de 10/mbytes/minuto" -> aplique a Ação definida (DERRUBAR, ACEITAR, etc) (Quando houver mais de 10MB por minuto, aplique uma Ação) - "limite até 10/mbytes/hora" -> aplique a Ação definida (ACEITAR) O valor deve estar no formato: VALOR/UNIDADES/TEMPO, por exemplo: - 10/mbytes/minuto, 1/gbytes/hora, etc </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="623"/> <source>num</source> <translation>num</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="637"/> <source>to</source> <translation>para</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="482"/> <source>Add at least one statement.</source> <translation>Adicione pelo menos uma instrução.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="976"/> <source>Warning: ct set mark value is empty, malformed rule?</source> <translation>Aviso: o valor da marca do conjunto ct está vazio, regra malformada?</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="299"/> <source>Info</source> <translation>Informações</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="303"/> <source>Error</source> <translation>Erro</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="307"/> <source>Warning</source> <translation>Aviso</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="684"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>As notificações do sistema não estão disponíveis, você precisa instalar o python3-notify2.</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>para sempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Processo desconhecido</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Conexão de saída</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Processo lançado de:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>a partir deste executável</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>a partir desta linha de comando</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>para a porta {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>para {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>do usuário {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>para {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>para *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">para *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation><b>Processo remoto</b> %s rodando em <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>está conectando a <b>%s</b> em %s na porta %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>está tentando resolver <b>%s</b> via %s, %s porta %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation>a partir desse PID</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="114"/> <source>New outgoing connection</source> <translation>Nova conexão de saída</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation>Rejeitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation>está se conectando a <b>%s</b>, %s</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation>Abrir</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="311"/> <source>Server address can not be empty</source> <translation>O endereço do servidor não pode estar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="598"/> <source>Configuration applied.</source> <translation>Configuração aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="409"/> <source>Exception saving config: {0}</source> <translation>Configuração de salvamento de exceção: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="529"/> <source>Applying configuration on {0} ...</source> <translation>Aplicando configuração em {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="342"/> <source>Error loading {0} configuration</source> <translation>Erro ao carregar configuração de {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="600"/> <source>Error applying configuration: {0}</source> <translation>Erro ao aplicar configuração: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Warning</source> <translation>Aviso</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Você deve selecionar um arquivo para o banco de dados&lt;br&gt;ou escolher o tipo &quot;Na memória&quot;.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="424"/> <source>DB type changed</source> <translation>Tipo de banco de dados alterado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation type="obsolete">Reinicie a GUI para que os efeitos tenham efeito</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="637"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Passe o mouse sobre os textos para exibir a ajuda&lt;br&gt;&lt;br&gt;Não se esqueça de visitar a wiki: &lt;a href=&quot;{0}&quot;&gt;{0}&lt;/a&gt;</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="495"/> <source>System</source> <translation>Sistema</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="191"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>Temas não disponíveis. Instale qt-material: pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="496"/> <source>UI theme changed</source> <translation>Tema da IU alterado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="496"/> <source>Restart the GUI in order to apply the new theme</source> <translation>Reinicie a GUI para aplicar o novo tema</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="537"/> <source>Ok</source> <translation>Ok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="39"/> <source>Restart the GUI in order changes to take effect</source> <translation>Reinicie a GUI para que as mudanças tenham efeito</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="411"/> <source>There're no nodes connected</source> <translation>Não há nodes conectados</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="550"/> <source>Exception saving node config {0}: {1}</source> <translation>Exceção ao salvar a configuração do node {0}: {1}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="168"/> <source>System default</source> <translation>Sistema padrão</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="462"/> <source>Language changed</source> <translation>Idioma alterado</translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Erro ao carregar as informações do processo:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Erro ao parar o processo de monitoramento:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>carregando...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="235"/> <source>There're no nodes connected.</source> <translation>Não há nodes conectados.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="278"/> <source>Rule applied.</source> <translation>Regra aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="648"/> <source>protocol can not be empty, or uncheck it</source> <translation>protocolo não pode estar vazio ou desmarque-o</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="662"/> <source>Protocol regexp error</source> <translation>Erro de expressão de protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="666"/> <source>process path can not be empty</source> <translation>o caminho do processo não pode estar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="680"/> <source>Process path regexp error</source> <translation>Erro de expressão regular do caminho do processo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="684"/> <source>command line can not be empty</source> <translation>a linha de comando não pode estar vazia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="698"/> <source>Command line regexp error</source> <translation>Erro de expressão regular da linha de comando</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="738"/> <source>Dest port can not be empty</source> <translation>A porta de destino não pode estar vazia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="752"/> <source>Dst port regexp error</source> <translation>Erro de expressão regular da porta Dst</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="756"/> <source>Dest host can not be empty</source> <translation>Dest host não pode estar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="770"/> <source>Dst host regexp error</source> <translation>Erro de expressão regular do host Dst</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="812"/> <source>Dest IP/Network can not be empty</source> <translation>O IP/rede de destino não pode estar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="838"/> <source>Dst IP regexp error</source> <translation>Erro de expressão regular de IP Dst</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="850"/> <source>User ID can not be empty</source> <translation>O ID do usuário não pode estar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="864"/> <source>User ID regexp error</source> <translation>Erro de expressão regular do ID do usuário</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="280"/> <source>Error applying rule: {0}</source> <translation>Erro ao aplicar regra: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="983"/> <source><b>Rule not supported</b></source> <translation><b>Regra não suportada</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="938"/> <source>Lists field cannot be empty</source> <translation>O campo de listas não pode estar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="940"/> <source>Lists field must be a directory</source> <translation>O campo de listas deve ser um diretório</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="546"/> <source><b>Error loading rule</b></source> <translation><b>Erro ao carregar regra</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="252"/> <source>There's already a rule with this name.</source> <translation>Já existe uma regra com este nome.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="868"/> <source>PID field can not be empty</source> <translation>O campo PID não pode ficar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="882"/> <source>PID field regexp error</source> <translation>Erro de expressão regular do campo PID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="970"/> <source>Select at least one field.</source> <translation>Selecione pelo menos um campo.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="702"/> <source>Network interface can not be empty</source> <translation>A interface de rede não pode estar vazia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="716"/> <source>Network interface regexp error</source> <translation>Erro de expressão regular da interface de rede</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="720"/> <source>Source port can not be empty</source> <translation>A porta de origem não pode estar vazia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="734"/> <source>Source port regexp error</source> <translation>Erro de regexp da porta de origem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="774"/> <source>Source IP/Network can not be empty</source> <translation>O IP/rede de origem não pode estar vazio</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="800"/> <source>Source IP regexp error</source> <translation>Erro de regexp do IP de origem</translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <source>Not running</source> <translation>Não está em execução</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Disabled</source> <translation>Desabilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Running</source> <translation>Em execução</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1188"/> <source> Your are about to delete this rule. </source> <translation> Você está prestes a excluir esta regra. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1710"/> <source> Are you sure?</source> <translation> Você tem certeza?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="635"/> <source>OpenSnitch Network Statistics {0}</source> <translation>Estatísticas da rede OpenSnitch {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="637"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>Estatísticas da rede OpenSnitch para {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nome</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Endereço</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> <source>Version</source> <translation type="obsolete">Versão</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Rules</source> <translation>Regras</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Tempo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="874"/> <source>Action</source> <translation>Ação</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duração</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>Acertos</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2597"/> <source>Save as CSV</source> <translation>Salvar como CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Ativado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Delete</source> <translation>Deletar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete">&lt;b&gt;Erro:&lt;/b&gt;&lt;br&gt;&lt;br&gt;{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="953"/> <source>Disable</source> <translation>Desabilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="955"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="958"/> <source>Duplicate</source> <translation>Duplicado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Edit</source> <translation>Editar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1247"/> <source>Rule not found by that name and node</source> <translation>Regra não encontrada por esse nome e node</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1300"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Erro:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1307"/> <source>Warning:</source> <translation>Atenção:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="939"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="944"/> <source>Always</source> <translation>Sempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Until reboot</source> <translation>Até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1710"/> <source> You are about to delete this rule. </source> <translation> Você está prestes a excluir esta regra. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regra</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nome</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nome</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Endereço</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versão</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regras</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Tempo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Ação</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duração</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Ativado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acertos</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regra</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="286"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nome</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Endereço</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Versão</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="419"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regras</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Tempo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ação</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Duração</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Node</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Acessos</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Processo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regra</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>UltimaConexao</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstHost</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>TempoAtividade</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> <source>Uptime</source> <translation type="obsolete">Tempo de atividade</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> <source>Connections</source> <translation type="obsolete">Conexões</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> <source>Dropped</source> <translation type="obsolete">Dropado</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Qual</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Reject</source> <translation>Rejeitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="930"/> <source>Apply to</source> <translation>Aplicar para</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>Nome da rede</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Tempo de atividade</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Conexoes</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Dropado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="436"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Qual</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Precedencia</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="794"/> <source>New node connected</source> <translation>Novo node conectado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Descrição</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Terminal</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="405"/> <source>Export rules</source> <translation>Exportar regras</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Import rules</source> <translation>Importar regras</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Export events to CSV</source> <translation>Exportar eventos para CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Quit</source> <translation>Sair</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Export</source> <translation>Exportar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="963"/> <source>To clipboard</source> <translation>Para a área de transferência</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To disk</source> <translation>Para o disco</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2539"/> <source>Select a directory to export rules</source> <translation>Selecione um diretório para exportar regras</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1190"/> <source> Your are about to delete this entry. </source> <translation> Você está prestes a excluir esta entrada. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1677"/> <source> You are about to delete this node. </source> <translation> Você está prestes a excluir este node. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1686"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation><b>Erro ao excluir node</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2494"/> <source>Error exporting rules</source> <translation>Erro ao exportar regras</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2568"/> <source>Select a directory with rules to import (JSON files)</source> <translation>Selecione um diretório com regras para importar (arquivos JSON)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2582"/> <source>Rules imported fine</source> <translation>Regras importadas corretamente</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="229"/> <source>WARNING</source> <translation>AVISO</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="832"/> <source>Details</source> <translation>Detalhes</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>New</source> <translation>Novo</translation> </message> </context> </TS> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/ro_RO/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017525�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/ro_RO/opensnitch-ro_RO.ts������������������������������������������0000664�0000000�0000000�00000411177�15003540030�0023300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ro_RO"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>din acest executabil</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>din această linie de comandă</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>acest port de destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>acest utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>această adresă IP de destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>o dată</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1o</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>mereu</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>ID utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Executat din</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>EtichetăText</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Adresă IP sursă</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>ID proces</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>Adresă IP destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Port destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation type="unfinished">ÃŽnchide</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation type="unfinished">Nod</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation type="unfinished">Activează</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation type="unfinished">AcÈ›iune</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation type="unfinished">Șterge</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation type="unfinished">Salvează</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>PreferinÈ›e</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Interfață utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Arată implicit vizualizarea avansată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>o dată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1o</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>mereu</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>AcÈ›iune</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Èšintă implicită</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>Dacă este bifată, ferestrele de notificare care apar vor fi afiÈ™ate cu vizualizarea avansată activă.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>refuză</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>permite</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>după executabil</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>după linia de comandă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>după portul de destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>după adresa IP de destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>după identificatorul utilizatorului</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>centru</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>sus la dreapta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>jos la dreapta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>sus la stânga</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>jos la stânga</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>Durată implicită fereastră de notificare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Durată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Filtrează conexiunile È™i după:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>ID utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Port destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>Adresă IP destinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Dezactivează ferestrele de notificare, arată doar o alertă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Durată implicită pentru alegere</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Noduri</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Metodă monitorizare procese</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>FiÈ™ierul jurnal unde să se scrie jurnalizări.<br/></p><p>/dev/stdout va tipări jurnalizările pe ieÈ™irea standard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>FiÈ™ier de jurnalizare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Durata implicită va fi folosită când nu este conectată nicio interfață grafică.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Durată implicită</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Aplică configuraÈ›ia la toate nodurile</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>AcÈ›iunea implicită va fi folosită când nu este conectată nicio interfață grafică.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>NumeGazdă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>întotdeauna</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Adresă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Versiune</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Nivel implicit de jurnalizare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Bază de date</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Tip bază de date</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Selectare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>ÃŽn memorie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>FiÈ™ier</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>ÃŽnchide</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Aplică</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Salvează</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>Vizualizarea avansată vă permite să selectaÈ›i cu uÈ™urință câmpuri multiple pentru a filtra conexiunile</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>Dacă este bifată, acest câmp va fi selectat când o fereastră de notificare este afiÈ™ată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>AcÈ›iune implicită când interfaÈ›a grafică cu utilizatorul este deconectată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Depanează conexiunile nevalide</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Ferestre de notificare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>OpÈ›iuni implicite</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>CompozziÈ›ia implicită pe ecran</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>oricare regulă temporară</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="450"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="453"/> <source>Don't save rules of duration</source> <translation type="obsolete">Nu salva regulile de durată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Timp</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>DestinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>Proces</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Regulă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Nod</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Coloane etichete evenimente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation type="unfinished">Reguli</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Detalii proces</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>Se încarcă...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: loading...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>Statistici memorie: se încarcă...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Stare</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Deschidere fiÈ™iere</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>Statistici intrare/ieÈ™ire</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>FiÈ™iere cartografiate în memorie</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Stivă</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Variabile de mediu</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Identificatori procese aplicaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>PorneÈ™te sau opreÈ™te monitorizarea acestui proces</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>ÃŽnchide</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Regulă</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Nod</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Aplică regula la toate nodurile</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>Pentru această adresă IP / reÈ›ea</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/cale/către/executabil, .*/bin/executabil[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>AcÈ›iune</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>Pentru acest port</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>Pentru această listă de domenii</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>LAN</source> <translation type="obsolete">ReÈ›ea locală (LAN)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="219"/> <source>127.0.0.0/8</source> <translation type="obsolete">127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="224"/> <source>192.168.0.0/24</source> <translation type="obsolete">192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <source>192.168.1.0/24</source> <translation type="obsolete">192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="234"/> <source>192.168.2.0/24</source> <translation type="obsolete">192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>192.168.0.0/16</source> <translation type="obsolete">192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="244"/> <source>169.254.0.0/16</source> <translation type="obsolete">169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="249"/> <source>172.16.0.0/12</source> <translation type="obsolete">172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/> <source>10.0.0.0/8</source> <translation type="obsolete">10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="259"/> <source>::1/128</source> <translation type="obsolete">::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/> <source>fc00::/7</source> <translation type="obsolete">fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="269"/> <source>ff00::/8</source> <translation type="obsolete">ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="274"/> <source>fe80::/10</source> <translation type="obsolete">fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="279"/> <source>fd00::/8</source> <translation type="obsolete">fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>o dată</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>30s</source> <translation type="obsolete">30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="328"/> <source>5m</source> <translation type="obsolete">5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/> <source>15m</source> <translation type="obsolete">15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>30m</source> <translation type="obsolete">30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>1h</source> <translation type="obsolete">1o</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>întotdeauna</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domeniu.org, .*\.domeniu.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>Pentru această gazdă</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Durată</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="436"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="441"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>De la acest executabil</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>De la această linie de comandă</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>De la acest ID utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Nume</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>Regulile sunt verificate în ordine alfabetică, aÈ™a că puteÈ›i să le numiÈ›i ca atare pentru a le prioritiza. 000-permite-localhost 001-respinge-broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="611"/> <source>leave blank to autocreate</source> <translation type="obsolete">lăsaÈ›i gol pentru creare automată</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Regulă prioritate</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>Sensibil la majuscule</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation type="unfinished">AplicaÈ›ii</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>Statistici de reÈ›ea OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="105"/> <source>Save to CSV</source> <translation type="obsolete">Salvează într-un fiÈ™ier CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="115"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Creare regulă nouă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Stare</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>PorniÈ›i sau opriÈ›i interceptarea</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Evenimente</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filtru</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>De exemplu: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Șterge toate evenimentele de interceptare</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Noduri</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Reguli</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Editare regulă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Șterge regula</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(faceÈ›i clic dublu pe un rând pentru a vizualiza detaliile regulii)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">Căutare nume regulă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Reguli aplicaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation type="unfinished">Permanent</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Temporar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Gazde</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(faceÈ›i clic dublu pentru a vizualiza detaliile unui element)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>AplicaÈ›ii</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Șterge toate aplicaÈ›iile interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Adrese</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Porturi</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Utilizatori</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>Conexiuni</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Aruncate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Durată activitate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Versiune</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="665"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Șterge toate conexiunile care se potrivesc cu această regulă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Toate aplicaÈ›iile</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Șterge toate gazdele interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Șterge toate adresele interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Șterge toate porturile interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Șterge toÈ›i utilizatorii interceptaÈ›i</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>Statistici</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Dezactivează</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Ajutor</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>ÃŽnchide</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation type="unfinished">ConfiguraÈ›ia a fost aplicată.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation type="unfinished">Adresă IP sursă</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation type="unfinished">Avertisment</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>mereu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Conexiune de ieÈ™ire</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>Procesul a fost lansat din:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>de la acest executabil</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>de la această linie de comandă</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>către portul {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>către {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>de la utilizatorul {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>către {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>către *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">către *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation>Procesul %s<b>telecomandat</b> rulează pe <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>se conectează la <b>%s</b> pe %s portul %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>încearcă să rezolve <b>%s</b> prin %s, %s portul %d</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Exception saving config: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Avertisment</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>You must select a file for the database<br>or choose "In memory" type.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>Tipul bazei de date a fost schimbat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>ReporniÈ›i interfaÈ›a grafică cu utilizatorul pentru ca modificările să aibă efect</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Se aplică configuraÈ›ia pe {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>Adresa servitorului nu poate fi goală</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Error loading {0} configuration</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>ConfiguraÈ›ia a fost aplicată.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Eroare la aplicarea configuraÈ›iei: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Eroare la încărcarea informaÈ›iilor procesului:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Eroare la oprirea monitorizării procesului:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>Se încarcă...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>Nu există niciun nod conectat.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>Regulă aplicată.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Eroare la aplicarea regulii: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Eroare la încărcarea regulii</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>Protocolul nu poate fi gol, sau debifaÈ›i-l</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Protocol regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>Calea procesului nu poate fi goală</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Process path regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>Linia de comandă nu poate fi goală</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Command line regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>Dest port can not be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Dst port regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>Dest host can not be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Dst host regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>Dest IP/Network can not be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Dst IP regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>User ID can not be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>User ID regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>Lists field cannot be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>Lists field must be a directory</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Regula nu este sprijinită</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Nu rulează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Dezactivată</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Rulează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch Network Statistics {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch Network Statistics for {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Eroare:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Avertisment:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>ÃŽntotdeauna</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Până la repornire</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Dezactivează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Duplică</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Editare</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Șterge</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> SunteÈ›i pe cale să È™tergeÈ›i aceasă regulă. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Sigur doriÈ›i acest lucru?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Regula nu a putut fi găsită după acel nume È™i nod</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> SunteÈ›i pe cale să È™tergeÈ›i această regulă. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Save as CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nume</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Adresă</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Stare</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nume gazdă</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Versiune</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Reguli</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Timp</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>AcÈ›iune</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Durată</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Nod</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Activată</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Atingeri</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Proces</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DestinaÈ›ie</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Regulă</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>IdentificatorUtilizator</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>UltimaConexiune</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="275"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Argumente</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstHost</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstPort</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation type="unfinished">Atingeri</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Durată activitate</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Conexiuni</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">Aruncate</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reguli</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">AcÈ›iune</translation> </message> </context> </TS> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/ru_RU/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017541�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/ru_RU/opensnitch-ru_RU.ts������������������������������������������0000664�0000000�0000000�00000510222�15003540030�0023317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ru_RU"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>ID пользователÑ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Выполнено из</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>Заметка</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>ИÑходный IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>ID процеÑÑа</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>IP назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Порт назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>из Ñтого иÑполнÑемого файла</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>из Ñтой командной Ñтроки</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>Ñтот порт назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>Ñтот пользователь</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>Ñтот ip-Ð°Ð´Ñ€ÐµÑ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>один раз</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30 Ñекунд</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5 минут</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15 минут</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30 минут</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1 чаÑ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>поÑтоÑнно</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Запретить</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>до перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>из Ñтого PIDа</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>дейÑтвие</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>Фаервол</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Фаервол</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>ВходÑщие</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>ИÑходÑщие</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>Профиль</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation>Разрешить входÑщие ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð½Ð° порт</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation>Разрешить ÑÐµÑ€Ð²Ð¸Ñ (ВХОДЯЩИЕ)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation>ИÑключение иÑходÑщих подключений к порту от перехвата</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation>Разрешить ÑÐµÑ€Ð²Ð¸Ñ (ИСХОДЯЩИЕ)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation>Ðовое правило</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="453"/> <source>xxx</source> <translation type="obsolete">Ñ…Ñ…Ñ…</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation>Закрыть</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>Правило фаервола</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>Узел</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> <source>All</source> <translation type="obsolete">Ð’Ñе</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation>Включить</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation>ОпиÑание</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation>ПроÑто</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation>Добавить новое уÑловие</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation>Удалить выбранное уÑловие</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation>Ðаправление</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation>ВХОДЯЩИЕ</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation>ИСХОДЯЩИЕ</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation>ДейÑтвие</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation>ПРИÐИМÐТЬ</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation>ОТБРÐСЫВÐТЬ</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation>ОТКЛОÐЯТЬ</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation>ВОЗВРÐЩÐТЬ</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation>ОчиÑтить</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation>Удалить</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation>Сохранить</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation>Добавить</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation>ПЕРЕÐÐПРÐВЛЕÐИЕ</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>ÐаÑтройки</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>ПользовательÑкий интерфейÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Тайм-аут по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>ПродолжительноÑть вÑплывающего окна по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>ПродолжительноÑть по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Acción por defecto de la ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Acción por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Цель по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>в центре</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>в правом верхнем углу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>в нижнем правом углу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>в левом верхнем углу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>в нижнем левом углу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posición por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>иÑполнÑемым файлом</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>по командной Ñтроке</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>по порту назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>по IP-адреÑу назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>по ID пользователÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>один раз</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30 Ñекунд</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5 минут</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15 минут</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30 минут</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1 чаÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>поÑтоÑнно</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>запрещено</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>разрешено</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Отключить вÑплывающие окна, отображать только предупреждение</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Узлы</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>Метод мониторинга процеÑÑа</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>ПродолжительноÑть по умолчанию будет иметь меÑто, когда пользовательÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð½Ðµ подключен.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>ÐÐ´Ñ€ÐµÑ ÑƒÐ·Ð»Ð°.</p><p>По умолчанию: unix:///tmp/osui.sock (unix:// ÑвлÑетÑÑ Ð¾Ð±Ñзательным, еÑли Ñто Unix Ñокет) </p><p>Это также может быть IP-Ð°Ð´Ñ€ÐµÑ Ñ Ð¿Ð¾Ñ€Ñ‚Ð¾Ð¼: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>ÐдреÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Уровень Ð»Ð¾Ð³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>ВерÑиÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>ДейÑтвие по умолчанию выполнÑетÑÑ Ð¿Ñ€Ð¸ отÑутÑтвии подключенного пользовательÑкого интерфейÑа.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Лог файл Ð´Ð»Ñ Ð»Ð¾Ð³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ.<br/></p><p>/dev/stdout выводит логи на Ñтандартный вывод.</p></body> </html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Лог файл</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. La ventana emergente sólo contendrá información relativa a la conexión. Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexiones desconocidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>Ð˜Ð¼Ñ Ñ…Ð¾Ñта</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>до перезапуÑка</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>вÑегда</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Применить конфигурацию ко вÑем узлам</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>База данных</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>Ð’ памÑти</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Файл</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Закрыть</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Применить</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Сохранить</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>до перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Тип базы данных</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Выбрать</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posición en pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="102"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Показывать раÑширенный вид по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>ДейÑтвие</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>ЕÑли Ñтот флажок уÑтановлен, вÑплывающие окна будут отображатьÑÑ Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ‹Ð¼ раÑширенным видом.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>ДлительноÑть</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>По умолчанию, когда поÑвлÑетÑÑ Ð½Ð¾Ð²Ð¾Ðµ вÑплывающее окно, в его проÑтейшей форме вы Ñможете фильтровать ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾ одному ÑвойÑтву ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (иÑполнÑемый файл, порт, IP-Ð°Ð´Ñ€ÐµÑ Ð¸ Ñ‚. д.).</p><p>С помощью Ñтих вариантов, вы можете выбрать неÑколько полей Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸ подключений.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>Также фильтровать ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="362"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>ID пользователÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Порт назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>IP назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Этот тайм-аут предÑтавлÑет Ñобой обратный отÑчет, который вы видите, когда отображаетÑÑ Ð²Ñплывающее диалоговое окно.</p><p>ЕÑли вÑплывающее окно не отвечает, будут применены параметры по умолчанию.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>РаÑширенный вид позволÑет легко выбирать неÑколько полей Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸ подключений</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>ЕÑли флажок уÑтановлен, Ñто поле будет выбрано при отображении вÑплывающего окна</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>ДейÑтвие вÑплывающего окна по умолчанию.</p><p>Когда будет уÑтановлено новое иÑходÑщее Ñоединение, Ñто дейÑтвие будет выбрано по умолчанию, поÑтому, еÑли тайм-аут Ñрабатывает , будет применен Ñтот параметр.</p><p><br/></p><p>Когда вÑплывающее окно проÑит Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐ¸Ñ‚ÑŒ или запретить Ñоединение:</p><p >1. новые иÑходÑщие ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€ÐµÑ‰ÐµÐ½Ñ‹.</p><p>2. извеÑтные ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐ°ÑŽÑ‚ÑÑ Ð¸Ð»Ð¸ запрещаютÑÑ Ð½Ð° оÑнове правил, определенных пользователем.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>Обычное дейÑтвие, когда Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Отладка недейÑтвительных Ñоединений</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Ð’Ñплывающие окна</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Параметры по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Положение по умолчанию на Ñкране</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>любые временные правила</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="478"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>ЕÑли выбран Ñтот параметр, правила выбранной продолжительноÑти не будут добавлÑтьÑÑ Ð² ÑпиÑок временных правил в графичеÑком интерфейÑе.</p><p><br/></p><p>Временные правила оÑтанутÑÑ Ð² Ñиле, и вы Ñможете иÑпользовать их, когда будет предложено разрешить/запретить новое подключение.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="481"/> <source>Don't save rules of duration</source> <translation type="obsolete">Ðе ÑохранÑть правила длительноÑти</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source>Show events columns</source> <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>ВремÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Ðазначение</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>Протокол</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>ПроцеÑÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Правило</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Узел</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>EÑли Ñтот флажок уÑтановлен, opensnitch предложит вам разрешить или запретить ÑоединениÑ, не имеющие ÑвÑзанного PID, по неÑкольким причинам, в оÑновном из-за плохого ÑоÑтоÑÐ½Ð¸Ñ Ñоединений.</p> <p>Ð’Ñплывающее диалоговое окно будет Ñодержать только информацию о Ñетевом подключении.</p><p>Ð¥Ð¾Ñ‚Ñ Ð² некоторых ÑценариÑÑ… Ñто дейÑтвительные подключениÑ, например, при уÑтановке VPN Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ wireguard.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Столбцы вкладки "СобытиÑ"</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>по PIDу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation>Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð½Ð° рабочем Ñтоле</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation>ИÑпользовать ÑиÑтемные уведомлениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation>ИÑользовать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Qt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation>ТеÑÑ‚</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation>СиÑтема</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation>Тема</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation>минут</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation>Минут между очиÑткой Ñобытий</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation>дни</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation>МакÑимальное количеÑтво дней Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñобытий</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>отклонÑть</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation>Запретить вÑплывающие окна, показывать только уведомлениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation>ÐšÐ¾Ð¼Ð°Ð½Ð´Ð½Ð°Ñ Ñтрока</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation>Правила</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation>Ðе ÑохранÑть/Удалить правила по продолжительноÑти</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation>Ðе более 30 Ñек</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation>Ðе более 5 мин</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation>Ðе более 15 мин</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation>Ðе более 30 мин</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation>Ðе более 1 чаÑа</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation>Язык</translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>Детали процеÑÑа</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>загрузка...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: загрузка...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>ÑтатиÑтика памÑти: загружаетÑÑ...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>СоÑтоÑние</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Открыть файлы</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>I/O СтатиÑтика</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Файлы Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸ÐµÐ¼ памÑти</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Стек</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Переменные Ñреды</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>PIDÑ‹ приложений</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Ðачать или оÑтановить мониторинг Ñтого процеÑÑа</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Закрыть</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Правило</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Узел</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Применить правило ко вÑем узлам</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Из Ñтой командной Ñтроки</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>Из Ñтого иÑполнÑемого файла</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>ДейÑтвие</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/путь/к/иÑполнÑемому/файлу, .*/bin/executable[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>К Ñтому IP / Сети</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>один раз</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/> <source>30s</source> <translation type="obsolete">30 Ñекунд</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> <source>5m</source> <translation type="obsolete">5 минут</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> <source>15m</source> <translation type="obsolete">15 минут</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> <source>30m</source> <translation type="obsolete">30 минут</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> <source>1h</source> <translation type="obsolete">1 чаÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>вÑегда</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>К Ñтому порту</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>От Ñтого ID пользователÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>ЗапÑтые или пробелы не могут указывать неÑколько доменов. ВмеÑто Ñтого иÑпользуйте регулÑрные выражениÑ: .*(opensnitch|duckduckgo).com .*\.google.com или один домен: www.gnu.org - Ñто будет ÑоответÑтвовать только www.gnu.org, но не ftp.gnu.org, и не www2.gnu.org,... gnu.org - Ñто будет ÑоответÑтвовать только gnu.org, но не www.gnu.org, и не ftp.gnu.org, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Только TCP, UDP или UDPLITE разрешены</p><p>Ð’Ñ‹ можете иÑпользовать regexp: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Ð’Ñ‹ можете указать один IP: - 192.168.1.1 или регулÑрное выражение: - 192\.168\.1\.[0-9]+ неÑколько IP-адреÑов: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Ð’Ñ‹ также можете указать подÑеть: - 192.168.1.0/24 Примечание. ЗапÑтые или пробелы не могут иÑпользоватьÑÑ Ð´Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ IP-адреÑов или Ñетей.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>LAN</source> <translation type="obsolete">LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> <source>127.0.0.0/8</source> <translation type="obsolete">127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>192.168.0.0/24</source> <translation type="obsolete">192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> <source>192.168.1.0/24</source> <translation type="obsolete">192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> <source>192.168.2.0/24</source> <translation type="obsolete">192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> <source>192.168.0.0/16</source> <translation type="obsolete">192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> <source>169.254.0.0/16</source> <translation type="obsolete">169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> <source>172.16.0.0/12</source> <translation type="obsolete">172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> <source>10.0.0.0/8</source> <translation type="obsolete">10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> <source>::1/128</source> <translation type="obsolete">::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> <source>fc00::/7</source> <translation type="obsolete">fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> <source>ff00::/8</source> <translation type="obsolete">ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>fe80::/10</source> <translation type="obsolete">fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>fd00::/8</source> <translation type="obsolete">fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>ДлительноÑть</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>Протокол</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>К Ñтому хоÑту</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Запретить</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">ИмÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>Включить</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>Правила проверÑÑŽÑ‚ÑÑ Ð² алфавитном порÑдке, поÑтому вы можете назвать их ÑоответÑтвующим образом, чтобы раÑÑтавить приоритеты. 000-allow-localhost 001-deny-broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/> <source>leave blank to autocreate</source> <translation type="obsolete">оÑтавьте пуÑтым Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾ÑозданиÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>ЕÑли Ñтот флажок уÑтановлен, Ñто правило будет иметь приоритет над оÑтальными правилами. Ðикакие другие правила не будут проверÑтьÑÑ Ð¿Ð¾Ñле Ñтого. Ð’Ñ‹ должны назвать правило таким образом, чтобы оно проверÑлоÑÑŒ первым, потому что они проверÑÑŽÑ‚ÑÑ Ð² алфавитном порÑдке. Ðапример: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Правило приоритета</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>По умолчанию поле правил не чувÑтвительно к региÑтру, Ñ‚. е. еÑли процеÑÑ Ð¿Ñ‹Ñ‚Ð°ÐµÑ‚ÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к gOOgle.CoM, а у Ð²Ð°Ñ ÐµÑть правило Запретить .*google.com, Ñоединение будет заблокировано.<br/></p><p>ЕÑли вы уÑтановите Ñтот флажок, вы должны указать точную Ñтроку (домен, иÑполнÑемый файл, командную Ñтроку), которую вы хотите отфильтровать.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>ЧувÑтвительно к региÑтру</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Ð’Ñ‹ можете указать неÑколько портов, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ñ€ÐµÐ³ÑƒÐ»Ñрные выражениÑ:</p><p><br/></p><p>- 53, 80 или 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 или 5551, 5552, 5553, итд:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>до перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>К Ñтому ÑпиÑку доменов</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Выберите каталог Ñо ÑпиÑками доменов, которые нужно заблокировать или разрешить.</p><p>ПомеÑтите в Ñтот каталог файлы Ñ Ð»ÑŽÐ±Ñ‹Ð¼ раÑширением, Ñодержащие ÑпиÑки доменов.</p><p><br/>Формат каждой запиÑи ÑпиÑка Ñледующий (формат хоÑтов):</p><p>127.0.0.1 www.domain.com</p><p>или </p><p>0.0.0.0 www.domain.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>Отказ мгновенно разорвёт Ñоединение</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>Отклонение приведет к разрыву ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¸ уничтожению Ñокета, который его инициировал</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Отклонить</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>Разрешить разрешит ÑоединениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>ПриложениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>РегулÑрное выражение</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>Из Ñтого PIDа</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation>регулÑрное выражение</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation>Сеть</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation>СпиÑок доменов/IP-адреÑов</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation>К Ñтому ÑпиÑку Ñетевых диапазонов</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation>К Ñтому ÑпиÑку IP-адреÑов</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation>К Ñтому ÑпиÑку доменов (регулÑрные выражениÑ)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation>Ещё</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> <source>Name (leave blank to autocreate)</source> <translation type="obsolete">Ð˜Ð¼Ñ (при отÑутÑтвии ÑоздаётÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation>ICMP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation>ICMP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation>SCTP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation>SCTP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation>Сетевой интерфейÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation>Ðе логировать ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ ÑоотвтÑвующие Ñтому правилу</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation>Ðе логировать ÑоединениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> <source>Color</source> <translation type="obsolete">Цвет</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation>ОпиÑание...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation>От Ñтого IP или Ñети</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation>От Ñтого порта</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>Ð¡ÐµÑ‚ÐµÐ²Ð°Ñ ÑтатиÑтика OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="284"/> <source>Save to CSV</source> <translation type="obsolete">Сохранить в CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="294"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Создать новое правило</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>СоÑтоÑние</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Ðачать или оÑтановить перехват</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>СобытиÑ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Фильтр</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Запретить</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Ðапример: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Узлы</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните Ñтолбец ÐдреÑа, чтобы проÑмотреть ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð± узле)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Правила</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>включить</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="684"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">иÑкать название правила</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Правила приложений</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>ПоÑтоÑнно</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Временно</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>ХоÑты</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните, чтобы проÑмотреть ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð± Ñлементе)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>ПриложениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>ÐдреÑа</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>Порты</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Пользователи</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>СоединениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Сброшено</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Ð’Ñ€ÐµÐ¼Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>ВерÑиÑ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Удалить вÑе перехваченные ÑобытиÑ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Редактировать правило</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Удалить правило</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Удалить вÑÑŽ информацию о перехваченных хоÑтах</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Удалить вÑÑŽ информацию о перехваченных приложениÑÑ…</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Удалить вÑÑŽ информацию о перехваченных адреÑах</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Удалить вÑÑŽ информацию о перехваченных портах</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Удалить вÑÑŽ информацию о перехваченных пользователÑÑ…</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните Ñтроку, чтобы проÑмотреть ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ правиле)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="665"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Удалить подключениÑ, ÑоответÑтвующие Ñтому правилу</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Ð’Ñе приложениÑ</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Отклонить</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation>Удалить Ñтот узел</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation>Показать наÑтройки Ñтого узла</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation>ЗапуÑтить или оÑтановить перехват Ñтого узла</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation>СиÑтемные правила</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>СтатиÑтика</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Помощь</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Закрыть</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>Включить</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Выключить</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation>ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð°.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation>Ошибка: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation>Применить изменениÑ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation>Ошибка Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ð¸Ñ‚Ð¸ÐºÐ¸ цепочки INPUT</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation>Ошибка Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ð¸Ñ‚Ð¸ÐºÐ¸ цепочки OUTPUT</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation>Чтобы наÑтроить правила фаервола из графичеÑкого интерфейÑа, нам нужно иÑпользовать 'nftables' вмеÑто 'iptables'</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation>Включение фаервола...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation>Выключение фервола...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>Порт назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>ИÑходный порт</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>IP-Ð°Ð´Ñ€ÐµÑ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation>ИÑходный IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation>Входной интерфейÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation>Выходной интерфейÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation>УÑтановить метку ÑоединениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation>Совпадение Ñ Ð¼ÐµÑ‚ÐºÐ¾Ð¹ conntrack</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation>Совпадение Ñ ÑоÑтоÑнием ÑоединениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation>УÑтановить метку на пакете</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation>Совпадение информации о пакете</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation>Квоты пропуÑкной ÑпоÑобноÑти</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation>Ð¡Ð¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸ÐµÐ¼ ÑкороÑти</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation>Ваша верÑÐ¸Ñ protobuf неÑовмеÑтима, необходимо уÑтановить protobuf 3.8.0 или выше (pip3 install --ignore-installed protobuf==3.8.0)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation>Правило удалено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation>Правило добавлено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation>УдалÑем правило, подождите</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation>Ðе удалоÑÑŒ обновить правило</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation>ДобавлÑем правило, подождите</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation>Равно</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation>Ðе равно</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation>Больше или равно чем</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation>Больше чем</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation>Меньше или равно чем</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation>Меньше чем</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation>Правило фаервола</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation>ПроÑто</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation>Сложно</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation>Это правило пока не поддерживаетÑÑ.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation>ИÑключить ÑервиÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation>Разрешить входÑщие Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº выбранному порту.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation>Разрешить иÑходÑщие Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº выбранному порту.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation>значение не может быть 0 или пуÑтым.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation>формат значениÑ: 1024/kbytes (или bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation>формат значениÑ: 1024/kbytes/Ñекунд (или bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation>ограничение ÑкороÑти недейÑтвительно, иÑпользуйте: bytes, kbytes, mbytes или gbytes.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation>ограничение по времени недейÑтвительно, иÑпользуйте: Ñекунды, минуты, чаÑÑ‹ или дни</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation>порт недейÑтвителен.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation>ИнформациÑ</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation>Ошибка</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation>Предупреждение</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>СиÑтемные ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð½ÐµÐ´Ð¾Ñтупны, нужно уÑтановить python3-notify2.</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Запретить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>навÑегда</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>ИÑходÑщее Ñоединение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>ПроцеÑÑ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½ из:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>из Ñтой командной Ñтроки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>из Ñтого иÑполнÑемого файла</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Proceso no encontrado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>до перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>в порт {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>в {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>от Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>в {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>в *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">в *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation><b>Удаленный</b> процеÑÑ %s запущенный на <b>%s</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>подключаетÑÑ Ðº <b>%s</b> через %s порт %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>пытаетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐ¸Ñ‚ÑŒ <b>%s</b> через %s, %s порт %d</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation>Ðовое иÑходÑщее Ñоединение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation>из Ñтого PIDа</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation>ОтклонÑть</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Error al guarda la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Aplicando configuración en %s ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>ÐÐ´Ñ€ÐµÑ Ñервера не может быть пуÑтым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Error al cargar la configuración %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð°.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Error al aplicar la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸Ñключений: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>Применение конфигурации к {0}...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>Ошибка при загрузке конфигурации {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Ошибка Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Предупреждение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Ð’Ñ‹ должны выбрать файл Ð´Ð»Ñ Ð±Ð°Ð·Ñ‹ данных<br>или выбрать тип "Ð’ памÑти".</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>Тип БД изменен</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>ПерезапуÑтите графичеÑкий интерфейÑ, чтобы Ñффекты вÑтупили в Ñилу</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Ðаведите указатель мыши на текÑÑ‚, чтобы отобразить Ñправку<br><br>Ðе забудьте поÑетить вики: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation>СиÑтема</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>Темы недоÑтупны. УÑтановите qt-material: pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation>Тема Ð¾Ñ„Ð¾Ñ€Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð°</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation>ПерезапуÑтите графичеÑкий интерфейÑ, чтобы изменить тему оформлениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation>Ðет подключенных узлов</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation>Хорошо</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> <source>Restart the GUI in order changes to take effect</source> <translation type="obsolete">ПерезапуÑтите графичеÑкий интерфейÑ, чтобы Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ñтупили в Ñилу</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation>ИÑключение ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ узла {0}: {1}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>Ошибка при загрузке информации о процеÑÑе:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>Ошибка при оÑтановке мониторинга процеÑÑа:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>загрузка...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>Ðет подключенных узлов.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>Правило применено.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Error al aplicar la regla: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>протокол не может быть пуÑтым, или Ñнимите галочку</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð°</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>путь процеÑÑа не может быть пуÑтым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿ÑƒÑ‚Ð¸ процеÑÑа</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð°Ñ Ñтрока не может быть пуÑтой</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð¹ Ñтроки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>Порт Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð½Ðµ может быть пуÑтым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñ€Ñ‚Ð° назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>ХоÑÑ‚ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð½Ðµ может быть пуÑтым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ…Ð¾Ñта назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>Целевой IP/Ñеть не могут быть пуÑтыми</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¦ÐµÐ»ÐµÐ²Ð¾Ð¹ IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>Укажите идентификатор пользователÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ ID пользователÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Ошибка Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð°: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>Поле ÑпиÑков не может быть пуÑтым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>Поле ÑпиÑков должно быть каталогом</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Правило не поддерживаетÑÑ</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Ошибка загрузки правила</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation>Правило Ñ Ñ‚Ð°ÐºÐ¸Ð¼ названием уже ÑущеÑтвует.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation>Поле PID не может быть пуÑтым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ñ PID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation>Выберите Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ одно поле.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation>Ðазвание Ñетевого интерфейÑа не может быть пуÑтым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation>Ошибка регулÑрного Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñетевого интерфейÑа</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Ðе запущено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Отключено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Запущено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Ð’Ñ‹ ÑобираетеÑÑŒ удалить Ñто правило. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Ð’Ñ‹ уверены?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch ÑтатиÑтика Ñети {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch ÑтатиÑтика Ñети Ð´Ð»Ñ {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>ПопаданиÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>Сохранить как CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Удалить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Отключить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>Включить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>Дублировать</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Редактировать</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Правило не найдено по Ñтому имени и узлу</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Ошибка:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Предупреждение:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Запретить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Ð’Ñегда</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>До перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Ð’Ñ‹ ÑобираетеÑÑŒ удалить Ñто правило. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <translation type="obsolete">Última Conexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ИмÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ÐдреÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>СоÑтоÑние</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ð˜Ð¼Ñ Ñ…Ð¾Ñта</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ВерÑиÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Правила</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ВремÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ДейÑтвие</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ПродолжительноÑть</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Узел</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Включено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ПоÑещаемоÑть</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Протокол</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ПроцеÑÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ðазначение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Правило</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ID пользователÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ПоÑледнее Ñоединение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Ðргументы</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>IP назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ХоÑÑ‚ назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Порт назначениÑ</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation>Подключен новый узел</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Что</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>Ð˜Ð¼Ñ Ñети</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ð’Ñ€ÐµÐ¼Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Приоритет</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>СоединениÑ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Сброшено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Что</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation>Применить к</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation>Отклонить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ОпиÑание</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ÐšÐ¾Ð¼Ð°Ð½Ð´Ð½Ð°Ñ Ñтрока</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation>ЭкÑпортировать правила</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation>Импортиовать правила</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation>ЭкÑпортиовать ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð² CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation>Выйти</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation>ЭкÑпортировать</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation>Ð’ буфер обмена</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation>Ðа диÑк</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation>Выберите директорию Ð´Ð»Ñ ÑкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation> Ð’Ñ‹ ÑобираетеÑÑŒ удалить Ñту запиÑÑŒ. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation> Ð’Ñ‹ ÑобираетеÑÑŒ удалить Ñтот узел. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation><b>Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÑƒÐ·Ð»Ð°</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation>Ошибка ÑкÑпорта правил</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation>Выберите директорию Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° (JSON) файлов Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð°Ð¼Ð¸</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation>Правила импортированы нормально</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/tr_TR/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017537�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/tr_TR/opensnitch-tr_TR.ts������������������������������������������0000664�0000000�0000000�00000476041�15003540030�0023325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="tr"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>Kullanıcı KimliÄŸi</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">Åžuradan çalıştırıldı</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>Kaynak IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>İşlem KimliÄŸi</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>Hedef IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>Hedef BaÄŸlantı Noktası</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>bu programdan</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>bu komut satırından</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>bu hedef baÄŸlantı noktası</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>bu kullanıcı</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>bu hedef IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>bir kere</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30sn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5dak</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15dak</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30dak</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1sa</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="706"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>sonsuza kadar</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>yeniden baÅŸlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>bu iÅŸlem kimliÄŸinden</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>eylem</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>Güvenlik duvarı</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Güvenlik duvarı</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>Gelen</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>Giden</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>Profil</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation>Bir baÄŸlantı noktasına gelen baÄŸlantılara izin ver</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation>Hizmete izin ver (GELEN)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="397"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation>Bir baÄŸlantı noktasına giden baÄŸlantıları araya girmekten hariç tut</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="406"/> <source>Allow service (OUT)</source> <translation>Hizmete izin ver (GİDEN)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="426"/> <source>New rule</source> <translation>Yeni kural</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="453"/> <source>xxx</source> <translation type="obsolete">xxx</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> <source>Close</source> <translation>Kapat</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>Güvenlik duvarı kuralı</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>Düğüm</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> <source>All</source> <translation type="obsolete">Tümü</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation>EtkinleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation>Açıklama</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation>Basit</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation>Yeni koÅŸul ekle</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation>Seçilen koÅŸulu kaldır</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> <source>Direction</source> <translation>Yön</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> <source>IN</source> <translation>GELEN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> <source>OUT</source> <translation>GİDEN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> <source>Action</source> <translation>Eylem</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> <source>ACCEPT</source> <translation>KABUL ET</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> <source>DROP</source> <translation>BIRAK</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> <source>REJECT</source> <translation>GERİ ÇEVİR</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> <source>RETURN</source> <translation>GERİ DÖN</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> <source>Clear</source> <translation>Temizle</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> <source>Delete</source> <translation>Sil</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> <source>Save</source> <translation>Kaydet</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> <source>Add</source> <translation>Ekle</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> <source>FORWARD</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> <source>PREROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> <source>POSTROUTING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> <source>QUEUE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> <source>DNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> <source>SNAT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> <source>REDIRECT</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>Tercihler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="484"/> <source>UI</source> <translation>Kullanıcı arayüzü</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="54"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Default timeout</source> <translation>Öntanımlı zaman aşımı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>Öntanımlı açılır pencere süresi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="866"/> <source>Default duration</source> <translation>Öntanımlı süre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="162"/> <source>Pop-up default action</source> <translation type="obsolete">Acción por defecto de la ventana emergente</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="483"/> <source>Default action</source> <translation type="obsolete">Acción por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>Öntanımlı hedef</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>merkez</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>saÄŸ üst</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>saÄŸ alt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>sol üst</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>sol alt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="167"/> <source>Prompt dialog default position on screen</source> <translation type="obsolete">Posición por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>programa göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>komut satırına göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>hedef baÄŸlantı noktasına göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>hedef IP'ye göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>kullanıcı kimliÄŸine göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="970"/> <source>once</source> <translation>bir kere</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30sn</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5dak</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15dak</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30dak</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1sa</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>for this session</source> <translation type="obsolete">durante esta sesión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>sonsuza kadar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> <source>deny</source> <translation>reddet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> <source>allow</source> <translation>izin ver</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="406"/> <source>Disable pop-ups, only display an alert</source> <translation type="obsolete">Açılır pencereleri devre dışı bırak, yalnızca bir uyarı görüntüle</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="823"/> <source>Nodes</source> <translation>Düğümler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="829"/> <source>Process monitor method</source> <translation>İşlem izleme yöntemi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Öntanımlı süre, baÄŸlı bir kullanıcı arayüzü olmadığında gerçekleÅŸecektir.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="988"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>Düğümün adresi.</p><p>Öntanımlı: unix:///tmp/osui.sock (Unix soketi ise unix:// zorunludur)</p><p>BaÄŸlantı noktası ile birlikte bir IP adresi de olabilir: 127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="991"/> <source>Address</source> <translation>Adres</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> <source>Default log level</source> <translation>Öntanımlı günlük kaydı düzeyi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Version</source> <translation>Sürüm</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="902"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>Öntanımlı eylem, baÄŸlı bir kullanıcı arayüzü olmadığında gerçekleÅŸecektir.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="846"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>Günlük kayıtlarının yazılacağı günlük dosyası.<br/></p><p>/dev/stdout standart çıktıya yazdıracaktır.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="849"/> <source>Log file</source> <translation>Günlük kaydı dosyası</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="578"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. La ventana emergente sólo contendrá información relativa a la conexión. Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>Intercept Unknown Connections</source> <translation type="obsolete">Interceptar conexiones desconocidas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="921"/> <source>HostName</source> <translation>Ana makine adı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="975"/> <source>until restart</source> <translation>yeniden baÅŸlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="980"/> <source>always</source> <translation>her zaman</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="879"/> <source>Apply configuration to all nodes</source> <translation>Yapılandırmayı tüm düğümlere uygula</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> <source>Database</source> <translation>Veri tabanı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> <source>In memory</source> <translation>Bellekte</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> <source>File</source> <translation>Dosya</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> <source>Close</source> <translation>Kapat</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> <source>Apply</source> <translation>Uygula</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> <source>Save</source> <translation>Kaydet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>yeniden baÅŸlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> <source>Database type</source> <translation>Veri tabanı türü</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> <source>Select</source> <translation>Seç</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="83"/> <source>Pop-ups default options</source> <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="367"/> <source>Pop-ups default position on screen</source> <translation type="obsolete">Posición en pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="102"/> <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>Öntanımlı olarak geliÅŸmiÅŸ görünümü göster</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="653"/> <source>Action</source> <translation>Eylem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>İşaretlenirse, açılır pencereler geliÅŸmiÅŸ görünüm etkinken görüntülenecektir.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>Süre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>Öntanımlı olarak, yeni bir açılır pencere göründüğünde, en basit haliyle, baÄŸlantıları veya uygulamaları baÄŸlantının bir özelliÄŸine göre (program, baÄŸlantı noktası, IP, vb.) filtreleyebileceksiniz.</p><p>Bu seçeneklerle, baÄŸlantıları filtrelemek için birden fazla alan seçebilirsiniz.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>BaÄŸlantıları ÅŸuna göre de filtrele:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="362"/> <source>If checked, this field will be checked when a pop-up is displayed</source> <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>Kullanıcı kimliÄŸi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>Hedef baÄŸlantı noktası</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>Hedef IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>Bu zaman aşımı, bir açılır iletiÅŸim kutusu gösterildiÄŸinde gördüğünüz geri sayımdır.</p><p>Açılır pencereye yanıt verilmezse, öntanımlı seçenekler uygulanacaktır.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>GeliÅŸmiÅŸ görünüm, baÄŸlantıları filtrelemek için birden fazla alanı kolayca seçmenize olanak tanır</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>İşaretlenirse, bir açılır pencere görüntülendiÄŸinde bu alan seçilecektir</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>Açılır pencere öntanımlı eylemi.</p><p>Yeni bir giden baÄŸlantı kurulmak üzereyken, bu eylem öntanımlı olarak seçilecektir, bu nedenle zaman aşımı devreye girerse, uygulanacak seçenek budur.</p><p><br/></p><p>Bir açılır pencere kullanıcıdan bir baÄŸlantıya izin vermesini veya reddetmesini isterken:</p><p>1. yeni giden baÄŸlantılar reddedilir.</p><p>2. bilinen baÄŸlantılara kullanıcı tarafından tanımlanan kurallara göre izin verilir veya reddedilir.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>Default action when the GUI is disconnected</source> <translation>GUI baÄŸlantısı kesildiÄŸinde öntanımlı eylem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>Debug invalid connections</source> <translation>Geçersiz baÄŸlantılarda hata ayıkla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>Açılır pencereler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>Öntanımlı seçenekler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>Ekranda öntanımlı konum</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>any temporary rules</source> <translation>herhangi bir geçici kural</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="478"/> <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Bu seçenek seçildiÄŸinde, seçilen sürenin kuralları grafiksel kullanıcı arayüzündeki geçici kurallar listesine eklenmeyecektir.</p><p><br/></p><p>Geçici kurallar hala geçerli olacaktır ve yeni bir baÄŸlantıya izin vermeniz/reddetmeniz istendiÄŸinde bunları kullanabilirsiniz.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="481"/> <source>Don't save rules of duration</source> <translation type="obsolete">Süre kurallarını kaydetme</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="463"/> <source>Show events columns</source> <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Time</source> <translation>Zaman</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="669"/> <source>Destination</source> <translation>Hedef</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="637"/> <source>Protocol</source> <translation>İletiÅŸim kuralı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Process</source> <translation>İşlem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Rule</source> <translation>Kural</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="621"/> <source>Node</source> <translation>Düğüm</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="723"/> <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> <translation type="obsolete"><html><head/><body><p>İşaretlenirse, opensnitch, çoÄŸunlukla kötü durumdaki baÄŸlantılar olmak üzere çeÅŸitli nedenlerden dolayı, atanmış bir iÅŸlem kimliÄŸine sahip olmayan baÄŸlantılara izin vermenizi veya reddetmenizi isteyecektir.</p><p>Açılır iletiÅŸim kutusu yalnızca aÄŸ baÄŸlantısı hakkında bilgi içerecektir.</p><p>Yine de, örneÄŸin wireguard kullanarak bir VPN kurarken olduÄŸu gibi, bunların geçerli baÄŸlantılar olduÄŸu bazı durumlar vardır.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="577"/> <source>Events tab columns</source> <translation>Olaylar sekmesi sütunları</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>iÅŸlem kimliÄŸine göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="obsolete">Açılır pencereleri devre dışı bırak, yalnızca bir bildirim görüntüle</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Desktop notifications</source> <translation>Masaüstü bildirimleri</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="514"/> <source>Use system notifications</source> <translation>Sistem bildirimlerini kullan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="530"/> <source>Use Qt notifications</source> <translation>Qt bildirimlerini kullan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="559"/> <source>Test</source> <translation>Test</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="716"/> <source>System</source> <translation>Sistem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="708"/> <source>Theme</source> <translation>Tema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="998"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation><html><head/><body><p>İşaretlenirse OpenSnitch, çoÄŸunlukla kötü durumdaki baÄŸlantılar olmak üzere çeÅŸitli nedenlerden dolayı iliÅŸkili bir iÅŸlem kimliÄŸine sahip olmayan baÄŸlantılara izin vermenizi veya reddetmenizi isteyecektir.</p><p>Açılır iletiÅŸim kutusu yalnızca aÄŸ baÄŸlantısı hakkında bilgi içerecektir.</p><p>WireGuard kullanarak bir VPN kurarken olduÄŸu gibi, bunların geçerli baÄŸlantılar olduÄŸu bazı senaryolar vardır.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>minutes</source> <translation>dakika</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> <source>Minutes between events purges</source> <translation>Olay temizlemeleri arasındaki dakikalar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> <source>days</source> <translation>gün</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> <source>Maximum days of events to keep</source> <translation>Saklanacak azami etkinlik günü sayısı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>geri çevir</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="473"/> <source>Disable pop-ups, only display a notification</source> <translation>Açılır pencereleri devre dışı bırak, yalnızca bir bildirim görüntüle</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>Command line</source> <translation>Komut satırı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Rules</source> <translation>Kurallar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="756"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation>Bu seçenek seçildiÄŸinde, seçilen sürenin kuralları grafiksel arayüzdeki geçici kurallar listesine eklenmeyecektir. Geçici kurallar geçerli olmaya devam edecektir ve yeni bir baÄŸlantıya izin vermeniz/vermemeniz istendiÄŸinde bunları kullanabilirsiniz.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="761"/> <source>Don't save/Delete rules of duration</source> <translation>Süre kurallarını kaydetme/silme</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="779"/> <source>30s or less</source> <translation>30sn veya daha az</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="784"/> <source>5m or less</source> <translation>5dak veya daha az</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="789"/> <source>15m or less</source> <translation>15dak veya daha az</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="794"/> <source>30m or less</source> <translation>30dak veya daha az</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="799"/> <source>1h or less</source> <translation>1sa veya daha az</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="724"/> <source>Language</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>İşlem ayrıntıları</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>yükleniyor...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD: yükleniyor...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>bellek istatistikleri: yükleniyor...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>Durum</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>Açık dosyalar</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>G/Ç İstatistikleri</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>Bellek eÅŸlemeli dosyalar</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>Yığın</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>Ortam deÄŸiÅŸkenleri</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>Uygulama iÅŸlem kimlikleri</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>Bu iÅŸlemi izlemeyi baÅŸlat veya durdur</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>Kapat</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>Kural</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>Düğüm</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>Kuralı tüm düğümlere uygula</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>Bu komut satırından</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> <source>From this executable</source> <translation>Bu programdan</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>Eylem</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> <translation type="obsolete">/programın/yolu, .*/bin/program[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>To this IP / Network</source> <translation>Bu IP'ye / AÄŸa</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>bir kere</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/> <source>30s</source> <translation type="obsolete">30sn</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> <source>5m</source> <translation type="obsolete">5dak</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> <source>15m</source> <translation type="obsolete">15dak</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> <source>30m</source> <translation type="obsolete">30dak</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> <source>1h</source> <translation type="obsolete">1sa</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <source>until restart</source> <translation type="obsolete">hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>her zaman</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> <source>To this port</source> <translation>Bu baÄŸlantı noktasına</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>Bu kullanıcı kimliÄŸinden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation>Birden fazla etki alanı belirtmek için virgül veya boÅŸluk kullanılmasına izin verilmez. Bunun yerine düzenli ifadeler kullanın: .*(opensnitch|duckduckgo).com .*\.google.com veya tek bir etki alanı: www.gnu.org - yalnızca www.gnu.org ile eÅŸleÅŸir, ftp.gnu.org, www2.gnu.org vb. ile eÅŸleÅŸmez. gnu.org - yalnızca gnu.org ile eÅŸleÅŸir, www.gnu.org, ftp.gnu.org vb. ile eÅŸleÅŸmez.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.etkialani.org, .*\.etkialani.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>Yalnızca TCP, UDP veya UDPLITE izin verilir</p><p>Düzenli ifade kullanabilirsiniz, örn: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> <source>UDP</source> <translation type="obsolete">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> <source>UDPLITE</source> <translation type="obsolete">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>TCP6</source> <translation type="obsolete">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDP6</source> <translation type="obsolete">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>UDPLITE6</source> <translation type="obsolete">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>Tek bir IP belirtebilirsiniz: - 192.168.1.1 veya düzenli bir ifade: - 192\.168\.1\.[0-9]+ birden fazla IP: - ^(192\.168\.1\.1|172\.16\.0\.1)$ Ayrıca bir alt aÄŸ da belirtebilirsiniz: - 192.168.1.0/24 Not: IP veya aÄŸları ayırmak için virgüllere veya boÅŸluklara izin verilmez.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> <source>LAN</source> <translation type="obsolete">LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> <source>127.0.0.0/8</source> <translation type="obsolete">127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>192.168.0.0/24</source> <translation type="obsolete">192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> <source>192.168.1.0/24</source> <translation type="obsolete">192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> <source>192.168.2.0/24</source> <translation type="obsolete">192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> <source>192.168.0.0/16</source> <translation type="obsolete">192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> <source>169.254.0.0/16</source> <translation type="obsolete">169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> <source>172.16.0.0/12</source> <translation type="obsolete">172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> <source>10.0.0.0/8</source> <translation type="obsolete">10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> <source>::1/128</source> <translation type="obsolete">::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> <source>fc00::/7</source> <translation type="obsolete">fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> <source>ff00::/8</source> <translation type="obsolete">ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>fe80::/10</source> <translation type="obsolete">fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>fd00::/8</source> <translation type="obsolete">fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>Süre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> <source>Protocol</source> <translation>İletiÅŸim kuralı</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> <source>To this host</source> <translation>Bu ana makineye</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation type="unfinished">Ad</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>EtkinleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>Kurallar alfabetik sıraya göre denetlenir, böylece onları önceliklendirmek için uygun ÅŸekilde adlandırabilirsiniz. 000-localhost-izinver 001-genelyayin-reddet ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/> <source>leave blank to autocreate</source> <translation type="obsolete">otomatik oluÅŸturmak için boÅŸ bırakın</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>İşaretlenirse, bu kural diÄŸer kurallara göre öncelikli olacaktır. Bundan sonra baÅŸka hiçbir kural denetlenmeyecektir. Alfabetik sıraya göre denetlendikleri için kuralı önce denetlenecek ÅŸekilde adlandırmalısınız. ÖrneÄŸin: [x] Öncelik - 000-oncelik-kurali [ ] Öncelik - 001-daha-az-oncelik-kurali</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>Öncelik kuralı</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>Öntanımlı olarak, kuralların alanı büyük/küçük harfe duyarsızdır, yani bir iÅŸlem gOOgle.CoM adresine eriÅŸmeye çalışırsa ve .*google.com adresini Reddetmek için bir kuralınız varsa, baÄŸlantı engellenecektir.<br/></p><p>Bu kutuyu iÅŸaretlerseniz, filtrelemek istediÄŸiniz dizgeyi (etki alanı, program, komut satırı) tam olarak belirtmeniz gerekir.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> <source>Case-sensitive</source> <translation>Büyük/küçük harfe duyarlı</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Düzenli ifadeler kullanarak birden fazla baÄŸlantı noktası belirtebilirsiniz:</p><p><br/></p> <p> - 53, 80 veya 443:</p><p>^(53|80|443)$</p><p><br/></p> <p> - 53, 443 veya 5551, 5552, 5553, vs:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>yeniden baÅŸlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> <source>To this list of domains</source> <translation>Bu etki alanı listesine</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> <translation type="obsolete"><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının listelerini içeren bir dizin seçin.</p><p>Etki alanı listelerini içeren herhangi bir uzantıya sahip dosyaları bu dizinin içine yerleÅŸtirin.</p><p><br/>Bir listenin her girdisinin biçimi ÅŸu ÅŸekildedir (hosts dosyası biçimi):</p><p>127.0.0.1 www.etkialani.com</p><p>veya </p><p>0.0.0.0 www.etkialani.com</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>Reddet seçeneÄŸi yalnızca baÄŸlantıyı iptal edecektir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>Geri çevir seçeneÄŸi baÄŸlantıyı kesecek ve onu baÅŸlatan soketi sonlandıracaktır</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>Geri çevir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>İzin ver seçeneÄŸi baÄŸlantıya izin verecektir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>Uygulamalar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation><html><head/><body><p>Bu alanın deÄŸeri her zaman program dosyasının mutlak yoludur: /programın/yolu<br/></p><p>Örnekler:</p><p>- Basit: /programın/yolu</p><p>- Birden çok yol: ^/usr/lib(64|)/firefox/firefox$</p><p>- Birden fazla program: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- /tmp'den çalıştırmaları reddet/izin ver:</p><p>^/(var/|)tmp/.*$<br/></p><p>Daha fazla örnek için <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki sayfasını</a> ziyaret edin veya <a href="https://github.com/evilsocket/opensnitch/discussions">Tartışma forumlarında</a> sorun.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>Düzenli ifadedir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation><html><head/><body><p>Bu alan kullanıcı tarafından çalıştırılan komut satırını içerecek ve eÅŸleÅŸecektir.<br/></p><p>Kullanıcı komutu yazdıysa, yalnızca komut görünecektir:</p><p>telnet 1.2.3.4<br/></p><p>Kullanıcı komutun mutlak veya göreceli yolunu yazdıysa, görünecek olan budur:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>Bu iÅŸlem kimliÄŸinden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>is regular expression</source> <translation>düzenli ifadedir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> <source>Network</source> <translation>AÄŸ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>List of domains/IPs</source> <translation>Etki alanlarının/IP'lerin listesi</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> <source>To this list of network ranges</source> <translation>Bu aÄŸ aralıkları listesine</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> <source>To this list of IPs</source> <translation>Bu IP listesine</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Engellenecek veya izin verilecek IP'lerin listesini içeren dosyaların bulunduÄŸu bir dizin seçin:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>vb.</p><p>Satır başına bir IP. BoÅŸ veya # ile baÅŸlayan satırlar dikkate alınmaz.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Engellenecek veya izin verilecek aÄŸ aralıklarının listesini içeren dosyaların bulunduÄŸu bir dizin seçin:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>vb.<br/></p><p>Satır başına bir aÄŸ aralığı. BoÅŸ veya # ile baÅŸlayan satırlar dikkate alınmaz.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının listelerini içeren bir dizin seçin.</p><p>Etki alanı listelerini içeren herhangi bir uzantıya sahip dosyaları bu dizinin içine yerleÅŸtirin.</p><p><br/>Bir listenin her girdisinin biçimi ÅŸu ÅŸekildedir (hosts dosyası biçimi):</p><p>127.0.0.1 www.etkialani.com</p><p>veya </p><p>0.0.0.0 www.etkialani.com</p><p>BoÅŸ veya # ile baÅŸlayan satırlar dikkate alınmaz.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> <source>To this list of domains (regular expressions)</source> <translation>Bu etki alanları listesine (düzenli ifadeler)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının düzenli ifadelerini içeren dosyaları içeren bir dizin seçin:</p><p>.*\.ornek\.com</p><p>Bir etki alanını olduÄŸu gibi de kullanabilirsiniz: &quot;ornek.com&quot;, bu herhangibirsey.ornek.com, herhangibirsey.ornek.com.localdomain, vb. ile eÅŸleÅŸecektir.</p><p>Satır başına bir etki alanı. BoÅŸ veya # ile baÅŸlayan satırlar dikkate alınmaz.</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source>More</source> <translation>Daha fazla</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> <source>Name (leave blank to autocreate)</source> <translation type="obsolete">Ad (otomatik oluÅŸturmak için boÅŸ bırakın)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>ICMP</source> <translation>ICMP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> <source>ICMP6</source> <translation>ICMP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> <source>SCTP</source> <translation>SCTP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> <source>SCTP6</source> <translation>SCTP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> <source>Network interface</source> <translation>AÄŸ arayüzü</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> <source>Don't log connections that match this rule</source> <translation>Bu kuralla eÅŸleÅŸen baÄŸlantıları günlüğe kaydetme</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> <source>Don't log connections</source> <translation>BaÄŸlantıları günlüğe kaydetme</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> <source>Color</source> <translation type="obsolete">Renk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> <source>Description...</source> <translation>Açıklama...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> <source>From this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> <source>From this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation type="unfinished"></translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch AÄŸ İstatistikleri</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="284"/> <source>Save to CSV</source> <translation type="obsolete">CSV olarak kaydet.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="294"/> <source>Ctrl+S</source> <translation type="obsolete">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>Yeni bir kural oluÅŸtur</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">ana makine adı - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>Durum</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1793"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>Araya girmeyi baÅŸlat veya durdur</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>Olaylar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>Filtrele</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>Örn: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="826"/> <source>Nodes</source> <translation>Düğümler</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="554"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir düğümün ayrıntılarını görüntülemek için Adres sütununa çift tıklayın)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1700"/> <source>Rules</source> <translation>Kurallar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="995"/> <source>enable</source> <translation>etkinleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="684"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="692"/> <source>search rule name</source> <translation type="obsolete">kural adı ara</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Application rules</source> <translation>Uygulama kuralları</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <source>Permanent</source> <translation>Kalıcı</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="945"/> <source>Temporary</source> <translation>Geçici</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1063"/> <source>Hosts</source> <translation>Ana makineler</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1364"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir ögenin ayrıntılarını görüntülemek için çift tıklayın)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1153"/> <source>Applications</source> <translation>Uygulamalar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1266"/> <source>Addresses</source> <translation>Adresler</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1356"/> <source>Ports</source> <translation>BaÄŸlantı noktaları</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1440"/> <source>Users</source> <translation>Kullanıcılar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1544"/> <source>Connections</source> <translation>BaÄŸlantılar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1596"/> <source>Dropped</source> <translation>Bırakıldı</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1648"/> <source>Uptime</source> <translation>Çalışma süresi</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1767"/> <source>Version</source> <translation>Sürüm</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>Araya girilen tüm olayları sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1022"/> <source>Edit rule</source> <translation>Kuralı düzenle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1039"/> <source>Delete rule</source> <translation>Kuralı sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="926"/> <source>Delete all intercepted hosts</source> <translation type="obsolete">Araya girilen tüm ana makineleri sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation type="obsolete">Araya girilen tüm uygulamaları sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation type="obsolete">Araya girilen tüm adresleri sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation type="obsolete">Araya girilen tüm baÄŸlantı noktalarını sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation type="obsolete">Araya girilen tüm kullanıcıları sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="699"/> <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir kuralın ayrıntılarını görüntülemek için bir satıra çift tıklayın)</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="665"/> <source>Delete connections that matched this rule</source> <translation type="obsolete">Bu kuralla eÅŸleÅŸen baÄŸlantıları sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="927"/> <source>All applications</source> <translation>Tüm uygulamalar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>Geri çevir</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="637"/> <source>Delete this node</source> <translation>Bu düğümü sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="653"/> <source>Show the preferences of this node</source> <translation>Bu düğümün tercihlerini göster</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="669"/> <source>Start or stop interception of this node</source> <translation>Bu düğümün araya girmesini baÅŸlat veya durdur</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="777"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="954"/> <source>System rules</source> <translation>Sistem kuralları</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Statistics</source> <translation>İstatistikler</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Help</source> <translation>Yardım</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Close</source> <translation>Kapat</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Enable</source> <translation>EtkinleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Disable</source> <translation>Devre dışı bırak</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> <source>Configuration applied.</source> <translation>Yapılandırma uygulandı.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> <source>Error: {0}</source> <translation>Hata: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> <source>Applying changes...</source> <translation>DeÄŸiÅŸiklikler uygulanıyor...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> <source>Error getting INPUT chain policy</source> <translation>INPUT zinciri politikası alınırken hata oluÅŸtu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> <source>Error getting OUTPUT chain policy</source> <translation>OUTPUT zinciri politikası alınırken hata oluÅŸtu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation>Grafiksel arayüzden güvenlik duvarı kurallarını yapılandırmak için 'iptables' yerine 'nftables' kullanmamız gerekir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> <source>Enabling firewall...</source> <translation>Güvenlik duvarı etkinleÅŸtiriliyor...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> <source>Disabling firewall...</source> <translation>Güvenlik duvarı devre dışı bırakılıyor...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>Hedef BaÄŸlantı Noktası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>Kaynak BaÄŸlantı Noktası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>Hedef IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation>Kaynak IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation>GiriÅŸ arayüzü</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation>Çıkış arayüzü</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation>conntrack iÅŸaretini ayarla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation>conntrack iÅŸaretini eÅŸleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation>conntrack durum(lar)ını eÅŸleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation>Paket üzerinde iÅŸareti ayarla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation>Paket bilgilerini eÅŸleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation>Bant geniÅŸliÄŸi kotaları</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation>BaÄŸlantı hızlarını sınırla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation>protobuf sürümünüz uyumsuz, protobuf 3.8.0 veya üstünü kurmanız gerekiyor (pip3 install --ignore-installed protobuf==3.8.0)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> <source>Rule deleted</source> <translation>Kural silindi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> <source>Rule added</source> <translation>Kural eklendi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation>Birden fazla baÄŸlantı noktası/IP veya aralık/deÄŸer belirtmek için ',' veya '-' kullanabilirsiniz:<br><br>baÄŸlantı noktaları: 22 veya 22,443 veya 50000-60000<br>IP adresleri: 192.168.1.1 veya 192.168.1.30-192.168 .1.130<br>DeÄŸerler: echo-reply,echo-request<br>DeÄŸerler: new,established,related</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> <source>Deleting rule, wait</source> <translation>Kural siliniyor, bekleyin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> <source>Error updating rule</source> <translation>Kural güncellenirken hata oluÅŸtu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> <source>Adding rule, wait</source> <translation>Kural ekleniyor, bekleyin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> <source><select a statement></source> <translation><bir ifade seçin></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> <source>Equal</source> <translation>EÅŸit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> <source>Not equal</source> <translation>EÅŸit deÄŸil</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> <source>Greater or equal than</source> <translation>Büyük veya eÅŸit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> <source>Greater than</source> <translation>Büyük</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> <source>Less or equal than</source> <translation>Küçük veya eÅŸit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> <source>Less than</source> <translation>Küçük</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> <source>Firewall rule</source> <translation>Güvenlik duvarı kuralı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> <source>Simple</source> <translation>Basit</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> <source>Advanced</source> <translation>GeliÅŸmiÅŸ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> <source>This rule is not supported yet.</source> <translation>Bu kural henüz desteklenmiyor.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> <source>Exclude service</source> <translation>Hizmeti hariç tut</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> <source>Allow inbound connections to the selected port.</source> <translation>Seçilen baÄŸlantı noktasına gelen baÄŸlantılara izin ver.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> <source>Allow outbound connections to the selected port.</source> <translation>Seçilen baÄŸlantı noktasına giden baÄŸlantılara izin ver.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> <source>select a statement.</source> <translation>bir ifade seçin.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> <source>value cannot be 0 or empty.</source> <translation>deÄŸer 0 veya boÅŸ olamaz.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation>deÄŸer biçimi 1024/kbayttır (veya bayt, mbayt, gbayt)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation>deÄŸer biçimi 1024/kbayt/saniyedir (veya bayt, mbayt, gbayt)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation>hız sınırı geçerli deÄŸil, ÅŸunları kullanın: bayt, kbayt, mbayt veya gbayt.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation>zaman sınırı geçerli deÄŸil, ÅŸunları kullanın: saniye, dakika, saat veya gün</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> <source>port not valid.</source> <translation>baÄŸlantı noktası geçerli deÄŸil.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> <source>Match output interface. Regular expressions not allowed.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> <source>Print a message when this rule matches a packet.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> <source>num</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> <source>to</source> <translation type="unfinished"></translation> </message> </context> <context> <name>menu_close</name> <message> <location filename="../../../opensnitch/service.py" line="131"/> <source>Close</source> <translation type="obsolete">Cerrar</translation> </message> </context> <context> <name>menu_help</name> <message> <location filename="../../../opensnitch/service.py" line="126"/> <source>Help</source> <translation type="obsolete">Ayuda</translation> </message> </context> <context> <name>menu_statistics</name> <message> <location filename="../../../opensnitch/service.py" line="120"/> <source>Statistics</source> <translation type="obsolete">Eventos</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="281"/> <source>Info</source> <translation>Bilgi</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="285"/> <source>Error</source> <translation>Hata</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="289"/> <source>Warning</source> <translation>Uyarı</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>Sistem bildirimleri kullanılamıyor, python3-notify2 kurmanız gerekiyor.</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>forever</source> <translation>sonsuza kadar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> <source>Outgoing connection</source> <translation>Giden baÄŸlantı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> <source>Process launched from:</source> <translation>İşlem ÅŸuradan baÅŸlatıldı:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this command line</source> <translation>bu komut satırından</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> <source>from this executable</source> <translation>bu programdan</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> <source>Unknown process</source> <translation type="obsolete">Proceso no encontrado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> <source>until reboot</source> <translation>yeniden baÅŸlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> <source>to port {0}</source> <translation>{0} baÄŸlantı noktasına</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> <source>to {0}</source> <translation>{0} hedefine</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> <source>from user {0}</source> <translation>{0} kullanıcısından</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> <source>to {0}.*</source> <translation>{0}.* hedefine</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> <source>to *.{0}</source> <translation>*.{0} hedefine</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation type="obsolete">*{0} hedefine</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation>%s <b>uzak</b> iÅŸlemi, <b>%s</b> üzerinde çalışıyor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation><b>%s</b> hedefine baÄŸlanıyor, %s baÄŸlantı noktası %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation><b>%s</b> çözümlemeye çalışıyor, %s aracılığıyla, %s baÄŸlantı noktası %d</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="122"/> <source>New outgoing connection</source> <translation>Yeni giden baÄŸlantı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from this PID</source> <translation>bu iÅŸlem kimliÄŸinden</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> <source>Reject</source> <translation>Geri çevir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> <source>is connecting to <b>%s</b>, %s</source> <translation><b>%s</b> hedefine baÄŸlanıyor, %s</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation type="unfinished"></translation> </message> </context> <context> <name>popups2</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> <source>Exception saving config: %s</source> <translation type="obsolete">Error al guarda la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> <source>Applying configuration on %s ...</source> <translation type="obsolete">Aplicando configuración en %s ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> <source>Server address can not be empty</source> <translation>Sunucu adresi boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> <source>Error loading %s configuration</source> <translation type="obsolete">Error al cargar la configuración %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> <source>Configuration applied.</source> <translation>Yapılandırma uygulandı.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> <source>Error applying configuration: %s</source> <translation type="obsolete">Error al aplicar la configuración: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> <source>Exception saving config: {0}</source> <translation>Yapılandırma kaydedilirken istisna oluÅŸtu: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> <source>Applying configuration on {0} ...</source> <translation>{0} üzerinde yapılandırma uygulanıyor...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> <source>Error loading {0} configuration</source> <translation>{0} yapılandırması yüklenirken hata oluÅŸtu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> <source>Error applying configuration: {0}</source> <translation>Yapılandırma uygulanırken hata oluÅŸtu: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>Warning</source> <translation>Uyarı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>Veri tabanı için bir dosya seçmelisiniz<br>veya "Bellekte" türünü seçmelisiniz.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> <source>DB type changed</source> <translation>V.T. türü deÄŸiÅŸti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> <source>Restart the GUI in order effects to take effect</source> <translation>DeÄŸiÅŸikliklerin etkili olabilmesi için grafiksel arayüzü yeniden baÅŸlatın</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>Yardımı görüntülemek için fareyi metinlerin üzerine getirin<br><br>Wiki sayfasını ziyaret etmeyi unutmayın: <a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> <source>System</source> <translation>Sistem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>Temalar kullanılamıyor. qt-material kurun: pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>UI theme changed</source> <translation>Kullanıcı arayüzü teması deÄŸiÅŸtirildi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> <source>Restart the GUI in order to apply the new theme</source> <translation>Yeni temayı uygulamak için grafiksel arayüzü yeniden baÅŸlatın</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> <source>There're no nodes connected</source> <translation>BaÄŸlı düğüm yok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> <source>Ok</source> <translation>Tamam</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> <source>Restart the GUI in order changes to take effect</source> <translation type="obsolete">DeÄŸiÅŸikliklerin etkili olması için grafiksel arayüzü yeniden baÅŸlatın</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> <source>Exception saving node config {0}: {1}</source> <translation>{0} düğüm yapılandırması kaydedilirken hata oluÅŸtu: {1}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> <source>System default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> <source>Language changed</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>İşlem bilgileri yüklenirken hata oluÅŸtu:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>İşlemin izlenmesi durdurulurken hata oluÅŸtu:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>yükleniyor...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> <source>There're no nodes connected.</source> <translation>BaÄŸlı düğüm yok.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> <source>Rule applied.</source> <translation>Kural uygulandı.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> <source>Error applying rule: %s</source> <translation type="obsolete">Error al aplicar la regla: %s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> <source>protocol can not be empty, or uncheck it</source> <translation>iletiÅŸim kuralı boÅŸ olamaz veya iÅŸaretini kaldırın</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> <source>Protocol regexp error</source> <translation>İletiÅŸim kuralı düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> <source>process path can not be empty</source> <translation>iÅŸlem yolu boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> <source>Process path regexp error</source> <translation>İşlem yolu düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> <source>command line can not be empty</source> <translation>komut satırı boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> <source>Command line regexp error</source> <translation>Komut satırı düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> <source>Dest port can not be empty</source> <translation>Hedef baÄŸlantı noktası boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> <source>Dst port regexp error</source> <translation>Hedef baÄŸlantı noktası düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Dest host can not be empty</source> <translation>Hedef ana makine boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> <source>Dst host regexp error</source> <translation>Hedef ana makine düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> <source>Dest IP/Network can not be empty</source> <translation>Hedef IP/AÄŸ boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> <source>Dst IP regexp error</source> <translation>Hedef IP düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> <source>User ID can not be empty</source> <translation>Kullanıcı kimliÄŸi boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> <source>User ID regexp error</source> <translation>Kullanıcı kimliÄŸi düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> <source>Error applying rule: {0}</source> <translation>Kural uygulanırken hata oluÅŸtu: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> <source>Lists field cannot be empty</source> <translation>Listeler alanı boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> <source>Lists field must be a directory</source> <translation>Listeler alanı bir dizin olmalıdır</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> <source><b>Rule not supported</b></source> <translation><b>Kural desteklenmiyor</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <source><b>Error loading rule</b></source> <translation><b>Kural yüklenirken hata oluÅŸtu</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> <source>There's already a rule with this name.</source> <translation>Bu ada sahip bir kural zaten var.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> <source>PID field can not be empty</source> <translation>İşlem kimliÄŸi alanı boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> <source>PID field regexp error</source> <translation>İşlem kimliÄŸi alanı düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> <source>Select at least one field.</source> <translation>En az bir alan seçin.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> <source>Network interface can not be empty</source> <translation>AÄŸ arayüzü boÅŸ olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> <source>Network interface regexp error</source> <translation>AÄŸ arayüzü düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> <source>Source port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> <source>Source port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> <source>Source IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> <source>Source IP regexp error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Not running</source> <translation>Çalışmıyor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Disabled</source> <translation>Devre dışı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> <source>Running</source> <translation>Çalışıyor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de OpenSnitch</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> <source> Your are about to delete this rule. </source> <translation> Bu kuralı silmek üzeresiniz. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> Are you sure?</source> <translation> Emin misiniz?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch AÄŸ İstatistikleri {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>{0} için OpenSnitch AÄŸ İstatistikleri</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>Rules</source> <translation type="unfinished">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation type="unfinished">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>Kullanıldı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> <source>Save as CSV</source> <translation>CSV olarak kaydet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> <source>Delete</source> <translation>Sil</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <source>always</source> <translation type="obsolete">siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> <source><b>Error:</b><br><br>{0}</source> <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> <source>Disable</source> <translation>Devre dışı bırak</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Enable</source> <translation>EtkinleÅŸtir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> <source>Duplicate</source> <translation>ÇoÄŸalt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> <source>Edit</source> <translation>Düzenle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> <source>Rule not found by that name and node</source> <translation>Bu ada ve düğüme göre kural bulunamadı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation><b>Hata:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> <source>Warning:</source> <translation>Uyarı:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> <source>Always</source> <translation>Her zaman</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> <source>Until reboot</source> <translation>Yeniden baÅŸlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> <source> You are about to delete this rule. </source> <translation> Bu kuralı silmek üzeresiniz. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <translation type="obsolete">Última Conexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>xxxxx</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Name</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nombre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Address</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Status</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Estado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Hostname</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hostname</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Version</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Versión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Rules</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Reglas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Time</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Hora</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Action</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Acción</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Duration</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Duración</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Node</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Nodo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Enabled</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Habilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Hits</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Total</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Protocol</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Process</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Aplicación</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Destination</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Destino</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">Regla</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">UserID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces</comment> <translation type="obsolete">ÚltimaConexión</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ad</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Adres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Durum</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ana makine adı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Sürüm</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kurallar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Zaman</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Eylem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Süre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Düğüm</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Etkin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kullanıldı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>İletiÅŸim kuralı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>İşlem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Hedef</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Kural</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>KullanıcıKimliÄŸi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>SonBaÄŸlantı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Args</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="obsolete">Argümanlar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>HedefIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>HedefAnaMakine</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>HedefBaÄŸlantıNoktası</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="776"/> <source>New node connected</source> <translation>Yeni düğüm baÄŸlandı</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>Ne</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>AÄŸ adı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Çalışma süresi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Öncelik</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>BaÄŸlantılar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Bırakıldı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Ne</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> <source>Apply to</source> <translation>Uygula</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Reject</source> <translation>Geri çevir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Açıklama</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Komut satırı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Export rules</source> <translation>Kuralları dışa aktar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Import rules</source> <translation>Kuralları içe aktar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Export events to CSV</source> <translation>Olayları CSV dosyasına aktar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>Quit</source> <translation>Çıkış</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> <source>Export</source> <translation>Dışa aktar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>To clipboard</source> <translation>Panoya</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> <source>To disk</source> <translation>Diske</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> <source>Select a directory to export rules</source> <translation>Kuralları dışa aktarmak için bir dizin seçin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> <source> Your are about to delete this entry. </source> <translation> Bu girdiyi silmek üzeresiniz. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> <source> You are about to delete this node. </source> <translation> Bu düğümü silmek üzeresiniz. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation><b>Düğüm silinirken hata oluÅŸtu</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> <source>Error exporting rules</source> <translation>Kuralları dışa aktarırken hata oluÅŸtu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> <source>Select a directory with rules to import (JSON files)</source> <translation>İçe aktarılacak kuralları (JSON dosyaları) içeren bir dizin seçin</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> <source>Rules imported fine</source> <translation>Kurallar baÅŸarıyla içe aktarıldı</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="211"/> <source>WARNING</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> <source>New</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats_deleterule</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> <source> Your are about to delete this rule. </source> <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> </message> </context> <context> <name>stats_deleterule2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> <source> Are you sure?</source> <translation type="obsolete"> ¿Estás seguro?</translation> </message> </context> <context> <name>stats_disabled</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> <source>Disabled</source> <translation type="obsolete">Deshabilitado</translation> </message> </context> <context> <name>stats_notrunning</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> <source>Not running</source> <translation type="obsolete">Parado</translation> </message> </context> <context> <name>stats_running</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> <source>Running</source> <translation type="obsolete">Interceptando</translation> </message> </context> <context> <name>stats_wintitle</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> <source>OpenSnitch Network Statistics</source> <translation type="obsolete">Eventos de red OpenSnitch</translation> </message> </context> <context> <name>stats_wintitle2</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> <source>OpenSnitch Network Statistics for</source> <translation type="obsolete">Eventos de OpenSnitch de</translation> </message> </context> </TS> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/zh_TW/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017540�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/locales/zh_TW/opensnitch-zh_TW.ts������������������������������������������0000664�0000000�0000000�00000413260�15003540030�0023321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1"> <context> <name>Dialog</name> <message> <location filename="../../../opensnitch/res/prompt.ui" line="34"/> <source>opensnitch-qt</source> <translation>opensnitch-qt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="300"/> <source>User ID</source> <translation>使用者 ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="334"/> <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-weight:600;">執行來自</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="647"/> <source>TextLabel</source> <translation>文字標籤</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="437"/> <source>Source IP</source> <translation>ä¾†æº IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="458"/> <source>Process ID</source> <translation>處ç†ç¨‹åº ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="601"/> <source>Destination IP</source> <translation>目標 IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="622"/> <source>Dst Port</source> <translation>目標連接埠</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="702"/> <source>from this executable</source> <translation>來自此執行檔</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="707"/> <source>from this command line</source> <translation>來自此命令列</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="712"/> <source>this destination port</source> <translation>此目標連接埠</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="717"/> <source>this user</source> <translation>此使用者</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="722"/> <source>this destination ip</source> <translation>此目標 IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="727"/> <source>from this PID</source> <translation>來自此 PID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="751"/> <source>once</source> <translation>一次</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="756"/> <source>30s</source> <translation>30 ç§’</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="761"/> <source>5m</source> <translation>5 分é˜</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="766"/> <source>15m</source> <translation>15 分é˜</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="771"/> <source>30m</source> <translation>30 分é˜</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="776"/> <source>1h</source> <translation>1 å°æ™‚</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="781"/> <source>until reboot</source> <translation>æŒçºŒåˆ°é‡æ–°å•Ÿå‹•</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="786"/> <source>forever</source> <translation>永久</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="809"/> <source>action</source> <translation>動作</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="337"/> <source>Allow</source> <translation>å…許</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="865"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="14"/> <source>Firewall</source> <translation>防ç«ç‰†</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="55"/> <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">防ç«ç‰†</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="275"/> <source>Profile</source> <translation>設定檔</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="346"/> <source>Deny</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="313"/> <source>Outbound</source> <translation>å°å¤–</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="320"/> <source>Inbound</source> <translation>å°å…§</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="375"/> <source>Allow inbound connections to a port</source> <translation>å…許一個連接埠的å°å…§é€£ç·š</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="378"/> <source>Allow service (IN)</source> <translation>å…許æœå‹™ (å°å…§)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="398"/> <source>Exclude outbound connections to a port from being intercepted</source> <translation>排除被攔截到一個連接埠的å°å¤–連線</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="407"/> <source>Allow service (OUT)</source> <translation>å…許æœå‹™ (å°å¤–)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall.ui" line="427"/> <source>New rule</source> <translation>新增è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="421"/> <source>Close</source> <translation>關閉</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> <source>Firewall rule</source> <translation>防ç«ç‰†è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> <source>Node</source> <translation>節點</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> <source>Enable</source> <translation>啟用</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> <source>Description</source> <translation>æè¿°</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> <source>Simple</source> <translation>簡易</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> <source>Add new condition</source> <translation>新增æ¢ä»¶</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> <source>Remove selected condition</source> <translation>移除é¸å®šçš„æ¢ä»¶</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="221"/> <source>Direction</source> <translation>æ–¹å‘</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="232"/> <source>IN</source> <translation>輸入</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="241"/> <source>OUT</source> <translation>輸出</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="250"/> <source>FORWARD</source> <translation>轉發</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="255"/> <source>PREROUTING</source> <translation>é è·¯ç”±</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="260"/> <source>POSTROUTING</source> <translation>後路由</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="268"/> <source>Action</source> <translation>動作</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="279"/> <source>ACCEPT</source> <translation>接å—</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="288"/> <source>DROP</source> <translation>丟棄</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="297"/> <source>REJECT</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="306"/> <source>RETURN</source> <translation>返回</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="315"/> <source>QUEUE</source> <translation>佇列</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="323"/> <source>DNAT</source> <translation>DNAT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="328"/> <source>SNAT</source> <translation>SNAT</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="333"/> <source>REDIRECT</source> <translation>釿–°å°Žå‘</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="349"/> <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</source> <translation>便“šå‹•ä½œï¼ˆä¾‹å¦‚ï¼šç›®æ¨™ï¼‰ï¼Œåƒæ•¸çš„語法將有所ä¸åŒã€‚ 一些範例: QUEUE -> num 0(或 1ã€2ã€...) REDIRECTã€TPROXYã€DNATã€SNATã€MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048(masquerade)</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="432"/> <source>Clear</source> <translation>清除</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="443"/> <source>Delete</source> <translation>刪除</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="454"/> <source>Save</source> <translation>儲存</translation> </message> <message> <location filename="../../../opensnitch/res/firewall_rule.ui" line="465"/> <source>Add</source> <translation>新增</translation> </message> </context> <context> <name>PreferencesDialog</name> <message> <location filename="../../../opensnitch/res/preferences.ui" line="14"/> <source>Preferences</source> <translation>å好設定</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="39"/> <source>Pop-ups</source> <translation>彈出視窗</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="64"/> <source>Default options</source> <translation>é è¨­é¸é …</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="110"/> <source>If checked, this field will be selected when a pop-up is displayed</source> <translation>如果é¸å–ï¼Œå‰‡åœ¨é¡¯ç¤ºå½ˆå‡ºè¦–çª—æ™‚å°‡é¸æ“‡æ­¤æ¬„ä½</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="81"/> <source>User ID</source> <translation>使用者 ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>Destination port</source> <translation>目標連接埠</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination IP</source> <translation>目標 IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1173"/> <source>deny</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1182"/> <source>allow</source> <translation>å…許</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="147"/> <source>reject</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="159"/> <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> <translation><html><head/><body><p>彈出視窗的é è¨­å‹•作。</p><p>ç•¶æ–°çš„å°å¤–連線å³å°‡å»ºç«‹æ™‚,此動作將被é è¨­é¸æ“‡ï¼Œæ‰€ä»¥å¦‚果逾時,這將是被套用的é¸é …。</p><p><br/></p><p>當彈出視窗正在詢å•使用者是å¦å…許或拒絕連線時:</p><p>1. æ–°çš„å°å¤–連線被拒絕。</p><p>2. å·²çŸ¥çš„é€£ç·šä¾æ“šä½¿ç”¨è€…定義的è¦å‰‡è¢«å…許或拒絕。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="872"/> <source>Action</source> <translation>動作</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="179"/> <source>center</source> <translation>中央</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="184"/> <source>top right</source> <translation>å³ä¸Š</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="189"/> <source>bottom right</source> <translation>å³ä¸‹</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="194"/> <source>top left</source> <translation>左上</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="199"/> <source>bottom left</source> <translation>左下</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1205"/> <source>once</source> <translation>一次</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="219"/> <source>30s</source> <translation>30 ç§’</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="224"/> <source>5m</source> <translation>5 分é˜</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="229"/> <source>15m</source> <translation>15 分é˜</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="234"/> <source>30m</source> <translation>30 分é˜</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="239"/> <source>1h</source> <translation>1 å°æ™‚</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="244"/> <source>until reboot</source> <translation>æŒçºŒåˆ°é‡æ–°å•Ÿå‹•</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="249"/> <source>forever</source> <translation>永久</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="263"/> <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> <translation><html><head/><body><p>é è¨­æƒ…æ³ä¸‹ï¼Œç•¶å‡ºç¾æ–°çš„彈出視窗時,以其最簡單的形å¼ï¼Œæ‚¨å°‡èƒ½å¤ æŒ‰é€£ç·šçš„一個屬性(執行檔ã€é€£æŽ¥åŸ ã€IP 等)篩é¸é€£ç·šæˆ–應用程å¼ã€‚</p><p>使用這些é¸é …,您å¯ä»¥é¸æ“‡å¤šå€‹æ¬„ä½ä¾†ç¯©é¸é€£ç·šã€‚</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="266"/> <source>Filter connections also by:</source> <translation>也按以下方å¼ç¯©é¸é€£ç·šï¼š</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="283"/> <source>by executable</source> <translation>ä¾åŸ·è¡Œæª”</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="288"/> <source>by command line</source> <translation>ä¾å‘½ä»¤åˆ—</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="293"/> <source>by destination port</source> <translation>ä¾ç›®æ¨™é€£æŽ¥åŸ </translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="298"/> <source>by destination ip</source> <translation>ä¾ç›®æ¨™ IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="303"/> <source>by user id</source> <translation>ä¾ä½¿ç”¨è€… ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="308"/> <source>by PID</source> <translation>ä¾ PID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="323"/> <source>Default target</source> <translation>é è¨­ç›®æ¨™</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="330"/> <source>Default position on screen</source> <translation>在螢幕上的é è¨­ä½ç½®</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="340"/> <source>Pop-up default duration</source> <translation>彈出視窗的é è¨­æŒçºŒæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="343"/> <source>Duration</source> <translation>æŒçºŒæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="356"/> <source>The advanced view allows you to easily select multiple fields to filter connections</source> <translation>進階檢視讓您å¯ä»¥è¼•鬆鏿“‡å¤šå€‹æ¬„ä½ä¾†ç¯©é¸é€£ç·š</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="359"/> <source>Show advanced view by default</source> <translation>é è¨­é¡¯ç¤ºé€²éšŽæª¢è¦–</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="375"/> <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> <translation><html><head/><body><p>如果é¸å–,彈出視窗將會以進階檢視顯示。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> <translation><html><head/><body><p>此逾時是顯示彈出å°è©±æ¡†æ™‚看到的倒數計時。</p><p>如果未回答彈出視窗,將套用é è¨­é¸é …。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="469"/> <source>Default timeout</source> <translation>é è¨­é€¾æ™‚</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="476"/> <source>Disable pop-ups, only display a notification</source> <translation>åœç”¨å½ˆå‡ºè¦–窗,åªé¡¯ç¤ºé€šçŸ¥</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="487"/> <source>UI</source> <translation>使用者介é¢</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1128"/> <source>General</source> <translation>一般</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="509"/> <source>Language</source> <translation>語言</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="533"/> <source>System</source> <translation>系統</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="547"/> <source>Theme</source> <translation>主題</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="554"/> <source>By default the GUI is started when login</source> <translation>é è¨­ç™»å…¥æ™‚啟動圖形介é¢</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="557"/> <source>Autostart the GUI upon login</source> <translation>登入時自動啟動圖形介é¢</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="568"/> <source>Server</source> <translation>伺æœå™¨</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="581"/> <source>4MiB</source> <translation>4 MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="586"/> <source>8MiB</source> <translation>8 MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="591"/> <source>16MiB</source> <translation>16 MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="596"/> <source>32MiB</source> <translation>32 MiB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="605"/> <source>Simple</source> <translation>簡易</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="610"/> <source>Simple TLS</source> <translation>簡易 TLS</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="615"/> <source>Mutual TLS</source> <translation>é›™å‘ TLS</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="623"/> <source>Absolute path to the cert file</source> <translation>憑證檔案的絕å°è·¯å¾‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="630"/> <source>Maximum size of each message from nodes. Default 4MB</source> <translation>來自節點的æ¯å€‹è¨Šæ¯çš„æœ€å¤§å¤§å°ã€‚é è¨­ 4MB</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <source>Max gRPC channel size</source> <translation>gRPC é »é“的最大大å°</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="640"/> <source>Absolute path to the cert key file</source> <translation>憑證金鑰檔案的絕å°è·¯å¾‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="647"/> <source><p>Simple: no authentication, TLS simple/mutual: use SSL certificates to authenticate nodes.</p><p>Visit the wiki for more information.</p></source> <translation><p>簡易:無驗證,TLS 簡易/é›™å‘:使用 SSL 憑證來驗證節點。</p><p>造訪 wiki 以å–得更多資訊。</p></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="650"/> <source>Authentication type</source> <translation>驗證類型</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="657"/> <source>Absolute path to the CA cert file</source> <translation>CA 憑證檔案的絕å°è·¯å¾‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="673"/> <source>Desktop notifications</source> <translation>桌é¢é€šçŸ¥</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="685"/> <source>Enable</source> <translation>啟用</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="703"/> <source>Use system notifications</source> <translation>使用系統通知</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="719"/> <source>Use Qt notifications</source> <translation>使用 Qt 通知</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="748"/> <source>Test</source> <translation>測試</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="769"/> <source>Events tab columns</source> <translation>事件標籤欄ä½</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="808"/> <source>Time</source> <translation>時間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="824"/> <source>Rule</source> <translation>è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="840"/> <source>Node</source> <translation>節點</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="856"/> <source>Protocol</source> <translation>通訊å”定</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="888"/> <source>Destination</source> <translation>目標</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="904"/> <source>Process</source> <translation>處ç†ç¨‹åº</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <source>Command line</source> <translation>命令列</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <source>Rules</source> <translation>è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="940"/> <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> <translation>鏿“‡æ­¤é¸é …時,é¸å®šæŒçºŒæ™‚é–“çš„è¦å‰‡å°‡ä¸æœƒè¢«æ–°å¢žåˆ° GUI 的臨時è¦å‰‡åˆ—表中。 臨時è¦å‰‡ä»ç„¶æœ‰æ•ˆï¼Œä¸¦ä¸”您å¯ä»¥åœ¨æç¤ºå…許/阻擋新連線時使用它們。</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="945"/> <source>Don't save/Delete rules of duration</source> <translation>ä¸å„²å­˜/刪除æŒçºŒæ™‚é–“çš„è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="953"/> <source>any temporary rules</source> <translation>任何臨時è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="963"/> <source>30s or less</source> <translation>30 秒或更少</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="968"/> <source>5m or less</source> <translation>5 åˆ†é˜æˆ–æ›´å°‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="973"/> <source>15m or less</source> <translation>15 åˆ†é˜æˆ–æ›´å°‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="978"/> <source>30m or less</source> <translation>30 åˆ†é˜æˆ–æ›´å°‘</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>1h or less</source> <translation>1 å°æ™‚或更少</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1007"/> <source>Nodes</source> <translation>節點</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1019"/> <source>HostName</source> <translation>主機å稱</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1067"/> <source>Version</source> <translation>版本</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1109"/> <source>Apply configuration to all nodes</source> <translation>將設定套用到所有節點</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1134"/> <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> <translation><html><head/><body><p>節點的ä½å€ã€‚</p><p>é è¨­ï¼šunix:///tmp/osui.sock(如果是 Unix socket,unix:// 是必須的)</p><p>也å¯ä»¥æ˜¯å¸¶æœ‰é€£æŽ¥åŸ çš„ IP ä½å€ï¼š127.0.0.1:50051</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1137"/> <source>Address</source> <translation>ä½å€</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1148"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1156"/> <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>當沒有連接 UI 時,將進行é è¨­å‹•作。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1159"/> <source>Default action when the GUI is disconnected</source> <translation>GUI 斷開連接時的é è¨­å‹•作</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1194"/> <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> <translation><html><head/><body><p>當沒有連接 UI 時,將進行é è¨­æŒçºŒæ™‚間。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1197"/> <source>Default duration</source> <translation>é è¨­çš„æŒçºŒæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1210"/> <source>until restart</source> <translation>ç›´åˆ°é‡æ–°å•Ÿå‹•</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1215"/> <source>always</source> <translation>æ°¸é </translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1223"/> <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> <translation><html><head/><body><p>如果é¸å–,OpenSnitch å°‡æç¤ºæ‚¨å…許或拒絕沒有相關 PID çš„é€£ç·šï¼ŒåŽŸå› æœ‰å¾ˆå¤šï¼Œä¸»è¦æ˜¯å› ç‚ºé€£ç·šç‹€æ…‹ä¸ä½³ã€‚</p><p>彈出å°è©±æ¡†åªæœƒåŒ…嫿œ‰é—œç¶²è·¯é€£ç·šçš„資訊。</p><p>儘管在æŸäº›æƒ…æ³ä¸‹ï¼Œé€™äº›æ˜¯æœ‰æ•ˆçš„連線,例如使用 WireGuard 建立 VPN。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1226"/> <source>Debug invalid connections</source> <translation>åµéŒ¯ç„¡æ•ˆé€£ç·š</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1240"/> <source>Process monitor method</source> <translation>處ç†ç¨‹åºç›£æŽ§æ–¹æ³•</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1278"/> <source>Logging</source> <translation>記錄</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1291"/> <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> <translation><html><head/><body><p>寫入記錄的日誌檔案。<br/></p><p>/dev/stdout 將會將記錄列å°åˆ°æ¨™æº–輸出。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> <source>Log file</source> <translation>日誌檔案</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1301"/> <source><html><head/><body><p>If checked, OpenSnitch will log timestamp microseconds.</p></body></html></source> <translation><html><head/><body><p>如果é¸å–,OpenSnitch 將記錄時間戳微秒。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1304"/> <source>Log timestamp microseconds</source> <translation>記錄時間戳微秒</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1348"/> <source><html><head/><body><p>If checked, OpenSnitch will use the UTC timezone for timestamps.</p></body></html></source> <translation><html><head/><body><p>如果é¸å–,OpenSnitch 將使用 UTC 時å€çš„æ™‚間戳。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1351"/> <source>Log UTC timestamps</source> <translation>記錄 UTC 時間戳</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1358"/> <source>Default log level</source> <translation>é è¨­è¨˜éŒ„等級</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1369"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1374"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1410"/> <source>Database</source> <translation>資料庫</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1445"/> <source>In memory</source> <translation>在記憶體中</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1450"/> <source>File</source> <translation>檔案</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1464"/> <source>Database type</source> <translation>資料庫類型</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1471"/> <source>Select</source> <translation>鏿“‡</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1558"/> <source>minutes</source> <translation>分é˜</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1590"/> <source>Minutes between events purges</source> <translation>äº‹ä»¶æ¸…é™¤ä¹‹é–“çš„åˆ†é˜æ•¸</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1616"/> <source>days</source> <translation>天</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1629"/> <source>Maximum days of events to keep</source> <translation>ä¿ç•™äº‹ä»¶çš„æœ€å¤§å¤©æ•¸</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1753"/> <source>Close</source> <translation>關閉</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1764"/> <source>Apply</source> <translation>套用</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1775"/> <source>Save</source> <translation>儲存</translation> </message> </context> <context> <name>ProcessDetailsDialog</name> <message> <location filename="../../../opensnitch/res/process_details.ui" line="14"/> <source>Process details</source> <translation>處ç†ç¨‹åºè©³ç´°è³‡è¨Š</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="61"/> <source>loading...</source> <translation>載入中...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="81"/> <source>CWD: loading...</source> <translation>CWD:載入中...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="93"/> <source>mem stats: loading...</source> <translation>記憶體狀態:載入中...</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="121"/> <source>Status</source> <translation>狀態</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="135"/> <source>Open files</source> <translation>開啟檔案</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="149"/> <source>I/O Statistics</source> <translation>I/O 統計</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="163"/> <source>Memory mapped files</source> <translation>記憶體映射檔案</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="177"/> <source>Stack</source> <translation>堆疊</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="191"/> <source>Environment variables</source> <translation>環境變數</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="210"/> <source>Application pids</source> <translation>æ‡‰ç”¨ç¨‹å¼ PID</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="240"/> <source>Start or stop monitoring this process</source> <translation>é–‹å§‹æˆ–åœæ­¢ç›£æŽ§æ­¤ç¨‹åº</translation> </message> <message> <location filename="../../../opensnitch/res/process_details.ui" line="256"/> <source>Close</source> <translation>關閉</translation> </message> </context> <context> <name>RulesDialog</name> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> <source>Rule</source> <translation>è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> <source>Action</source> <translation>動作</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> <source>Duration</source> <translation>æŒçºŒæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> <source>once</source> <translation>一次</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> <source>until reboot</source> <translation>ç›´åˆ°é‡æ–°å•Ÿå‹•</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> <source>always</source> <translation>æ°¸é </translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> <source>Deny will just discard the connection</source> <translation>阻擋將僅丟棄連線</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> <source>Deny</source> <translation>阻擋</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>Reject will drop the connection, and kill the socket that initiated it</source> <translation>拒絕將會丟棄連線,並終止啟動它的socket</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> <source>Reject</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> <source>Allow will allow the connection</source> <translation>å…許將å…許連線</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> <source>Allow</source> <translation>å…許</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> <source>Enable</source> <translation>啟用</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</source> <translation>如果é¸å–,此è¦å‰‡å°‡å„ªæ–¼å…¶ä»–è¦å‰‡ã€‚åœ¨æ­¤ä¹‹å¾Œä¸æœƒæª¢æŸ¥å…¶ä»–è¦å‰‡ã€‚ 您必須以這樣的方å¼å‘½åè¦å‰‡ï¼Œä»¥ä¾¿é¦–先檢查它,因為它們按字æ¯é †åºæª¢æŸ¥ã€‚例如: [x] 優先 - 000-priority-rule [ ] 優先 - 001-less-priority-rule</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> <source>Priority rule</source> <translation>優先è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</source> <translation>è¦å‰‡æŒ‰å­—æ¯é †åºæª¢æŸ¥ï¼Œå› æ­¤æ‚¨å¯ä»¥ç›¸æ‡‰åœ°å‘½å它們以優先考慮它們。 000-allow-localhost 001-deny-broadcast ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> <source>Name</source> <translation>å稱</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>Node</source> <translation>節點</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>Apply rule to all nodes</source> <translation>å°‡è¦å‰‡å¥—用到所有節點</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> <source>Applications</source> <translation>應用程å¼</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> <translation><html><head/><body><p>此欄ä½çš„值始終是執行檔的絕å°è·¯å¾‘:/path/to/binary<br/></p><p>範例:</p><p>- 簡易:/path/to/binary</p><p>- 多路徑:^/usr/lib(64|)/firefox/firefox$</p><p>- 多個執行檔:^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$</p><p>- 拒絕/å…許從 /tmp 執行:</p><p>^/(var/|)tmp/.*$<br/></p><p>è¦å–得更多範例,請造訪<a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki é é¢</a>或在<a href="https://github.com/evilsocket/opensnitch/discussions">討論論壇</a>æå•。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> <source>Is regular expression</source> <translation>是正è¦è¡¨é”å¼</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> <source>From this user ID</source> <translation>來自此使用者 ID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> <source>From this command line</source> <translation>來自此命令列</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> <translation><html><head/><body><p>此欄ä½å°‡åŒ…å«ä¸¦ç¬¦åˆä½¿ç”¨è€…執行的命令列。<br/></p><p>å¦‚æžœä½¿ç”¨è€…è¼¸å…¥äº†å‘½ä»¤ï¼Œåªæœƒå‡ºç¾è©²å‘½ä»¤ï¼š</p><p>telnet 1.2.3.4<br/></p><p>å¦‚æžœä½¿ç”¨è€…è¼¸å…¥äº†å‘½ä»¤çš„çµ•å°æˆ–相å°è·¯å¾‘,那就會出ç¾è©²å…§å®¹ï¼š</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> <source>From this PID</source> <translation>來自此 PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="466"/> <source>From this executable</source> <translation>來自此執行檔</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="473"/> <source>is regular expression</source> <translation>是正è¦è¡¨é”å¼</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="485"/> <source>Network</source> <translation>網路</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="520"/> <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> <translation><html><head/><body><p>僅å…許 TCPã€UDP 或 UDPLITE</p><p>您å¯ä»¥ä½¿ç”¨æ­£è¦è¡¨é”å¼ï¼Œä¾‹å¦‚:^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="560"/> <source>ICMP</source> <translation>ICMP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="565"/> <source>ICMP6</source> <translation>ICMP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="570"/> <source>SCTP</source> <translation>SCTP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="575"/> <source>SCTP6</source> <translation>SCTP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="586"/> <source>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/> <source>To this IP / Network</source> <translation>到此 IP / 網路</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="627"/> <source>Protocol</source> <translation>通訊å”定</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> <source>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</source> <translation>您å¯ä»¥æŒ‡å®šå–®ä¸€ IP: - 192.168.1.1 或是正è¦è¡¨é”å¼ï¼š - 192\.168\.1\.[0-9]+ 多個 IP: - ^(192\.168\.1\.1|172\.16\.0\.1)$ 您也å¯ä»¥æŒ‡å®šå­ç¶²ï¼š - 192.168.1.0/24 注æ„:ä¸å…許使用逗號或空格分隔 IP 或網路。</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="659"/> <source>LAN</source> <translation>å€åŸŸç¶²è·¯</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="664"/> <source>MULTICAST</source> <translation>多播</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="669"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="674"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="679"/> <source>192.168.1.0/24</source> <translation>92.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="689"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="694"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="699"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="704"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="709"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="714"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="719"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="724"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="729"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="737"/> <source>From this IP / Network</source> <translation>來自此 IP / 網路</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="744"/> <source>To this host</source> <translation>到此主機</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/> <source>Network interface</source> <translation>網路介é¢</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="866"/> <source>From this port</source> <translation>來自此連接埠</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/> <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> <translation><html><head/><body><p>您å¯ä»¥ä½¿ç”¨æ­£è¦è¡¨é”弿Œ‡å®šå¤šå€‹é€£æŽ¥åŸ ï¼š</p><p>- 53ã€80 或 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53ã€443 或 5551ã€5552ã€5553 等:</p><p>^(53|443|555[0-9])$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="896"/> <source>To this port</source> <translation>到此連接埠</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="926"/> <source>List of domains/IPs</source> <translation>網域/IP 清單</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> <source>To this list of network ranges</source> <translation>åˆ°æ­¤ç¶²è·¯ç¯„åœæ¸…å–®</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="939"/> <source>To this list of IPs</source> <translation>到此 IP 清單</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="965"/> <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>鏿“‡ä¸€å€‹å«æœ‰è¦å°éŽ–æˆ–å…許的 IP 清單的檔案的目錄:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>等等。</p><p>æ¯è¡Œä¸€å€‹ IP。空行或以 # 開頭的行將被忽略。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="974"/> <source>To this list of domains</source> <translation>到此網域清單</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1000"/> <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>鏿“‡ä¸€å€‹å«æœ‰è¦å°éŽ–æˆ–å…è¨±çš„ç¶²è·¯ç¯„åœæ¸…單的檔案的目錄:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>等等。<br/></p><p>æ¯è¡Œä¸€å€‹ç¶²è·¯ç¯„åœã€‚空行或以 # 開頭的行將被忽略。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1028"/> <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>鏿“‡ä¸€å€‹å«æœ‰è¦å°éŽ–æˆ–å…許的網域清單的檔案的目錄。</p><p>åœ¨è©²ç›®éŒ„ä¸­æ”¾ç½®å«æœ‰ç¶²åŸŸæ¸…單的任何副檔å的檔案。</p><p><br/>æ¯å€‹æ¸…å–®æ¢ç›®çš„æ ¼å¼å¦‚下(hosts æ ¼å¼ï¼‰ï¼š</p><p>127.0.0.1 www.domain.com</p><p>或 </p><p>0.0.0.0 www.domain.com</p><p>空行或以 # 開頭的行將被忽略。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1043"/> <source>To this list of domains (regular expressions)</source> <translation>到此網域清單 (æ­£è¦è¡¨é”å¼)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1070"/> <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> <translation><html><head/><body><p>è«‹åœ¨ç›®éŒ„ä¸­é¸æ“‡ä¸€å€‹å«æœ‰è¦å°éŽ–æˆ–å…許的網域正è¦è¡¨é”å¼çš„æª”案:</p><p>.*\.example\.com</p><p>您也å¯ä»¥ç›´æŽ¥ä½¿ç”¨ç¶²åŸŸï¼š&quot;example.com&quot;ï¼Œå®ƒå°‡ç¬¦åˆ whatever.example.comã€whatever.example.com.localdomain 等。</p><p>æ¯è¡Œè¼¸å…¥ä¸€å€‹ç¶²åŸŸã€‚空行或以 # 開頭的行將被忽略。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1080"/> <source>More</source> <translation>更多</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> <translation><html><head/><body><p>é è¨­æƒ…æ³ä¸‹ï¼Œè¦å‰‡çš„æ¬„ä½ä¸å€åˆ†å¤§å°å¯«ï¼Œå³ï¼Œå¦‚果一個程åºè©¦åœ–å­˜å– gOOgle.CoM,並且您有一個阻擋 .*google.com çš„è¦å‰‡ï¼Œå‰‡é€£ç·šå°‡è¢«é˜»æ“‹ã€‚<br/></p><p>如果您é¸å–此框,則必須指定您è¦ç¯©é¸çš„確切字串(網域ã€åŸ·è¡Œæª”ã€å‘½ä»¤åˆ—)。</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1089"/> <source>Case-sensitive</source> <translation>å€åˆ†å¤§å°å¯«</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1096"/> <source>Don't log connections that match this rule</source> <translation>ä¸è¨˜éŒ„ç¬¦åˆæ­¤è¦å‰‡çš„連線</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1099"/> <source>Don't log connections</source> <translation>ä¸è¨˜éŒ„連線</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="1145"/> <source>Description...</source> <translation>æè¿°...</translation> </message> </context> <context> <name>StatsDialog</name> <message> <location filename="../../../opensnitch/res/stats.ui" line="34"/> <source>OpenSnitch Network Statistics</source> <translation>OpenSnitch 網路統計</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="94"/> <source>Filter</source> <translation>篩é¸</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1814"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="107"/> <source>Allow</source> <translation>å…許</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="116"/> <source>Deny</source> <translation>阻擋</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="125"/> <source>Reject</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="143"/> <source>Ex.: firefox</source> <translation>例如:firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="180"/> <source>0</source> <translation>0</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="205"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="210"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="215"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="220"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="233"/> <source>Delete all intercepted events</source> <translation>刪除所有被攔截的事件</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="333"/> <source>Create a new rule</source> <translation>建立新è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="376"/> <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">主機å稱 - 192.168.1.1</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="413"/> <source>Status</source> <translation>狀態</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="451"/> <source>Start or Stop interception</source> <translation>é–‹å§‹æˆ–åœæ­¢æ””截</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="496"/> <source>Events</source> <translation>事件</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="829"/> <source>Nodes</source> <translation>節點</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="640"/> <source>Delete this node</source> <translation>刪除此節點</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="656"/> <source>Show the preferences of this node</source> <translation>顯示此節點的å好設定</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="672"/> <source>Start or stop interception of this node</source> <translation>é–‹å§‹æˆ–åœæ­¢æ””截此節點</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1721"/> <source>Rules</source> <translation>è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="780"/> <source>2</source> <translation>2</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="785"/> <source>Application rules</source> <translation>應用程å¼è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="939"/> <source>Permanent</source> <translation>永久</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="948"/> <source>Temporary</source> <translation>臨時</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="957"/> <source>System rules</source> <translation>系統è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="930"/> <source>All applications</source> <translation>所有應用程å¼</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="998"/> <source>enable</source> <translation>啟用</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1028"/> <source>Edit rule</source> <translation>編輯è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1045"/> <source>Delete rule</source> <translation>刪除è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1069"/> <source>Hosts</source> <translation>主機</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1162"/> <source>Applications</source> <translation>應用程å¼</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1278"/> <source>Addresses</source> <translation>ä½å€</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Ports</source> <translation>連接埠</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1458"/> <source>Users</source> <translation>使用者</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1565"/> <source>Connections</source> <translation>連線</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1617"/> <source>Dropped</source> <translation>已丟棄</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1669"/> <source>Uptime</source> <translation>é‹ä½œæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1788"/> <source>Version</source> <translation>版本</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="48"/> <source>Statistics</source> <translation>統計</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="49"/> <source>Enable</source> <translation>啟用</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="50"/> <source>Disable</source> <translation>åœç”¨</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="51"/> <source>Help</source> <translation>幫助</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="52"/> <source>Close</source> <translation>關閉</translation> </message> </context> <context> <name>firewall</name> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="96"/> <source>Configuration applied.</source> <translation>設定已套用。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="99"/> <source>Error: {0}</source> <translation>錯誤: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="198"/> <source>Applying changes...</source> <translation>正在套用更改...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="235"/> <source>Error getting INPUT chain policy</source> <translation>å–å¾— INPUT éˆç­–略時出錯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="242"/> <source>Error getting OUTPUT chain policy</source> <translation>å–å¾— OUTPUT éˆç­–略時出錯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="295"/> <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> <translation>為了從 GUI 設定防ç«ç‰†è¦å‰‡ï¼Œæˆ‘們需è¦ä½¿ç”¨ 'nftables' è€Œä¸æ˜¯ 'iptables'</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="309"/> <source>Enabling firewall...</source> <translation>正在啟用防ç«ç‰†...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall.py" line="311"/> <source>Disabling firewall...</source> <translation>正在åœç”¨é˜²ç«ç‰†...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> <source>Dest Port</source> <translation>目標連接埠</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> <source>Source Port</source> <translation>來æºé€£æŽ¥åŸ </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> <source>Dest IP</source> <translation>目標 IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> <source>Source IP</source> <translation>ä¾†æº IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> <source>Input interface</source> <translation>輸入介é¢</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> <source>Output interface</source> <translation>輸出介é¢</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> <source>Set conntrack mark</source> <translation>設定 conntrack 標記</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> <source>Match conntrack mark</source> <translation>ç¬¦åˆ conntrack 標記</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> <source>Match conntrack state(s)</source> <translation>ç¬¦åˆ conntrack 狀態</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> <source>Set mark on packet</source> <translation>在å°åŒ…上設定標記</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> <source>Match packet information</source> <translation>符åˆå°åŒ…資訊</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> <source>Bandwidth quotas</source> <translation>頻寬é…é¡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> <source>Rate limit connections</source> <translation>é™åˆ¶é€£æŽ¥é€Ÿçއ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> <source> Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 </source> <translation> 支æ´çš„æ ¼å¼ï¼š - 簡易:23 - 範åœï¼š80-1024 - 多個連接埠:80,443,8080 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> <source> Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 </source> <translation> 支æ´çš„æ ¼å¼ï¼š - 簡易:1.2.3.4 - IP 範åœï¼š1.2.3.100-1.2.3.200 - 網路範åœï¼š1.2.3.4/24 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> <source>Match input interface. Regular expressions not allowed. Use * to match multiple interfaces.</source> <translation>符åˆè¼¸å…¥ä»‹é¢ã€‚ä¸å…許使用正è¦è¡¨é”å¼ã€‚ 使用 * 符號符åˆå¤šå€‹ä»‹é¢ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="155"/> <source>Match output interface. Regular expressions not allowed. Use * to match multiple interfaces.</source> <translation>符åˆè¼¸å‡ºä»‹é¢ã€‚ä¸å…許使用正è¦è¡¨é”å¼ã€‚ 使用 * 符號符åˆå¤šå€‹ä»‹é¢ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="163"/> <source>Set a conntrack mark on the connection, in decimal format.</source> <translation>在連線上設定 conntrack 標記,以å進使 ¼å¼ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="173"/> <source>Match a conntrack mark of the connection, in decimal format.</source> <translation>符åˆé€£ç·šçš„ conntrack 標記,以å進使 ¼å¼ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="180"/> <source>Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new </source> <translation>ç¬¦åˆ conntrack 狀態。 支æ´çš„æ ¼å¼ï¼š - 簡易:new - 逗號分隔狀態:related,new </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="195"/> <source> Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. </source> <translation> 符åˆå°åŒ…的附加資訊。 值必須使用å進使 ¼å¼ï¼Œå”¯ç¨ "l4proto" é¸é …除外。 å°æ–¼ l4proto,它å¯ä»¥æ˜¯å°å¯«å­—串,例如: tcp udp icmp 等等 如果å”議或 lproto 的值為å進ä½ï¼Œå®ƒå°‡ç”¨ä½œè©²å”議的代碼。 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="215"/> <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> <translation>åœ¨ç¬¦åˆæŒ‡å®šæ¢ä»¶çš„å°åŒ…上設定標記。值為å進使 ¼å¼ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="223"/> <source> Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation> ç¬¦åˆ ICMP 代碼。 支æ´çš„æ ¼å¼ï¼š - 簡易:echo-request - 逗號分隔:echo-request,echo-reply </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="236"/> <source> Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply </source> <translation> ç¬¦åˆ ICMPv6 代碼。 支æ´çš„æ ¼å¼ï¼š - 簡易:echo-request - 逗號分隔:echo-request,echo-reply </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="249"/> <source>Print a message when this rule matches a packet.</source> <translation>ç•¶æ­¤è¦å‰‡ç¬¦åˆå°åŒ…時,列å°ä¸€æ¢è¨Šæ¯ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="256"/> <source> Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc </source> <translation> å°é€£ç·šå¥—用é…é¡ã€‚ 例如: - "quota over 10/mbytes" -> 套用定義的動作 (DROP) - "quota until 10/mbytes" -> 套用定義的動作 (ACCEPT) 值必須為 VALUE/UNITS æ ¼å¼ï¼Œä¾‹å¦‚: - 10mbytes, 1/gbytes, 等等 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="288"/> <source> Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc </source> <translation> å°é€£ç·šå¥—用é™åˆ¶ã€‚ 例如: - "limit over 10/mbytes/minute" -> 套用定義的動作 (DROP, ACCEPT, 等等) (ç•¶æ¯åˆ†é˜è¶…éŽ 10MB 時,套用一個動作) - "limit until 10/mbytes/hour" -> 套用定義的動作 (ACCEPT) 值必須為 VALUE/UNITS/TIME æ ¼å¼ï¼Œä¾‹å¦‚: - 10/mbytes/minute, 1/gbytes/hour, 等等 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="376"/> <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior (pip3 install --ignore-installed protobuf==3.8.0)</source> <translation>您的 protobuf 版本ä¸ç›¸å®¹ï¼Œæ‚¨éœ€è¦å®‰è£ protobuf 3.8.0 或更高版本 (pip3 install --ignore-installed protobuf==3.8.0)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="405"/> <source>Rule deleted</source> <translation>è¦å‰‡å·²åˆªé™¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="411"/> <source>Rule saved</source> <translation>è¦å‰‡å·²å„²å­˜</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="413"/> <source>Rule added</source> <translation>è¦å‰‡å·²æ–°å¢ž</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="423"/> <source>Error saving rule</source> <translation>儲存è¦å‰‡æ™‚出錯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="450"/> <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> <translation>您å¯ä»¥ä½¿ç”¨ ',' 或 '-' 來指定多個連接埠/IP 或範åœ/值:<br><br>連接埠:22 或 22,443 或 50000-60000<br>IP:192.168.1.1 或 192.168.1.30-192.168.1.130<br>值:echo-reply,echo-request<br>值:new,established,related</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="470"/> <source>Deleting rule, wait</source> <translation>正在刪除è¦å‰‡ï¼Œè«‹ç¨å€™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="473"/> <source>Error updating rule</source> <translation>æ›´æ–°è¦å‰‡æ™‚出錯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="508"/> <source>Add at least one statement.</source> <translation>至少新增一個語å¥ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="519"/> <source>Adding rule, wait</source> <translation>正在新增è¦å‰‡ï¼Œè«‹ç¨å€™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="529"/> <source><select a statement></source> <translation><鏿“‡ä¸€å€‹èªžå¥></translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="654"/> <source>num</source> <translation>數字</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="668"/> <source>to</source> <translation>到</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="834"/> <source>Equal</source> <translation>等於</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="835"/> <source>Not equal</source> <translation>ä¸ç­‰æ–¼</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="836"/> <source>Greater or equal than</source> <translation>大於或等於</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="837"/> <source>Greater than</source> <translation>大於</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="838"/> <source>Less or equal than</source> <translation>å°æ–¼æˆ–等於</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="839"/> <source>Less than</source> <translation>å°æ–¼</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1007"/> <source>Warning: ct set mark value is empty, malformed rule?</source> <translation>警告:ct 設定標記值為空,è¦å‰‡æ ¼å¼éŒ¯èª¤ï¼Ÿ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1523"/> <source>Firewall rule</source> <translation>防ç«ç‰†è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1059"/> <source>Simple</source> <translation>簡易</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1064"/> <source>Advanced</source> <translation>進階</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1186"/> <source>This rule is not supported yet.</source> <translation>æ­¤è¦å‰‡ç›®å‰å°šæœªæ”¯æ´ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1251"/> <source>Exclude service</source> <translation>排除æœå‹™</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1263"/> <source>Allow inbound connections to the selected port.</source> <translation>å…許到é¸å®šé€£æŽ¥åŸ çš„å°å…§é€£æŽ¥ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1265"/> <source>Allow outbound connections to the selected port.</source> <translation>å…許到é¸å®šé€£æŽ¥åŸ çš„å°å¤–連接。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1341"/> <source>select a statement.</source> <translation>鏿“‡ä¸€å€‹èªžå¥ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1357"/> <source>value cannot be 0 or empty.</source> <translation>值ä¸èƒ½ç‚º 0 或空。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1369"/> <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> <translation>值的格å¼ç‚º 1024/kbytes (或 bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1383"/> <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> <translation>值的格å¼ç‚º 1024/kbytes/second (或 bytes, mbytes, gbytes)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1386"/> <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> <translation>速率é™åˆ¶ç„¡æ•ˆï¼Œä½¿ç”¨ï¼šbytes, kbytes, mbytes 或 gbytes。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1388"/> <source>time-limit not valid, use: second, minute, hour or day</source> <translation>時間é™åˆ¶ç„¡æ•ˆï¼Œä½¿ç”¨ï¼šsecond, minute, hour 或 day</translation> </message> <message> <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1455"/> <source>port not valid.</source> <translation>連接埠無效。</translation> </message> </context> <context> <name>messages</name> <message> <location filename="../../../opensnitch/service.py" line="301"/> <source>Info</source> <translation>資訊</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="305"/> <source>Error</source> <translation>錯誤</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="309"/> <source>Warning</source> <translation>警告</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="767"/> <source>System notifications are not available, you need to install python3-notify2.</source> <translation>系統通知無法使用,您需è¦å®‰è£ python3-notify2 套件。</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/notifications.py" line="42"/> <source>Open</source> <translation>開啟</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="119"/> <source>Allow</source> <translation>å…許</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="118"/> <source>Deny</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="114"/> <source>New outgoing connection</source> <translation>æ–°çš„å°å¤–連線</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="494"/> <source>is connecting to <b>%s</b> on %s port %d</source> <translation>正在連線到 <b>%s</b> çš„ %s 連接埠 %d</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> <source>until reboot</source> <translation>ç›´åˆ°é‡æ–°å•Ÿå‹•</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>永久</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="120"/> <source>Reject</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="335"/> <source>Outgoing connection</source> <translation>å°å¤–連線</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="340"/> <source>Process launched from:</source> <translation>處ç†ç¨‹åºèµ·å§‹ä¾†æºï¼š</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> <source>from this executable</source> <translation>來自此執行檔</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="381"/> <source>from this command line</source> <translation>來自此命令列</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="383"/> <source>to port {0}</source> <translation>到埠號 {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="446"/> <source>to {0}</source> <translation>到 {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> <source>from user {0}</source> <translation>來自使用者 {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/> <source>from this PID</source> <translation>來自此 PID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="403"/> <source>to {0}.*</source> <translation>到 {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="456"/> <source>to *.{0}</source> <translation>到 *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> <source><b>Remote</b> process %s running on <b>%s</b></source> <translation><b>é ç«¯</b> 處ç†ç¨‹åº %s 在 <b>%s</b> 上執行</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="501"/> <source>is connecting to <b>%s</b>, %s</source> <translation>正在連線到 <b>%s</b>,%s</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="506"/> <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> <translation>正試圖é€éŽ %s,%s 連接埠 %d è§£æž <b>%s</b></translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="458"/> <source>Warning</source> <translation>警告</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="44"/> <source>Restart the GUI in order changes to take effect</source> <translation>釿–°å•Ÿå‹• GUI 以使變更生效</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="438"/> <source>There're no nodes connected</source> <translation>沒有任何節點已連線。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="183"/> <source>System default</source> <translation>系統é è¨­</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="534"/> <source>System</source> <translation>系統</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="206"/> <source>Themes not available. Install qt-material: pip3 install qt-material</source> <translation>主題ä¸å¯ç”¨ã€‚å®‰è£ qt-material:pip3 install qt-material</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="337"/> <source>Server address can not be empty</source> <translation>伺æœå™¨ä½å€ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="368"/> <source>Error loading {0} configuration</source> <translation>載入 {0} 設定時出錯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="436"/> <source>Exception saving config: {0}</source> <translation>儲存設定時發生例外:{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="452"/> <source>DB type changed</source> <translation>DB 類型已變更</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="458"/> <source>You must select a file for the database<br>or choose "In memory" type.</source> <translation>æ‚¨å¿…é ˆç‚ºè³‡æ–™åº«é¸æ“‡ä¸€å€‹æª”案<br>æˆ–æ˜¯é¸æ“‡ã€Œè¨˜æ†¶é«”中ã€çš„類型。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="490"/> <source>Certificates changed</source> <translation>憑證已變更</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="504"/> <source>Language changed</source> <translation>語言已變更</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="535"/> <source>UI theme changed</source> <translation>UI 主題已變更</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="565"/> <source>Applying configuration on {0} ...</source> <translation>æ­£åœ¨å° {0} 套用設定 ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="573"/> <source>Ok</source> <translation>確定</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="583"/> <source>Exception saving node config {0}: {1}</source> <translation>儲存節點設定 {0} 時發生例外:{1}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="594"/> <source>Certs fields cannot be empty.</source> <translation>憑證欄ä½ä¸èƒ½ç‚ºç©ºã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="597"/> <source>cert file has excessive permissions, it should have 0600</source> <translation>憑證檔案權é™éŽå¤§ï¼Œæ‡‰è¨­ç‚º 0600</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="601"/> <source>cert key file has excessive permissions, it should have 0600</source> <translation>憑證金鑰檔案權é™éŽå¤§ï¼Œæ‡‰è¨­ç‚º 0600</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> <source>CA cert file has excessive permissions, it should have 0600</source> <translation>CA 憑證檔案權é™éŽå¤§ï¼Œæ‡‰è¨­ç‚º 0600</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="667"/> <source>Configuration applied.</source> <translation>設定已套用。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="669"/> <source>Error applying configuration: {0}</source> <translation>套用設定時出錯:{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="674"/> <source>Certs changed</source> <translation>憑證已變更</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="710"/> <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> <translation>將滑鼠åœåœ¨æ–‡å­—上以顯示幫助<br><br>別忘了造訪 wiki:<a href="{0}">{0}</a></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="737"/> <source>Auth type changed</source> <translation>èªè­‰é¡žåž‹å·²è®Šæ›´</translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> <source><b>Error loading process information:</b> <br><br> </source> <translation><b>載入處ç†ç¨‹åºè³‡è¨Šæ™‚出錯:</b> <br><br> </translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation><b>åœæ­¢ç›£æŽ§è™•ç†ç¨‹åºæ™‚出錯:</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> <source>loading...</source> <translation>載入中...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="238"/> <source>There're no nodes connected.</source> <translation>沒有已連線的節點。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="255"/> <source>There's already a rule with this name.</source> <translation>已經有一æ¢ç›¸åŒå稱的è¦å‰‡ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="281"/> <source>Rule applied.</source> <translation>è¦å‰‡å·²å¥—用。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="283"/> <source>Error applying rule: {0}</source> <translation>套用è¦å‰‡å‡ºéŒ¯ï¼š{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="549"/> <source><b>Error loading rule</b></source> <translation><b>載入è¦å‰‡å‡ºéŒ¯</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/> <source>protocol can not be empty, or uncheck it</source> <translation>通訊å”定ä¸èƒ½ç‚ºç©ºæˆ–å–æ¶ˆå‹¾é¸</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/> <source>Protocol regexp error</source> <translation>通訊å”定正è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/> <source>process path can not be empty</source> <translation>處ç†ç¨‹åºè·¯å¾‘ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/> <source>Process path regexp error</source> <translation>處ç†ç¨‹åºè·¯å¾‘æ­£è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="687"/> <source>command line can not be empty</source> <translation>命令列ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="701"/> <source>Command line regexp error</source> <translation>命令列正è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="705"/> <source>Network interface can not be empty</source> <translation>網路介é¢ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="719"/> <source>Network interface regexp error</source> <translation>ç¶²è·¯ä»‹é¢æ­£è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="723"/> <source>Source port can not be empty</source> <translation>來æºé€£æŽ¥åŸ ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="737"/> <source>Source port regexp error</source> <translation>來æºé€£æŽ¥åŸ æ­£è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/> <source>Dest port can not be empty</source> <translation>目標連接埠ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="755"/> <source>Dst port regexp error</source> <translation>目標連接埠正è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="759"/> <source>Dest host can not be empty</source> <translation>目標主機ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="773"/> <source>Dst host regexp error</source> <translation>目標主機正è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/> <source>Source IP/Network can not be empty</source> <translation>ä¾†æº IP/網路ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="803"/> <source>Source IP regexp error</source> <translation>ä¾†æº IP æ­£è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="815"/> <source>Dest IP/Network can not be empty</source> <translation>目標 IP/網路ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="841"/> <source>Dst IP regexp error</source> <translation>目標 IP æ­£è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="856"/> <source>User ID can not be empty</source> <translation>使用者 ID ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="873"/> <source>User ID regexp error</source> <translation>使用者 ID æ­£è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="876"/> <source>Invalid UID, it must be a digit.</source> <translation>無效的 UID,必須是數字。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="890"/> <source>PID field can not be empty</source> <translation>PID 欄ä½ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="904"/> <source>PID field regexp error</source> <translation>PID æ¬„ä½æ­£è¦è¡¨é”å¼éŒ¯èª¤</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="960"/> <source>Lists field cannot be empty</source> <translation>列表欄ä½ä¸èƒ½ç‚ºç©º</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="962"/> <source>Lists field must be a directory</source> <translation>列表欄ä½å¿…須是目錄</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="992"/> <source>Select at least one field.</source> <translation>è‡³å°‘é¸æ“‡ä¸€å€‹æ¬„ä½ã€‚</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="1005"/> <source><b>Rule not supported</b></source> <translation><b>䏿”¯æ´çš„è¦å‰‡</b></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/service.py" line="231"/> <source>WARNING</source> <translation>警告</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="796"/> <source>New node connected</source> <translation>新節點已連接</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> <source>What</source> <translation>什麼</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> <source>Hits</source> <translation>命中次數</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> <source>Network name</source> <translation>網路å稱</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>時間</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>節點</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>動作</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>目的地</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>å”è­°</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>處ç†ç¨‹åº</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="286"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>å稱</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ä½å€</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>狀態</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>主機å稱</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> <source>Uptime</source> <comment>This is a word, without spaces and symbols.</comment> <translation>é‹ä½œæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>版本</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="419"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>æŒçºŒæ™‚é–“</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Description</source> <comment>This is a word, without spaces and symbols.</comment> <translation>æè¿°</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>已啟用</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>優先順åº</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>命中次數</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <source>Cmdline</source> <comment>This is a word, without spaces and symbols.</comment> <translation>命令列</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>目標 IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>目標主機</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>目標連接埠</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> <source>UserID</source> <comment>This is a word, without spaces and symbols.</comment> <translation>使用者 ID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>LastConnection</source> <comment>This is a word, without spaces and symbols.</comment> <translation>最後連線</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <source>Not running</source> <translation>未é‹ä½œ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> <source>Disabled</source> <translation>å·²åœç”¨</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> <source>Running</source> <translation>é‹ä½œä¸­</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="405"/> <source>Export rules</source> <translation>匯出è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> <source>Import rules</source> <translation>匯入è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> <source>Export events to CSV</source> <translation>將事件匯出至 CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> <source>Quit</source> <translation>退出</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> <source>Connections</source> <comment>This is a word, without spaces and symbols.</comment> <translation>連線</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> <source>Dropped</source> <comment>This is a word, without spaces and symbols.</comment> <translation>已丟棄</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="436"/> <source>What</source> <comment>This is a word, without spaces and symbols.</comment> <translation>內容</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="635"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch 網路統計 {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="637"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch 為 {0} 的網路統計</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="832"/> <source>Details</source> <translation>詳細資訊</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> <source>Rules</source> <translation>è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> <source>New</source> <translation>新增</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> <source>Export</source> <translation>匯出</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> <source>Action</source> <translation>動作</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> <source>Disable</source> <translation>åœç”¨</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="966"/> <source>Enable</source> <translation>啟用</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="971"/> <source>Delete</source> <translation>刪除</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="970"/> <source>Edit</source> <translation>編輯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="974"/> <source>To clipboard</source> <translation>複製到剪貼簿</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> <source>Apply to</source> <translation>套用於</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="950"/> <source>Allow</source> <translation>å…許</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="951"/> <source>Deny</source> <translation>阻擋</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="952"/> <source>Reject</source> <translation>拒絕</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="955"/> <source>Always</source> <translation>總是</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> <source>Until reboot</source> <translation>æŒçºŒåˆ°é‡æ–°å•Ÿå‹•</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="969"/> <source>Duplicate</source> <translation>複製</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="975"/> <source>To disk</source> <translation>儲存到ç£ç¢Ÿ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1730"/> <source> Are you sure?</source> <translation> 您確定嗎?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2559"/> <source>Select a directory to export rules</source> <translation>鏿“‡ä¸€å€‹ç›®éŒ„以匯出è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1207"/> <source> Your are about to delete this rule. </source> <translation> 您å³å°‡åˆªé™¤æ­¤è¦å‰‡ã€‚ </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1209"/> <source> Your are about to delete this entry. </source> <translation> 您å³å°‡åˆªé™¤æ­¤æ¢ç›®ã€‚ </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1266"/> <source>Rule not found by that name and node</source> <translation>未找到該å稱和節點的è¦å‰‡</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1319"/> <source>Error:</source> <translation>錯誤:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1327"/> <source>Warning:</source> <translation>警告:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1697"/> <source> You are about to delete this node. </source> <translation> 您å³å°‡åˆªé™¤æ­¤ç¯€é»žã€‚ </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1706"/> <source><b>Error deleting node</b><br><br></source> <comment>{0}</comment> <translation><b>刪除節點時出錯</b><br><br></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1730"/> <source> You are about to delete this rule. </source> <translation> 您å³å°‡åˆªé™¤æ­¤è¦å‰‡ã€‚ </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2514"/> <source>Error exporting rules</source> <translation>匯出è¦å‰‡æ™‚出錯</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2588"/> <source>Select a directory with rules to import (JSON files)</source> <translation>鏿“‡ä¸€å€‹å«æœ‰è¦åŒ¯å…¥çš„è¦å‰‡çš„目錄(JSON 檔案)</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2602"/> <source>Rules imported fine</source> <translation>è¦å‰‡åŒ¯å…¥æˆåŠŸ</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="2617"/> <source>Save as CSV</source> <translation>å¦å­˜ç‚º CSV</translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/i18n/opensnitch_i18n.pro��������������������������������������������������������0000664�0000000�0000000�00000003070�15003540030�0020616�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#TEMPLATE = app #TARGET = ts #INCLUDEPATH += opensnitch # Input SOURCES += ../opensnitch/service.py \ ../opensnitch/notifications.py \ ../opensnitch/customwidgets/addresstablemodel.py \ ../opensnitch/customwidgets/main.py \ ../opensnitch/dialogs/prompt.py \ ../opensnitch/dialogs/preferences.py \ ../opensnitch/dialogs/ruleseditor.py \ ../opensnitch/dialogs/processdetails.py \ ../opensnitch/dialogs/stats.py \ ../opensnitch/dialogs/firewall.py \ ../opensnitch/dialogs/firewall_rule.py FORMS += ../opensnitch/res/prompt.ui \ ../opensnitch/res/ruleseditor.ui \ ../opensnitch/res/preferences.ui \ ../opensnitch/res/process_details.ui \ ../opensnitch/res/stats.ui \ ../opensnitch/res/firewall.ui \ ../opensnitch/res/firewall_rule.ui TRANSLATIONS += locales/de_DE/opensnitch-de_DE.ts \ locales/es_ES/opensnitch-es_ES.ts \ locales/eu_ES/opensnitch-eu_ES.ts \ locales/hu_HU/opensnitch-hu_HU.ts \ locales/ja_JP/opensnitch-ja_JP.ts \ locales/pt_BR/opensnitch-pt_BR.ts \ locales/ro_RO/opensnitch-ro_RO.ts \ locales/fr_FR/opensnitch-fr_FR.ts \ locales/lt_LT/opensnitch-lt_LT.ts \ locales/tr_TR/opensnitch-tr_TR.ts \ locales/ru_RU/opensnitch-ru_RU.ts \ locales/nb_NO/opensnitch-nb_NO.ts \ locales/nl_NL/opensnitch-nl_NL.ts \ locales/fi_FI/opensnitch-fi_FI.ts \ locales/zh_TW/opensnitch-zh_TW.ts ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016456�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/__init__.py����������������������������������������������������������0000664�0000000�0000000�00000000000�15003540030�0020555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/actions/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020116�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/actions/__init__.py��������������������������������������������������0000664�0000000�0000000�00000011735�15003540030�0022236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtCore import QObject import json import os import glob import sys from opensnitch.utils.xdg import xdg_config_home from opensnitch.actions import highlight from opensnitch.actions.default_configs import commonDelegateConfig, rulesDelegateConfig, fwDelegateConfig class Actions(QObject): """List of actions to perform on the data that is displayed on the GUI. Whenever an item matches a condition an action is applied, for example: - if the text of a cell matches a condition for the given columns, then the properties of the cell/row and the text are customized. There's only 1 action supported right now: - highlight: for customizing rows and cells appearance. There're 3 actions by default of type Highlight: - rules: applied to the rules to colorize the columns Enabled and Action - firewall: applied to the fw rules to colorize the columns Action and Target. - common: applied to the rest of the views to colorize the column Action. Users can modify the default actions, by adding more patterns to colorize. At the same time they can also create new actions to be applied on certain views. The format of the actions is JSON: { "created": "....", "name": "...", "actions": { "highlight": { "cells": [ { "text": ["allow", "True", "online"], "cols": [3,5,6], "color": "green", }, { "text": ["deny", "False", "offline"], "cols": [3,5,6], "color": "red", } ], "rows": [] } } """ __instance = None # list of loaded actions _actions = None KEY_ACTIONS = "actions" KEY_NAME = "name" KEY_TYPE = "type" # TODO: emit a signal when the actions are (re)loaded # reloaded_signal = pyQtSignal() # default paths to look for actions _paths = [ os.path.dirname(sys.modules[__name__].__file__) + "/data/", "{0}/{1}".format(xdg_config_home, "/opensnitch/actions/") ] @staticmethod def instance(): if Actions.__instance == None: Actions.__instance = Actions() return Actions.__instance def __init__(self, parent=None): QObject.__init__(self) self._actions_list = {} try: base_dir = "{0}/{1}".format(xdg_config_home, "/opensnitch/actions/") os.makedirs(base_dir, 0o700) except: pass def _load_default_configs(self): self._actions_list[commonDelegateConfig[Actions.KEY_NAME]] = self.compile(commonDelegateConfig) self._actions_list[rulesDelegateConfig[Actions.KEY_NAME]] = self.compile(rulesDelegateConfig) self._actions_list[fwDelegateConfig[Actions.KEY_NAME]] = self.compile(fwDelegateConfig) def loadAll(self): """look for actions firstly on default system path, secondly on user's home. If a user customizes existing configurations, they'll be saved under the user's home directory. Action files are .json files. """ self._load_default_configs() for path in self._paths: for jfile in glob.glob(os.path.join(path, '*.json')): self.load(jfile) def load(self, action_file): """read a json file from disk and create the action.""" with open(action_file, 'r') as fd: data=fd.read() obj = json.loads(data) self._actions_list[obj[Actions.KEY_NAME]] = self.compile(obj) def compile(self, obj): try: if Actions.KEY_NAME not in obj or obj[Actions.KEY_NAME] == "": return None if obj.get(Actions.KEY_ACTIONS) == None: return None for action in obj[Actions.KEY_ACTIONS]: if action == highlight.Highlight.NAME: h = highlight.Highlight(obj[Actions.KEY_ACTIONS][action]) h.compile() obj[Actions.KEY_ACTIONS][action]= h else: print("Actions exception: Action '{0}' not supported yet".format(obj[Actions.KEY_NAME])) return obj except Exception as e: print("Actions.compile() exception:", e) return None def getAll(self): return self._actions_list def deleteAll(self): self._actions_list = {} def get(self, name): try: return self._actions_list[name] except Exception as e: print("get() exception:", e) return None def delete(self, name): try: del self._actions_list[name] # TODO: # self.reloaded_signal.emit() except: pass def isValid(self): pass �����������������������������������opensnitch-1.6.9/ui/opensnitch/actions/default_configs.py�������������������������������������������0000664�0000000�0000000�00000005215�15003540030�0023627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ # common configuration to highlight Action column commonDelegateConfig = { "name": "commonDelegateConfig", "created": "", "updated": "", "actions": { "highlight": { "cells": [ { "text": ["allow", "\u2713 online"], "operator": "==", "cols": [1, 2, 3], "color": "green", "bgcolor": "", "alignment": ["center"] }, { "text": ["deny", "\u2613 offline"], "cols": [1, 2, 3], "color": "red", "bgcolor": "", "alignment": ["center"] }, { "text": ["reject"], "cols": [1, 2, 3], "color": "purple", "bgcolor": "", "alignment": ["center"] } ], "rows": [] } } } # firewall rules configuration to highlight Enabled and Action columns fwDelegateConfig = { "name": "defaultFWDelegateConfig", "created": "", "updated": "", "actions": { "highlight": { "cells": [ { "text": [ "allow", "True", "accept", "jump", "masquerade", "snat", "dnat", "tproxy", "queue", "redirect", "True", "ACCEPT" ], "cols": [7, 10], "color": "green", "bgcolor": "", "alignment": ["center"] }, { "text": [ "deny", "False", "drop", "DROP", "stop" ], "cols": [7, 10], "color": "red", "bgcolor": "", "alignment": ["center"] }, { "text": [ "reject", "return" ], "cols": [7, 10], "color": "purple", "bgcolor": "", "alignment": ["center"] } ], "rows": [] } } } # rules configuration to highlight Enabled and Action columns rulesDelegateConfig = { "name": "defaultRulesDelegateConfig", "created": "", "updated": "", "actions": { "highlight": { "cells": [ { "text": ["allow", "True"], "cols": [3, 4], "color": "green", "bgcolor": "", "alignment": ["center"] }, { "text": ["deny", "False"], "cols": [3, 4], "color": "red", "bgcolor": "", "alignment": ["center"] }, { "text": ["reject"], "cols": [3, 4], "color": "purple", "bgcolor": "", "alignment": ["center"] } ], "rows": [] } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/actions/highlight.py�������������������������������������������������0000664�0000000�0000000�00000016522�15003540030�0022445�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import Qt, QtCore from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem # PyQt5 >= v5.15.8 (#821) if hasattr(Qt, 'QStyle'): from PyQt5.Qt import QStyle else: from PyQt5.QtWidgets import QStyle class Highlight(): """Customizes QTablewView cells via QItemDelegates. Format: [ { 'text': {"allow", "True", "online"}, 'cols': {1,4,5}, 'color': "green", 'bgcolor': None, 'alignment': ["center"], #"margins': [0, 0] #'font': {} }, ] text: will match any of the given texts. cols: look for patterns on these columns. color: colorizes the color of the text. bgcolor: colorizes the background color of the cell. etc. """ NAME = "highlight" MARGINS = "margins" ALIGNMENT = "alignment" # QtCore.Qt.AlignCenter ALIGN_CENTER = "center" # QtCore.Qt.AlignHCenter ALIGN_HCENTER = "hcenter" # QtCore.Qt.AlignVCenter ALIGN_VCENTER = "vcenter" COLOR = "color" BGCOLOR = "bgcolor" FONT = "font" CELLS = "cells" ROWS = "rows" COLS = "cols" TEXT = "text" def __init__(self, config): # original json config received self._config = config self._last_visited_row = -1 self._rowcells = "" def compile(self): """transform json items to Qt objects. These items are transformed: - color (to QColor), bgcolor (to QColor), alignment (to Qt.Align*), font (to QFont TODO) Return the original json object transformed. """ # cells, rows for idx in self._config: cells = self._config[idx] for cell in cells: for item in cell: # colors if (item == Highlight.COLOR or item == Highlight.BGCOLOR): if cell[item] != "" and cell[item] is not None: cell[item] = QColor(cell[item]) else: cell[item] = None # alignments if item == Highlight.ALIGNMENT: cell[item] = self.getAlignment(cell[item]) # fonts if item == Highlight.FONT: self.getFont(cell[item]) return self._config def run(self, args): """Highlight cells or rows based on patterns. Return if the cell was modified. Keyword arguments: args -- tuple of options. """ painter = args[0] option = args[1] index = args[2] style = args[3] modelColumns = args[4] curRow = args[5] curColumn = args[6] defaultPen = args[7] defaultBrush = args[8] cellAlignment = args[9] cellRect = args[10] cellValue = args[11] # signal that this cell has been modified modified = False cells = self._config.get(Highlight.CELLS) rows = self._config.get(Highlight.ROWS) if cells: for cell in cells: if curColumn not in cell[Highlight.COLS]: continue if cellValue not in cell[Highlight.TEXT]: continue # TODO # if cell['operator'] == 'simple' and cellValue != cell['text']: # continue # elif cell['text'] not in cellValue: # continue cellColor = cell.get(Highlight.COLOR) cellBgColor = cell.get(Highlight.BGCOLOR) if cell.get(Highlight.ALIGNMENT) != None: cellAlignment = cell[Highlight.ALIGNMENT] if cell.get(Highlight.MARGINS) != None: cellRect.adjust( int(cell[Highlight.MARGINS][self.HMARGIN]), int(cell[Highlight.MARGINS][self.VMARGIN]), -defaultPen.width(), -defaultPen.width() ) modified=True self.paintCell( style, painter, option, defaultPen, cellAlignment, cellRect, cellColor, cellBgColor, cellValue) if len(rows) == 0: return (modified,) # get row's cells only for the first cell of the row, # then reuse them for the rest of the cells of the current row. if curRow != self._last_visited_row: self._rowcells = " ".join( [index.sibling(curRow, col).data() for col in range(0, modelColumns)] ) self._last_visited_row = curRow for row in rows: skip = True for text in row[Highlight.TEXT]: if text in self._rowcells: skip = False if skip: continue cellColor = row.get(Highlight.COLOR) cellBgColor = row.get(Highlight.BGCOLOR) if row.get(Highlight.ALIGNMENT) != None: cellAlignment = row[Highlight.ALIGNMENT] if row.get(Highlight.MARGINS) != None: cellRect.adjust( int(row[Highlight.MARGINS][self.HMARGIN]), int(row[Highlight.MARGINS][self.VMARGIN]), -defaultPen.width(), -defaultPen.width() ) modified=True self.paintCell( style, painter, option, defaultPen, cellAlignment, cellRect, cellColor, cellBgColor, cellValue) return (modified,) def paintCell(self, style, painter, option, defaultPen, cellAlignment, cellRect, cellColor, cellBgColor, cellValue): cellSelected = option.state & QStyle.State_Selected painter.save() # don't customize selected state if not cellSelected: if cellBgColor != None: painter.fillRect(option.rect, cellBgColor) if cellColor is not None: defaultPen.setColor(cellColor) painter.setPen(defaultPen) # setting option.displayAlignment has no effect here, so we need to # draw the text. # FIXME: Drawing the text though, the background color of the SelectedState is # altered. # If we called super().paint(), modifying option.palette.* would be # enough to change the text color, but it wouldn't be aligned: # option.palette.setColor(QPalette.Text, cellColor) style.drawItemText(painter, cellRect, cellAlignment, option.palette, True, cellValue) painter.restore() def getAlignment(self, alignments): alignFlags = 0 for align in alignments: if align == Highlight.ALIGN_CENTER: alignFlags |= QtCore.Qt.AlignCenter elif align == Highlight.ALIGN_HCENTER: alignFlags |= QtCore.Qt.AlignHCenter elif align == Highlight.ALIGN_VCENTER: alignFlags |= QtCore.Qt.AlignVCenter if alignFlags == 0: return None return alignFlags def getFont(self, font): # TODO pass ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/actions/utils.py�����������������������������������������������������0000664�0000000�0000000�00000000473�15003540030�0021634�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtGui import QColor def getColorNames(): """Return the built-in color names that can be used to choose new colors: https://doc.qt.io/qtforpython-5/PySide2/QtGui/QColor.html#predefined-colors https://www.w3.org/TR/SVG11/types.html#ColorKeywords """ return QColor.colorNames() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/auth/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017417�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/auth/__init__.py�����������������������������������������������������0000664�0000000�0000000�00000002036�15003540030�0021531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ import grpc Simple = "simple" TLSSimple = "tls-simple" TLSMutual = "tls-mutual" NO_CLIENT_CERT = "no-client-cert" REQ_CERT = "req-cert" REQ_ANY_CERT = "req-any-cert" VERIFY_CERT = "verify-cert" REQ_AND_VERIFY_CERT = "req-and-verify-cert" def load_file(file_path): try: with open(file_path, "rb") as f: return f.read() except Exception as e: print("auth: error loading {0}: {1}".format(file_path, e)) return None def get_tls_credentials(ca_cert, server_cert, server_key): """return a new gRPC credentials object given a server cert and key file. https://grpc.io/docs/guides/auth/#python """ try: cacert = load_file(ca_cert) cert = load_file(server_cert) cert_key = load_file(server_key) auth_nodes = False if cacert == None else True return grpc.ssl_server_credentials( ((cert_key, cert),), cacert, auth_nodes ) except Exception as e: print("get_tls_credentials error:", e) return None ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/config.py������������������������������������������������������������0000664�0000000�0000000�00000023603�15003540030�0020301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import QtCore from opensnitch.database import Database class Config: __instance = None HELP_URL = "https://github.com/evilsocket/opensnitch/wiki/" HELP_RULES_URL = "https://github.com/evilsocket/opensnitch/wiki/Rules" HELP_SYS_RULES_URL = "https://github.com/evilsocket/opensnitch/wiki/System-rules#upgrading-from-previous-versions" HELP_SYSFW_URL = "https://github.com/evilsocket/opensnitch/wiki/System-rules" HELP_CONFIG_URL = "https://github.com/evilsocket/opensnitch/wiki/Configurations" HELP_SYSTRAY_WARN = "https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#gui-does-not-show-up" OPERAND_PROCESS_ID = "process.id" OPERAND_PROCESS_PATH = "process.path" OPERAND_PROCESS_COMMAND = "process.command" OPERAND_PROCESS_ENV = "process.env." OPERAND_USER_ID = "user.id" OPERAND_IFACE_OUT = "iface.out" OPERAND_IFACE_IN = "iface.in" OPERAND_SOURCE_IP = "source.ip" OPERAND_SOURCE_PORT = "source.port" OPERAND_DEST_IP = "dest.ip" OPERAND_DEST_HOST = "dest.host" OPERAND_DEST_PORT = "dest.port" OPERAND_DEST_NETWORK = "dest.network" OPERAND_SOURCE_NETWORK = "source.network" OPERAND_PROTOCOL = "protocol" OPERAND_LIST_DOMAINS = "lists.domains" OPERAND_LIST_DOMAINS_REGEXP = "lists.domains_regexp" OPERAND_LIST_IPS = "lists.ips" OPERAND_LIST_NETS = "lists.nets" RULE_TYPE_LIST = "list" RULE_TYPE_LISTS = "lists" RULE_TYPE_SIMPLE = "simple" RULE_TYPE_REGEXP = "regexp" RULE_TYPE_NETWORK = "network" RulesTypes = (RULE_TYPE_LIST, RULE_TYPE_LISTS, RULE_TYPE_SIMPLE, RULE_TYPE_REGEXP, RULE_TYPE_NETWORK) DEFAULT_TARGET_PROCESS = 0 ACTION_DENY_IDX = 0 ACTION_ALLOW_IDX = 1 ACTION_REJECT_IDX = 2 # don't translate ACTION_ALLOW = "allow" ACTION_DENY = "deny" ACTION_REJECT = "reject" ACTION_ACCEPT = "accept" ACTION_DROP = "drop" ACTION_JUMP = "jump" ACTION_REDIRECT = "redirect" ACTION_RETURN = "return" ACTION_TPROXY = "tproxy" ACTION_SNAT = "snat" ACTION_DNAT = "dnat" ACTION_MASQUERADE = "masquerade" ACTION_QUEUE = "queue" ACTION_LOG = "log" ACTION_STOP = "stop" DURATION_FIELD = "duration" DURATION_UNTIL_RESTART = "until restart" DURATION_ALWAYS = "always" DURATION_ONCE = "once" DURATION_1h = "1h" DURATION_30m = "30m" DURATION_15m = "15m" DURATION_5m = "5m" DURATION_30s = "30s" # Rules of this list are ignored/deleted RULES_DURATION_FILTER = () # Rules of this list are active RULES_ACTIVE_TEMPORARY_RULES = () RULES_TEMPORARY_LIST = [ DURATION_ONCE, DURATION_30s, DURATION_5m, DURATION_15m, DURATION_30m, DURATION_1h, DURATION_UNTIL_RESTART] DEFAULT_DURATION_IDX = 6 # until restart POPUP_CENTER = 0 POPUP_TOP_RIGHT = 1 POPUP_BOTTOM_RIGHT = 2 POPUP_TOP_LEFT = 3 POPUP_BOTTOM_LEFT = 4 DEFAULT_THEME = "global/theme" DEFAULT_THEME_DENSITY_SCALE = "global/theme_density_scale" DEFAULT_LANGUAGE = "global/language" DEFAULT_LANGNAME = "global/langname" DEFAULT_DISABLE_POPUPS = "global/disable_popups" DEFAULT_TIMEOUT_KEY = "global/default_timeout" DEFAULT_ACTION_KEY = "global/default_action" DEFAULT_DURATION_KEY = "global/default_duration" DEFAULT_TARGET_KEY = "global/default_target" DEFAULT_IGNORE_RULES = "global/default_ignore_rules" DEFAULT_IGNORE_TEMPORARY_RULES = "global/default_ignore_temporary_rules" DEFAULT_POPUP_POSITION = "global/default_popup_position" DEFAULT_POPUP_ADVANCED = "global/default_popup_advanced" DEFAULT_POPUP_ADVANCED_DSTIP = "global/default_popup_advanced_dstip" DEFAULT_POPUP_ADVANCED_DSTPORT = "global/default_popup_advanced_dstport" DEFAULT_POPUP_ADVANCED_UID = "global/default_popup_advanced_uid" DEFAULT_SERVER_ADDR = "global/server_address" DEFAULT_SERVER_MAX_MESSAGE_LENGTH = "global/server_max_message_length" DEFAULT_HIDE_SYSTRAY_WARN = "global/hide_systray_warning" DEFAULT_DB_TYPE_KEY = "database/type" DEFAULT_DB_FILE_KEY = "database/file" DEFAULT_DB_PURGE_OLDEST = "database/purge_oldest" DEFAULT_DB_MAX_DAYS = "database/max_days" DEFAULT_DB_PURGE_INTERVAL = "database/purge_interval" DEFAULT_DB_JRNL_WAL = "database/jrnl_wal" DEFAULT_TIMEOUT = 30 NOTIFICATIONS_ENABLED = "notifications/enabled" NOTIFICATIONS_TYPE = "notifications/type" NOTIFICATION_TYPE_SYSTEM = 0 NOTIFICATION_TYPE_QT = 1 STATS_REFRESH_INTERVAL = "statsDialog/refresh_interval" STATS_GEOMETRY = "statsDialog/geometry" STATS_LAST_TAB = "statsDialog/last_tab" STATS_FILTER_TEXT = "statsDialog/general_filter_text" STATS_FILTER_ACTION = "statsDialog/general_filter_action" STATS_LIMIT_RESULTS = "statsDialog/general_limit_results" STATS_SHOW_COLUMNS = "statsDialog/show_columns" STATS_NODES_COL_STATE = "statsDialog/nodes_columns_state" STATS_GENERAL_COL_STATE = "statsDialog/general_columns_state" STATS_GENERAL_FILTER_TEXT = "statsDialog/" STATS_GENERAL_FILTER_ACTION = "statsDialog/" STATS_RULES_COL_STATE = "statsDialog/rules_columns_state" STATS_FW_COL_STATE = "statsDialog/firewall_columns_state" STATS_RULES_TREE_EXPANDED_0 = "statsDialog/rules_tree_0_expanded" STATS_RULES_TREE_EXPANDED_1 = "statsDialog/rules_tree_1_expanded" STATS_RULES_SPLITTER_POS = "statsDialog/rules_splitter_pos" STATS_VIEW_COL_STATE = "statsDialog/view_columns_state" STATS_VIEW_DETAILS_COL_STATE = "statsDialog/view_details_columns_state" QT_PLATFORM_PLUGIN = "global/qt_platform_plugin" QT_AUTO_SCREEN_SCALE_FACTOR = "global/screen_scale_factor_auto" QT_SCREEN_SCALE_FACTOR = "global/screen_scale_factor" INFOWIN_GEOMETRY = "infoWindow/geometry" AUTH_TYPE = "auth/type" AUTH_CA_CERT = "auth/cacert" AUTH_CERT = "auth/cert" AUTH_CERTKEY = "auth/certkey" # don't translate @staticmethod def init(): Config.__instance = Config() return Config.__instance @staticmethod def get(): if Config.__instance == None: Config._instance = Config() return Config.__instance def __init__(self): self.settings = QtCore.QSettings("opensnitch", "settings") if self.settings.value(self.DEFAULT_TIMEOUT_KEY) == None: self.setSettings(self.DEFAULT_TIMEOUT_KEY, self.DEFAULT_TIMEOUT) if self.settings.value(self.DEFAULT_ACTION_KEY) == None: self.setSettings(self.DEFAULT_ACTION_KEY, self.ACTION_DENY_IDX) if self.settings.value(self.DEFAULT_DURATION_KEY) == None: self.setSettings(self.DEFAULT_DURATION_KEY, self.DEFAULT_DURATION_IDX) if self.settings.value(self.DEFAULT_TARGET_KEY) == None: self.setSettings(self.DEFAULT_TARGET_KEY, self.DEFAULT_TARGET_PROCESS) if self.settings.value(self.DEFAULT_DB_TYPE_KEY) == None: self.setSettings(self.DEFAULT_DB_TYPE_KEY, Database.DB_TYPE_MEMORY) self.setSettings(self.DEFAULT_DB_FILE_KEY, Database.DB_IN_MEMORY) self.setSettings(self.DEFAULT_DB_JRNL_WAL, Database.DB_JRNL_WAL) self.setRulesDurationFilter( self.getBool(self.DEFAULT_IGNORE_RULES), self.getInt(self.DEFAULT_IGNORE_TEMPORARY_RULES) ) def reload(self): self.settings = QtCore.QSettings("opensnitch", "settings") def hasKey(self, key): return self.settings.contains(key) def setSettings(self, path, value): self.settings.setValue(path, value) self.settings.sync() def getSettings(self, path): return self.settings.value(path) def getBool(self, path, default_value=False): return self.settings.value(path, type=bool, defaultValue=default_value) def getInt(self, path, default_value=0): try: return self.settings.value(path, type=int, defaultValue=default_value) except Exception: return default_value def getDefaultAction(self): _default_action = self.getInt(self.DEFAULT_ACTION_KEY) if _default_action == self.ACTION_ALLOW_IDX: return self.ACTION_ALLOW else: return self.ACTION_DENY def setRulesDurationFilter(self, ignore_temporary_rules=False, temp_rules=1): try: if ignore_temporary_rules: Config.RULES_DURATION_FILTER = [ Config.DURATION_ONCE, Config.DURATION_30s, Config.DURATION_5m, Config.DURATION_15m, Config.DURATION_30m, Config.DURATION_1h, Config.DURATION_UNTIL_RESTART] Config.RULES_DURATION_FILTER = [ rule for rule in Config.RULES_TEMPORARY_LIST if Config.RULES_TEMPORARY_LIST.index(rule) < temp_rules ] Config.RULES_ACTIVE_TEMPORARY_RULES = [ rule for rule in Config.RULES_TEMPORARY_LIST if Config.RULES_TEMPORARY_LIST.index(rule) >= temp_rules ] #print("Temp rules preserved (RULES_DURATION_FILTER):", Config.RULES_DURATION_FILTER) #print("Temp rules to delete (ACTIVE_TEMPORARY_RULES):", Config.RULES_ACTIVE_TEMPORARY_RULES) else: Config.RULES_DURATION_FILTER = [] except Exception as e: print("setRulesDurationFilter() exception:", e) def getMaxMsgLength(self): """return maximum configured length for the gRPC channel. Default size is 4MB, but in some scenarios it's not enough. """ maxmsglen = 4194304 maxmsglencfg = self.getSettings(Config.DEFAULT_SERVER_MAX_MESSAGE_LENGTH) if maxmsglencfg == '4MiB': maxmsglen = 4194304 elif maxmsglencfg == '8MiB': maxmsglen = 8388608 elif maxmsglencfg == '16MiB': maxmsglen = 16777216 print("gRPC Max Message Length:", maxmsglencfg) print(" Bytes:", maxmsglen) return maxmsglen �����������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/�������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0021357�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/__init__.py��������������������������������������������0000664�0000000�0000000�00000000000�15003540030�0023456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/addresstablemodel.py�����������������������������������0000664�0000000�0000000�00000004256�15003540030�0025416�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5.QtSql import QSqlQuery from opensnitch.utils import AsnDB from opensnitch.customwidgets.generictableview import GenericTableModel from PyQt5.QtCore import QCoreApplication as QC class AddressTableModel(GenericTableModel): def __init__(self, tableName, headerLabels): super().__init__(tableName, headerLabels) self.asndb = AsnDB.instance() self.reconfigureColumns() def reconfigureColumns(self): self.headerLabels = [] self.setHorizontalHeaderLabels(self.headerLabels) self.headerLabels.append(QC.translate("stats", "What", "")) self.headerLabels.append(QC.translate("stats", "Hits", "")) self.headerLabels.append(QC.translate("stats", "Network name", "")) self.setHorizontalHeaderLabels(self.headerLabels) self.setColumnCount(len(self.headerLabels)) self.lastColumnCount = len(self.headerLabels) def setQuery(self, q, db): self.origQueryStr = q self.db = db if self.prevQueryStr != self.origQueryStr: self.realQuery = QSqlQuery(q, db) self.realQuery.exec_() self.realQuery.last() queryRows = max(0, self.realQuery.at()+1) self.totalRowCount = queryRows self.setRowCount(self.totalRowCount) queryColumns = self.realQuery.record().count() if self.asndb.is_available() and queryColumns < 3: self.reconfigureColumns() else: # update view's columns if queryColumns != self.lastColumnCount: self.setModelColumns(queryColumns) self.prevQueryStr = self.origQueryStr self.rowCountChanged.emit() def fillVisibleRows(self, q, upperBound, force=False): super().fillVisibleRows(q, upperBound, force) if self.asndb.is_available() == True and self.columnCount() <= 3: for n, col in enumerate(self.items): try: if len(col) < 2: continue col[2] = self.asndb.get_asn(col[0]) except: col[2] = "" finally: self.items[n] = col self.lastItems = self.items ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/colorizeddelegate.py�����������������������������������0000664�0000000�0000000�00000005700�15003540030�0025420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import Qt, QtCore from PyQt5.QtWidgets import QApplication # PyQt5 >= v5.15.8 (28/01/2023) (#821) if hasattr(Qt, 'QItemDelegate'): from PyQt5.Qt import QItemDelegate, QStyleOptionViewItem else: from PyQt5.QtWidgets import QItemDelegate, QStyleOptionViewItem class ColorizedDelegate(QItemDelegate): HMARGIN = 0 VMARGIN = 1 def __init__(self, parent=None, *args, actions={}): QItemDelegate.__init__(self, parent, *args) self._actions = actions self.modelColumns = parent.model().columnCount() self._style = QApplication.style() def setConfig(self, actions): self._actions = actions #@profile_each_line def paint(self, painter, option, index): """Override default widget style to personalize it with our own. """ if self._actions.get('actions') == None: return super().paint(painter, option, index) if not index.isValid(): return super().paint(painter, option, index) cellValue = index.data(QtCore.Qt.DisplayRole) if cellValue == None: return super().paint(painter, option, index) # initialize new QStyleOptionViewItem with the default options of this # cell. option = QStyleOptionViewItem(option) # by default use item's default attributes. # if we modify any of them, set it to False nocolor=True # don't call these functions in for-loops cellRect = QtCore.QRect(option.rect) curColumn = index.column() curRow = index.row() cellAlignment = option.displayAlignment defaultPen = painter.pen() defaultBrush = painter.brush() self._style = QApplication.style() # get default margins in order to respect them. # option.widget is the QTableView hmargin = self._style.pixelMetric( self._style.PM_FocusFrameHMargin, None, option.widget ) + 1 vmargin = self._style.pixelMetric( self._style.PM_FocusFrameVMargin, None, option.widget ) + 1 # set default margins for this cell cellRect.adjust(hmargin, vmargin, -painter.pen().width(), -painter.pen().width()) for a in self._actions['actions']: action = self._actions['actions'][a] modified = action.run( (painter, option, index, self._style, self.modelColumns, curRow, curColumn, defaultPen, defaultBrush, cellAlignment, cellRect, cellValue) ) if modified[0]: nocolor=False if nocolor: super().paint(painter, option, index) ����������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/firewalltableview.py�����������������������������������0000664�0000000�0000000�00000026177�15003540030�0025456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5 import QtCore from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtSql import QSqlQuery, QSqlError from PyQt5.QtWidgets import QTableView, QAbstractSlider, QItemDelegate, QAbstractItemView, QPushButton, QWidget, QVBoxLayout from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import QCoreApplication as QC from opensnitch.nodes import Nodes from opensnitch.firewall import Firewall from opensnitch.customwidgets.updownbtndelegate import UpDownButtonDelegate class FirewallTableModel(QStandardItemModel): rowCountChanged = pyqtSignal() columnCountChanged = pyqtSignal(int) rowsUpdated = pyqtSignal(int, tuple) rowsReordered = pyqtSignal(int, str, str, int, int) # filter, addr, key, old_pos, new_pos tableName = "" # total row count which must de displayed in the view totalRowCount = 0 # last column count to compare against with lastColumnCount = 0 FILTER_ALL = 0 FILTER_BY_NODE = 1 FILTER_BY_TABLE = 2 FILTER_BY_CHAIN = 3 FILTER_BY_QUERY = 4 activeFilter = FILTER_ALL UP_BTN = -1 DOWN_BTN = 1 COL_BTNS = 0 COL_UUID = 1 COL_ADDR = 2 COL_CHAIN_NAME = 3 COL_CHAIN_TABLE = 4 COL_CHAIN_FAMILY = 5 COL_CHAIN_HOOK = 6 COL_ENABLED = 7 COL_DESCRIPTION = 8 COL_PARMS = 9 COL_ACTION = 10 COL_ACTION_PARMS = 11 headersAll = [ "", # buttons "", # uuid QC.translate("firewall", "Node", ""), QC.translate("firewall", "Name", ""), QC.translate("firewall", "Table", ""), QC.translate("firewall", "Family", ""), QC.translate("firewall", "Hook", ""), QC.translate("firewall", "Enabled", ""), QC.translate("firewall", "Description", ""), QC.translate("firewall", "Parameters", ""), QC.translate("firewall", "Action", ""), QC.translate("firewall", "ActionParms", ""), ] items = [] lastRules = [] position = 0 def __init__(self, tableName): self.tableName = tableName self._nodes = Nodes.instance() self._fw = Firewall.instance() self.lastColumnCount = len(self.headersAll) self.lastQueryArgs = () QStandardItemModel.__init__(self, 0, self.lastColumnCount) self.setHorizontalHeaderLabels(self.headersAll) def filterByNode(self, addr): self.activeFilter = self.FILTER_BY_NODE self.fillVisibleRows(0, True, addr) def filterAll(self): self.activeFilter = self.FILTER_ALL self.fillVisibleRows(0, True) def filterByTable(self, addr, name, family): self.activeFilter = self.FILTER_BY_TABLE self.fillVisibleRows(0, True, addr, name, family) def filterByChain(self, addr, table, family, chain, hook): self.activeFilter = self.FILTER_BY_CHAIN self.fillVisibleRows(0, True, addr, table, family, chain, hook) def filterByQuery(self, query): self.activeFilter = self.FILTER_BY_QUERY self.fillVisibleRows(0, True, query) def reorderRows(self, action, row): if (row.row()+action == self.rowCount() and action == self.DOWN_BTN) or \ (row.row() == 0 and action == self.UP_BTN): return # XXX: better use moveRow()? newRow = [] # save the row we're about to overwrite for c in range(self.columnCount()): item = self.index(row.row()+action, c) itemText = item.data() newRow.append(itemText) # overwrite next item with current data for c in range(self.columnCount()): curItem = self.index(row.row(), c).data() nextIdx = self.index(row.row()+action, c) self.setData(nextIdx, curItem, QtCore.Qt.DisplayRole) # restore row with the overwritten data for i, nr in enumerate(newRow): idx = self.index(row.row(), i) self.setData(idx, nr, QtCore.Qt.DisplayRole) self.rowsReordered.emit( self.activeFilter, self.index(row.row()+action, self.COL_ADDR).data(), # address self.index(row.row()+action, self.COL_UUID).data(), # key row.row(), row.row()+action) def refresh(self, force=False): self.fillVisibleRows(0, force, *self.lastQueryArgs) #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement #mimic QSqlQueryModel.query() def query(self): return self #mimic QSqlQueryModel.query().lastError() def lastError(self): return QSqlError() #mimic QSqlQueryModel.clear() def clear(self): self.items = [] self.removeColumns(0, self.lastColumnCount) self.setColumnCount(0) self.setRowCount(0) # set columns based on query's fields def setModelColumns(self, headers): count = len(headers) self.clear() self.setHorizontalHeaderLabels(headers) self.lastColumnCount = count self.setColumnCount(self.lastColumnCount) self.columnCountChanged.emit(count) def query(self): return QSqlQuery() def setQuery(self, q, db, args=None): self.refresh() def nextRecord(self, offset): self.position += 1 def prevRecord(self, offset): self.position -= 1 def fillVisibleRows(self, upperBound, force, *data): if self.activeFilter == self.FILTER_BY_NODE and len(data) == 0: return cols = [] rules = [] #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells self.blockSignals(True) # mandatory for rows refreshing self.layoutAboutToBeChanged.emit() if self.activeFilter == self.FILTER_BY_NODE: rules = self._fw.get_node_rules(data[0]) self.setModelColumns(self.headersAll) elif self.activeFilter == self.FILTER_BY_TABLE: rules = self._fw.filter_by_table(data[0], data[1], data[2]) self.setModelColumns(self.headersAll) elif self.activeFilter == self.FILTER_BY_CHAIN: rules = self._fw.filter_by_chain(data[0], data[1], data[2], data[3], data[4]) self.setModelColumns(self.headersAll) elif self.activeFilter == self.FILTER_BY_QUERY: rules = self._fw.filter_rules(data[0]) self.setModelColumns(self.headersAll) else: self.setModelColumns(self.headersAll) rules = self._fw.get_rules() self.addRows(rules) self.blockSignals(False) if self.lastRules != rules or force == True: self.layoutChanged.emit() self.totalRowCount = len(rules) self.setRowCount(self.totalRowCount) self.rowsUpdated.emit(self.activeFilter, data) self.dataChanged.emit(self.createIndex(0,0), self.createIndex(self.rowCount(), self.columnCount())) self.lastRules = rules self.lastQueryArgs = data del cols del rules def addRows(self, rules): self.items = [] for rows in rules: cols = [] cols.append(QStandardItem("")) # buttons column for cl in rows: item = QStandardItem(cl) item.setData(cl, QtCore.Qt.UserRole+1) cols.append(item) self.appendRow(cols) def dumpRows(self): for rule in self.lastRules: print(rule) class FirewallTableView(QTableView): # how many rows can potentially be displayed in viewport # the actual number of rows currently displayed may be less than this maxRowsInViewport = 0 rowsReordered = pyqtSignal(str) # addr def __init__(self, parent): QTableView.__init__(self, parent) self._fw = Firewall.instance() self._fw.rules.rulesUpdated.connect(self._cb_fw_rules_updated) self.verticalHeader().setVisible(True) self.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignCenter) self.horizontalHeader().setStretchLastSection(True) # FIXME: if the firewall being used is iptables, hide the column to # reorder rules, it's not supported. updownBtn = UpDownButtonDelegate(self) self.setItemDelegateForColumn(0, updownBtn) updownBtn.clicked.connect(self._cb_fw_rule_position_changed) def _cb_fw_rules_updated(self): self.model().refresh(True) def _cb_column_count_changed(self, num): for i in range(num): self.resizeColumnToContents(i) def _cb_fw_rule_position_changed(self, action, row): self.model().reorderRows(action, row) def _cb_rows_reordered(self, view, node_addr, uuid, old_pos, new_pos): if self._fw.swap_rules(view, node_addr, uuid, old_pos, new_pos): self.rowsReordered.emit(node_addr) #@QtCore.pyqtSlot(int, tuple) def _cb_rows_updated(self, view, data): for c in range(self.model().rowCount()): self.setColumnHidden(c, False) #self.horizontalHeader().setSectionResizeMode( # c, QHeaderView.ResizeToContents #) self.setColumnHidden(FirewallTableModel.COL_BTNS, True) self.setColumnHidden(FirewallTableModel.COL_UUID, True) if view >= self.model().FILTER_BY_NODE: # hide address column self.setColumnHidden(FirewallTableModel.COL_ADDR, True) if view >= self.model().FILTER_BY_TABLE: self.setColumnHidden(FirewallTableModel.COL_CHAIN_TABLE, True) self.setColumnHidden(FirewallTableModel.COL_CHAIN_FAMILY, True) if view >= self.model().FILTER_BY_CHAIN: # hide chain's name, family and hook self.setColumnHidden(FirewallTableModel.COL_CHAIN_NAME, True) self.setColumnHidden(FirewallTableModel.COL_CHAIN_HOOK, True) self.setColumnHidden(FirewallTableModel.COL_BTNS, False) def filterAll(self): self.model().filterAll() def filterByNode(self, addr): self.model().filterByNode(addr) def filterByTable(self, addr, name, family): self.model().filterByTable(addr, name, family) def filterByChain(self, addr, table, family, chain, hook): self.model().filterByChain(addr, table, family, chain, hook) def filterByQuery(self, query): self.model().filterByQuery(query) def refresh(self): self.model().refresh(True) def clearSelection(self): pass def copySelection(self): selection = self.selectedIndexes() if not selection: return None rows = [] row = [] lastRow = 0 for idx in selection: if idx.row() == lastRow: row.append(self.model().index(idx.row(), idx.column()).data()) else: row = [] lastRow = idx.row() rows.append(row) return rows def setModel(self, model): super().setModel(model) self.horizontalHeader().sortIndicatorChanged.disconnect() self.setSortingEnabled(True) self.model().columnCountChanged.connect(self._cb_column_count_changed) model.rowsUpdated.connect(self._cb_rows_updated) model.rowsReordered.connect(self._cb_rows_reordered) def setTrackingColumn(self, col): pass �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/generictableview.py������������������������������������0000664�0000000�0000000�00000040664�15003540030�0025262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql from PyQt5.QtWidgets import QTableView, QAbstractSlider from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent, Qt import time import math from PyQt5.QtCore import QCoreApplication as QC class GenericTableModel(QStandardItemModel): rowCountChanged = pyqtSignal() beginViewPortRefresh = pyqtSignal() endViewPortRefresh = pyqtSignal() db = None tableName = "" # total row count which must de displayed in the view totalRowCount = 0 # lastColumnCount = 0 # original query string before we modify it origQueryStr = QSqlQuery() # previous original query string; used to check if the query has changed prevQueryStr = '' # modified query object realQuery = QSqlQuery() items = [] lastItems = [] def __init__(self, tableName, headerLabels): self.tableName = tableName self.headerLabels = headerLabels self.lastColumnCount = len(self.headerLabels) QStandardItemModel.__init__(self, 0, self.lastColumnCount) self.setHorizontalHeaderLabels(self.headerLabels) #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement #mimic QSqlQueryModel.query() def query(self): return self #mimic QSqlQueryModel.query().lastQuery() def lastQuery(self): return self.origQueryStr #mimic QSqlQueryModel.query().lastError() def lastError(self): return self.realQuery.lastError() #mimic QSqlQueryModel.clear() def clear(self): pass def rowCount(self, index=None): """ensures that only the needed rows is created""" return len(self.items) def data(self, index, role=Qt.DisplayRole): """Paint rows with the data stored in self.items """ if role == Qt.DisplayRole or role == Qt.EditRole: items_count = len(self.items) if index.isValid() and items_count > 0 and index.row() < items_count: return self.items[index.row()][index.column()] return QStandardItemModel.data(self, index, role) # set columns based on query's fields def setModelColumns(self, newColumns): # Avoid firing signals while reconfiguring the view, it causes # segfaults. self.blockSignals(True); self.headerLabels = [] self.removeColumns(0, self.lastColumnCount) self.setHorizontalHeaderLabels(self.headerLabels) for col in range(0, newColumns): self.headerLabels.append(self.realQuery.record().fieldName(col)) self.lastColumnCount = newColumns self.setHorizontalHeaderLabels(self.headerLabels) self.setColumnCount(len(self.headerLabels)) self.blockSignals(False); def setQuery(self, q, db): self.origQueryStr = q self.db = db #print("q:", q) if self.prevQueryStr != self.origQueryStr: self.realQuery = QSqlQuery(q, db) self.realQuery.exec_() self.realQuery.last() queryRows = max(0, self.realQuery.at()+1) self.totalRowCount = queryRows self.setRowCount(self.totalRowCount) # update view's columns queryColumns = self.realQuery.record().count() if queryColumns != self.lastColumnCount: self.setModelColumns(queryColumns) self.prevQueryStr = self.origQueryStr self.rowCountChanged.emit() def nextRecord(self, offset): cur_pos = self.realQuery.at() q.seek(max(cur_pos, cur_pos+offset)) def prevRecord(self, offset): cur_pos = self.realQuery.at() q.seek(min(cur_pos, cur_pos-offset)) def refreshViewport(self, scrollValue, maxRowsInViewport, force=False): """Refresh the viewport with data from the db. Before making any changes, emit a signal which will perform several operations (save current selected row, etc). force var will force a refresh if the scrollbar is at the top or bottom of the viewport, otherwise skip it to allow rows analyzing without refreshing. """ if not force: return self.beginViewPortRefresh.emit() # set records position to last, in order to get correctly the number of # rows. self.realQuery.last() rowsFound = max(0, self.realQuery.at()+1) if scrollValue == 0 or self.realQuery.at() == QSql.BeforeFirstRow: self.realQuery.seek(QSql.BeforeFirstRow) elif self.realQuery.at() == QSql.AfterLastRow: self.realQuery.seek(rowsFound - maxRowsInViewport) else: self.realQuery.seek(min(scrollValue-1, self.realQuery.at())) upperBound = min(maxRowsInViewport, rowsFound) self.setRowCount(self.totalRowCount) # only visible rows will be filled with data, and only if we're not # updating the viewport already. if force and (upperBound > 0 or self.realQuery.at() < 0): self.fillVisibleRows(self.realQuery, upperBound, force) self.endViewPortRefresh.emit() def fillVisibleRows(self, q, upperBound, force=False): rowsLabels = [] self.setVerticalHeaderLabels(rowsLabels) self.items = [] cols = [] #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells for x in range(0, upperBound): q.next() if q.at() < 0: # if we don't set query to a valid record here, it gets stucked # forever at -2/-1. q.seek(upperBound) break rowsLabels.append(str(q.at()+1)) cols = [] for col in range(0, len(self.headerLabels)): cols.append(str(q.value(col))) self.items.append(cols) self.setVerticalHeaderLabels(rowsLabels) if self.lastItems != self.items or force == True: self.dataChanged.emit(self.createIndex(0,0), self.createIndex(upperBound, len(self.headerLabels))) self.lastItems = self.items del cols def dumpRows(self): rows = [] q = QSqlQuery(self.db) q.exec(self.origQueryStr) q.seek(QSql.BeforeFirstRow) while True: q.next() if q.at() == QSql.AfterLastRow: break row = [] for col in range(0, len(self.headerLabels)): row.append(q.value(col)) rows.append(row) return rows def copySelectedRows(self, start=QSql.BeforeFirstRow, end=QSql.AfterLastRow): rows = [] lastAt = self.realQuery.at() self.realQuery.seek(start) while True: self.realQuery.next() if self.realQuery.at() == QSql.AfterLastRow or len(rows) >= end: break row = [] for col in range(0, len(self.headerLabels)): row.append(self.realQuery.value(col)) rows.append(row) self.realQuery.seek(lastAt) return rows class GenericTableView(QTableView): # how many rows can potentially be displayed in viewport # the actual number of rows currently displayed may be less than this maxRowsInViewport = 0 vScrollBar = None curSelection = None trackingCol = 0 def __init__(self, parent): QTableView.__init__(self, parent) self.mousePressed = False #eventFilter to catch key up/down events and wheel events self.verticalHeader().setVisible(True) self.horizontalHeader().setDefaultAlignment(Qt.AlignCenter) self.horizontalHeader().setStretchLastSection(True) #the built-in vertical scrollBar of this view is always off self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.installEventFilter(self) def setVerticalScrollBar(self, vScrollBar): self.vScrollBar = vScrollBar self.vScrollBar.valueChanged.connect(self.onScrollbarValueChanged) self.vScrollBar.setVisible(False) def setModel(self, model): super().setModel(model) model.rowCountChanged.connect(self.onRowCountChanged) model.beginViewPortRefresh.connect(self.onBeginViewportRefresh) model.endViewPortRefresh.connect(self.onEndViewportRefresh) self.horizontalHeader().sortIndicatorChanged.disconnect() self.setSortingEnabled(False) def setTrackingColumn(self, col): """column used to track a selected row while scrolling""" self.trackingCol = col def clear(self): pass def refresh(self): self.calculateRowsInViewport() self.model().setRowCount(min(self.maxRowsInViewport, self.model().totalRowCount)) self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport, force=True) def forceViewRefresh(self): return (self.vScrollBar.minimum() == self.vScrollBar.value() or self.vScrollBar.maximum() == self.vScrollBar.value()) def calculateRowsInViewport(self): rowHeight = self.verticalHeader().defaultSectionSize() #columnSize = self.horizontalHeader().defaultSectionSize() # we don't want partial-height rows in viewport, hence .floor() self.maxRowsInViewport = math.floor(self.viewport().height() / rowHeight)+1 def currentChanged(self, cur, prev): #super().currentChanged(cur, prev) if not self.mousePressed or prev.row() == cur.row(): return maxVal = self.maxRowsInViewport-1 if cur.row() >= maxVal or prev.row() >= maxVal: self.vScrollBar.setValue(self.vScrollBar.value() + 1) elif cur.row() == 0: self.vScrollBar.setValue(max(0, self.vScrollBar.value() - 1)) def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) self.mousePressed = False # save the selected index, to preserve selection when moving around. def mousePressEvent(self, event): # we need to call upper class to paint selections properly super().mousePressEvent(event) if event.button() != Qt.LeftButton: return self.mousePressed = True item = self.indexAt(event.pos()) clickedItem = self.model().index(item.row(), self.trackingCol) if clickedItem.data() == None: return if item == None and self.curSelection == None: return elif item != None and self.curSelection == None: # force selecting the row below self.curSelection = "" if clickedItem == None: return if clickedItem.data() == self.curSelection: self.curSelection = None flags = QItemSelectionModel.Rows | QItemSelectionModel.Deselect else: self.curSelection = clickedItem.data() flags = QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent self.selectionModel().setCurrentIndex( clickedItem, flags ) def onBeginViewportRefresh(self): # if the selected row due to scrolling up/down doesn't match with the # saved index, deselect the row, because the saved index is out of the # view. index = self.selectionModel().selectedRows(self.trackingCol) if len(index) == 0: return if index[0].data() != self.curSelection: self.selectionModel().clear() def onEndViewportRefresh(self): self._selectSavedIndex() def resizeEvent(self, event): super().resizeEvent(event) #refresh the viewport data based on new geometry self.refresh() def onRowCountChanged(self): totalCount = self.model().totalRowCount self.vScrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False) self.vScrollBar.setMinimum(0) # we need to substract the displayed rows to the total rows, to scroll # down correctly. self.vScrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport+1)) self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport, force=self.forceViewRefresh()) def clearSelection(self): self.selectionModel().reset() self.selectionModel().clearCurrentIndex() def copySelection(self): model = self.selectionModel() curModel = self.model() selection = model.selectedRows() if not selection: return None rows = [] for idx in selection: row = [] for col in range(0, curModel.columnCount()): row.append(curModel.index(idx.row(), col).data()) rows.append(row) return rows def getCurrentIndex(self): return self.selectionModel().currentIndex().internalId() def currentSelection(self): return self.curSelection def selectItem(self, _data, _column): """Select a row based on the data displayed on the given column. """ items = self.model().findItems(_data, column=_column) if len(items) > 0: self.selectionModel().setCurrentIndex( items[0].index(), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent ) def _selectSavedIndex(self): if self.curSelection == None or self.mousePressed: return items = self.model().findItems(self.curSelection, column=self.trackingCol) if len(items) > 0: self.selectionModel().setCurrentIndex( items[0].index(), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent ) def _selectLastRow(self): if self.curSelection != None: return internalId = self.getCurrentIndex() self.selectionModel().setCurrentIndex( self.model().createIndex(self.maxRowsInViewport-2, self.trackingCol, internalId), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent ) def _selectRow(self, pos): internalId = self.getCurrentIndex() self.selectionModel().setCurrentIndex( self.model().createIndex(pos, self.trackingCol, internalId), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent ) def onScrollbarValueChanged(self, vSBNewValue): self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport, force=True) def onKeyUp(self): self.curSelection = self.selectionModel().currentIndex().data() if self.selectionModel().currentIndex().row() == 0: self.vScrollBar.setValue(max(0, self.vScrollBar.value() - 1)) def onKeyDown(self): self.curSelection = self.selectionModel().currentIndex().data() if self.curSelection == None: self._selectLastRow() return curRow = self.selectionModel().currentIndex().row() if curRow >= self.maxRowsInViewport-2: self.onKeyPageDown() self._selectRow(0) else: self._selectRow(curRow) def onKeyHome(self): self.vScrollBar.setValue(0) self.selectionModel().clear() def onKeyEnd(self): self.vScrollBar.setValue(self.vScrollBar.maximum()) self.selectionModel().clear() self._selectLastRow() def onKeyPageUp(self): newValue = max(0, self.vScrollBar.value() - self.maxRowsInViewport) self.vScrollBar.setValue(newValue) def onKeyPageDown(self): if self.vScrollBar.isVisible() == False: return newValue = self.vScrollBar.value() + (self.maxRowsInViewport-2) self.vScrollBar.setValue(newValue) def eventFilter(self, obj, event): if event.type() == QEvent.KeyPress: # FIXME: setValue() does not update the scrollbars correctly in # some pyqt versions. if event.key() == Qt.Key_Up: self.onKeyUp() elif event.key() == Qt.Key_Down: self.onKeyDown() elif event.key() == Qt.Key_Home: self.onKeyHome() elif event.key() == Qt.Key_End: self.onKeyEnd() elif event.key() == Qt.Key_PageUp: self.onKeyPageUp() elif event.key() == Qt.Key_PageDown: self.onKeyPageDown() elif event.key() == Qt.Key_Escape: self.selectionModel().clear() self.curSelection = None elif event.type() == QEvent.Wheel: self.vScrollBar.wheelEvent(event) return True return super(GenericTableView, self).eventFilter(obj, event) ����������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/main.py������������������������������������������������0000664�0000000�0000000�00000053575�15003540030�0022674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import QtCore from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql from PyQt5.QtWidgets import QTableView from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent import time import math from PyQt5.QtCore import QCoreApplication as QC class ColorizedQSqlQueryModel(QSqlQueryModel): """ model=CustomQSqlQueryModel( modelData= { 'colorize': {'offline': (QColor(QtCore.Qt.red), 2)}, 'alignment': { Qt.AlignLeft, 2 } } ) """ RED = QColor(QtCore.Qt.red) GREEN = QColor(QtCore.Qt.green) def __init__(self, modelData={}): QSqlQueryModel.__init__(self) self._model_data = modelData def data(self, index, role=QtCore.Qt.DisplayRole): if not index.isValid(): return QSqlQueryModel.data(self, index, role) column = index.column() row = index.row() if role == QtCore.Qt.TextAlignmentRole: return QtCore.Qt.AlignCenter if role == QtCore.Qt.TextColorRole: for _, what in enumerate(self._model_data): d = QSqlQueryModel.data(self, self.index(row, self._model_data[what][1]), QtCore.Qt.DisplayRole) if column == self._model_data[what][1] and what in d: return self._model_data[what][0] return QSqlQueryModel.data(self, index, role) class ConnectionsTableModel(QStandardItemModel): rowCountChanged = pyqtSignal() #max rowid in the db; starts with 1, not with 0 maxRowId = 0 #previous total number of rows in the db when the filter was applied prevFiltRowCount = 0 #total number of rows in the db when the filter was not applied prevNormRowCount = 0 #total row count which must de displayed in the view totalRowCount = 0 #new rows which must be added to the top of the rows displayed in the view prependedRowCount = 0 db = None #original query string before we modify it origQueryStr = QSqlQuery() #modified query object realQuery = QSqlQuery() #previous original query string; used to check if the query has changed prevQueryStr = '' #whether or not the original query has a filter (a WHERE condition) isQueryFilter = False limit = None #a map for fast lookup or rows when filter is enabled #contains ranges of rowids and count of filter hits #range format {'from': <rowid>, 'to': <rowid>, 'hits':<int>} #including the 'from' rowid up to but NOT including the 'to' rowid map = [] rangeSize = 1000 #all unique/distinct values for each column will be stored here distinct = {'time':[], 'process':[], 'dst_host':[], 'dst_ip':[], 'dst_port':[], 'rule':[], 'node':[], 'protocol':[]} #what was the last rowid\time when the distinct value were updates distinctLastRowId = 0 distinctLastUpdateTime = time.time() def __init__(self): self.headerLabels = [ QC.translate("stats", "Time", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Node", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Action", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Destination", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Protocol", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Process", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Rule", "This is a word, without spaces and symbols.").replace(" ", ""), ] QStandardItemModel.__init__(self, 0, len(self.headerLabels)) self.setHorizontalHeaderLabels(self.headerLabels) #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement #mimic QSqlQueryModel.query() def query(self): return self #mimic QSqlQueryModel.query().lastQuery() def lastQuery(self): return self.origQueryStr #mimic QSqlQueryModel.query().lastError() def lastError(self): return self.realQuery.lastError() #mimic QSqlQueryModel.clear() def clear(self): pass def setQuery(self, q, db): self.origQueryStr = q self.db = db maxRowIdQuery = QSqlQuery(db) maxRowIdQuery.setForwardOnly(True) maxRowIdQuery.exec("SELECT MAX(rowid) FROM connections") maxRowIdQuery.first() value = maxRowIdQuery.value(0) self.maxRowId = 0 if value == '' else int(value) self.updateDistinctIfNeeded() self.limit = int(q.split(' ')[-1]) if q.split(' ')[-2] == 'LIMIT' else None self.isQueryFilter = True if ("LIKE '%" in q and "LIKE '% %'" not in q) or 'Action = "' in q else False self.realQuery = QSqlQuery(db) isTotalRowCountChanged = False isQueryChanged = False if self.prevQueryStr != q: isQueryChanged = True if self.isQueryFilter: if isQueryChanged: self.buildMap() largestRowIdInMap = self.map[0]['from'] newRowsCount = self.maxRowId - largestRowIdInMap self.prependedRowCount = 0 if newRowsCount > 0: starttime = time.time() self.realQuery.setForwardOnly(True) for offset in range(0, newRowsCount, self.rangeSize): lowerBound = largestRowIdInMap + offset upperBound = min(lowerBound + self.rangeSize, self.maxRowId) part1, part2 = q.split('ORDER') qStr = part1 + 'AND rowid>'+ str(lowerBound) + ' AND rowid<=' + str(upperBound) + ' ORDER' + part2 self.realQuery.exec(qStr) self.realQuery.last() rowsInRange = max(0, self.realQuery.at()+1) if self.map[0]['from'] - self.map[0]['to'] < self.rangeSize: #consolidate with the previous range; we don't want many small ranges self.map[0]['from'] = upperBound self.map[0]['hits'] += rowsInRange else: self.map.insert(0, {'from':upperBound, 'to':lowerBound, 'hits':rowsInRange}) self.prependedRowCount += rowsInRange if time.time() - starttime > 0.5: #dont freeze the UI when fetching too many recent rows break self.totalRowCount = 0 for i in self.map: self.totalRowCount += i['hits'] if self.totalRowCount != self.prevFiltRowCount: isTotalRowCountChanged = True self.prevFiltRowCount = self.totalRowCount else: #self.isQueryFilter == False self.prependedRowCount = self.maxRowId - self.prevNormRowCount self.totalRowCount = self.maxRowId if self.totalRowCount != self.prevNormRowCount: isTotalRowCountChanged = True self.prevNormRowCount = self.totalRowCount self.prevQueryStr = self.origQueryStr if isTotalRowCountChanged or self.prependedRowCount > 0 or isQueryChanged: self.rowCountChanged.emit() #fill self.map with data def buildMap(self): self.map = [] q = QSqlQuery(self.db) q.setForwardOnly(True) self.updateDistinctIfNeeded(True) filterStr = self.getFilterStr() actionStr = self.getActionStr() #we only want to know the count of matching rows qStr = "SELECT COUNT(*) from connections WHERE (rowid> :lowerBound AND rowid<= :upperBound)" if actionStr: qStr += ' AND ' + actionStr matchStr = self.getMatch(filterStr) if filterStr else None if matchStr: qStr += ' AND ' + matchStr qStr += ' LIMIT ' + str(self.limit) if self.limit else '' q.prepare(qStr) totalRows = 0 for offset in range(self.maxRowId, -1, -self.rangeSize): upperBound = offset lowerBound = max(0, upperBound - self.rangeSize) if (not filterStr and actionStr) or (filterStr and matchStr): #either 1) only action was present or 2) filter which has a match (with or without action) q.bindValue(":lowerBound", str(lowerBound)) q.bindValue(":upperBound", str(upperBound)) q.exec_() q.first() rowsInRange = int(q.value(0)) else: rowsInRange = 0 totalRows += rowsInRange self.map.append({'from':upperBound, 'to':lowerBound, 'hits':rowsInRange}) if self.limit and totalRows >= self.limit: break #periodically keep track of all distinct values for each column #this is needed in order to build efficient queries when the filter is applied def updateDistinctIfNeeded(self, force=False): if (not force and (time.time() - self.distinctLastUpdateTime) < 10) or self.maxRowId == self.distinctLastRowId: return if (self.maxRowId < self.distinctLastRowId): #the db has been cleared, re-init the values self.distinctLastRowId = 0 self.distinct = {'time':[], 'process':[], 'dst_host':[], 'dst_ip':[], 'dst_port':[], 'rule':[], 'node':[], 'protocol':[]} q = QSqlQuery(self.db) q.setForwardOnly(True) for column in self.distinct.keys(): q.exec('SELECT DISTINCT ' + column + ' FROM connections WHERE rowid>' + str(self.distinctLastRowId) + ' AND rowid<=' + str(self.maxRowId)) while q.next(): if q.value(0) not in self.distinct[column]: self.distinct[column].append(q.value(0)) self.distinctLastRowId =self.maxRowId self.distinctLastUpdateTime = time.time() #refresh the viewport with data from the db #"value" is vertical scrollbar's value def refreshViewport(self, value, maxRowsInViewport): q = QSqlQuery(self.db) #sequential number of topmost/bottommost rows in viewport (numbering starts from the bottom with 1 not with 0) botRowNo = max(1, self.totalRowCount - (value + maxRowsInViewport-1)) topRowNo = min(botRowNo + maxRowsInViewport-1, self.totalRowCount) if not self.isQueryFilter: part1, part2 = self.origQueryStr.split('ORDER') qStr = part1 + 'WHERE rowid>='+ str(botRowNo) + ' AND rowid<=' + str(topRowNo) + ' ORDER' + part2 else: self.updateDistinctIfNeeded(True) #replace query part between WHERE and ORDER qStr = self.origQueryStr.split('WHERE')[0] + ' WHERE ' actionStr = self.getActionStr() if actionStr: qStr += actionStr + " AND " #find inside the map the range(s) in which top and bottom rows are located total, offsetInRange, botRowFound, topRowFound = 0, None, False, False ranges = [{'from':0, 'to':0, 'hits':0}] for i in reversed(self.map): if total + i['hits'] >= botRowNo: botRowFound = True if total + i['hits'] >= topRowNo: topRowFound = True if botRowFound and i['hits'] > 0: if i['to'] == ranges[-1]['from']: #merge two adjacent ranges ranges[-1]['from'] = i['from'] ranges[-1]['hits'] += i['hits'] else: ranges.append(i.copy()) if topRowFound: offsetInRange = i['hits'] - (topRowNo - total) break total += i['hits'] rangeStr = '' if len(ranges) > 0: rangeStr = '(' for r in ranges: rangeStr += '(rowid>' + str(r['to']) + ' AND rowid<=' + str(r['from']) + ') OR ' rangeStr = rangeStr[:-3] #remove trailing 'OR ' rangeStr += ') AND ' qStr += rangeStr filterStr = self.getFilterStr() matchStr = self.getMatch(filterStr) if filterStr else None if matchStr: qStr += matchStr + " AND " qStr = qStr[:-4] #remove trailing ' AND' qStr += ' ORDER '+ self.origQueryStr.split('ORDER')[1] q.exec(qStr) q.last() rowsFound = max(0, q.at()+1) if not self.isQueryFilter: q.seek(QSql.BeforeFirstRow) else: #position the db cursor on topRowNo q.seek(QSql.BeforeFirstRow if offsetInRange == 0 else offsetInRange-1) upperBound = min(maxRowsInViewport, rowsFound) self.setRowCount(upperBound) #only visible rows will be filled with data if upperBound > 0: #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells self.blockSignals(True) for x in range(0, upperBound): q.next() for col in range(0, len(self.headerLabels)): self.setItem(x, col, QStandardItem(q.value(col))) self.blockSignals(False) self.dataChanged.emit(self.createIndex(0,0), self.createIndex(upperBound, len(self.headerLabels))) #form a condition string for the query: if filterStr is (partially) present in any of the columns def getMatch (self, filterStr): match = {} for column in self.distinct.keys(): match[column] = [] for value in self.distinct[column]: if filterStr in value: match[column].append(value) matchStr = None if any([match[col] for col in match]): matchStr = '( ' if match['time']: matchStr += "time IN ('" + "','".join(match['time']) + "') OR" if match['process']: matchStr += "process IN ('" + "','".join(match['process']) + "') OR" if match['dst_host']: matchStr += " (dst_host != '' AND dst_host IN ('" + "','".join(match['dst_host']) + "') ) OR" if match['dst_ip']: matchStr += " (dst_host = '' AND dst_ip IN ('" + "','".join(match['dst_ip']) + "') ) OR" if match['dst_port']: matchStr += " dst_port IN ('" + "','".join(match['dst_port']) + "') OR" if match['rule']: matchStr += " rule IN ('" + "','".join(match['rule']) + "') OR" if match['node']: matchStr += " node IN ('" + "','".join(match['node']) + "') OR" if match['protocol']: matchStr += " protocol IN ('" + "','".join(match['protocol']) + "') OR" matchStr = matchStr[:-2] #remove trailing 'OR' matchStr += ' )' return matchStr #extract the filter string if any def getFilterStr(self): filterStr = None if "LIKE '%" in self.origQueryStr: filterStr = self.origQueryStr.split("LIKE '%")[1].split("%")[0] return filterStr #extract the action string if any def getActionStr(self): actionStr = None if 'WHERE Action = "' in self.origQueryStr: actionCond = self.origQueryStr.split('WHERE Action = "')[1].split('"')[0] actionStr = "action = '"+actionCond+"'" return actionStr def dumpRows(self): rows = [] q = QSqlQuery(self.db) q.exec(self.origQueryStr) q.seek(QSql.BeforeFirstRow) while True: q.next() if q.at() == QSql.AfterLastRow: break row = [] for col in range(0, len(self.headerLabels)): row.append(q.value(col)) rows.append(row) return rows class ConnectionsTableView(QTableView): # how many rows can potentially be displayed in viewport # the actual number of rows currently displayed may be less than this maxRowsInViewport = 0 #vertical scroll bar vScrollBar = None def __init__(self, parent): QTableView.__init__(self, parent) #eventFilter to catch key up/down events and wheel events self.installEventFilter(self) self.verticalHeader().setVisible(False) self.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignCenter) self.horizontalHeader().setStretchLastSection(True) #the built-in vertical scrollBar of this view is always off self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) def setVerticalScrollBar(self, vScrollBar): self.vScrollBar = vScrollBar self.vScrollBar.valueChanged.connect(self.onValueChanged) self.vScrollBar.setVisible(False) def setModel(self, model): super().setModel(model) model.rowCountChanged.connect(self.onRowCountChanged) model.rowsInserted.connect(self.onRowsInsertedOrRemoved) model.rowsRemoved.connect(self.onRowsInsertedOrRemoved) self.horizontalHeader().sortIndicatorChanged.disconnect() self.setSortingEnabled(False) #model().rowCount() is always <= self.maxRowsInViewport #stretch the bottom row; we don't want partial-height rows at the bottom #this will only trigger if rowCount value was changed def onRowsInsertedOrRemoved(self, parent, start, end): if self.model().rowCount() == self.maxRowsInViewport: self.verticalHeader().setStretchLastSection(True) else: self.verticalHeader().setStretchLastSection(False) def resizeEvent(self, event): super().resizeEvent(event) #refresh the viewport data based on new geometry self.calculateRowsInViewport() self.model().setRowCount(min(self.maxRowsInViewport, self.model().totalRowCount)) self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport) def calculateRowsInViewport(self): rowHeight = self.verticalHeader().defaultSectionSize() #we don't want partial-height rows in viewport, hence .floor() self.maxRowsInViewport = math.floor(self.viewport().height() / rowHeight) def onValueChanged(self, vSBNewValue): savedIndex = self.selectionModel().currentIndex() self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport) #restore selection which was removed by model's refreshing the data self.selectionModel().setCurrentIndex(savedIndex, QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent) # if ( scrollbar at the top or row limit set): # let new rows "push down" older rows without changing the scrollbar position # else: # don't update data in viewport, only change scrollbar position. def onRowCountChanged(self): totalCount = self.model().totalRowCount scrollBar = self.vScrollBar scrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False) scrollBarValue = scrollBar.value() if self.model().limit: newValue = min(scrollBarValue, self.model().limit - self.maxRowsInViewport) scrollBar.setMinimum(0) scrollBar.setMaximum( min(totalCount, self.model().limit) - self.maxRowsInViewport) if scrollBarValue != newValue: #setValue does not trigger valueChanged if new value is the same as old scrollBar.setValue(newValue) else: scrollBar.valueChanged.emit(newValue) else: scrollBar.setMinimum(0) scrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport)) if scrollBarValue == 0: scrollBar.valueChanged.emit(0) elif scrollBarValue > 0: if self.model().prependedRowCount == 0: scrollBar.valueChanged.emit(scrollBarValue) else: scrollBar.setValue(scrollBarValue + self.model().prependedRowCount) def onKeyUp(self): if self.selectionModel().currentIndex().row() == 0: self.vScrollBar.setValue(self.vScrollBar.value() - 1) def onKeyDown(self): if self.selectionModel().currentIndex().row() == self.maxRowsInViewport - 1: self.vScrollBar.setValue(self.vScrollBar.value() + 1) def onKeyHome(self): self.vScrollBar.setValue(0) self.selectionModel().setCurrentIndex(self.model().createIndex(0, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent) def onKeyEnd(self): self.vScrollBar.setValue(self.vScrollBar.maximum()) self.selectionModel().setCurrentIndex(self.model().createIndex(min(self.maxRowsInViewport, self.model().totalRowCount) - 1, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent) def onKeyPageUp(self): #scroll up only when on the first row if self.selectionModel().currentIndex().row() != 0: return self.vScrollBar.setValue(self.vScrollBar.value() - self.maxRowsInViewport) def onKeyPageDown(self): #scroll down only when on the last row if self.selectionModel().currentIndex().row() != self.maxRowsInViewport - 1: return self.vScrollBar.setValue(self.vScrollBar.value() + self.maxRowsInViewport) def eventFilter(self, obj, event): if event.type() == QEvent.KeyPress: if event.key() == QtCore.Qt.Key_Up: self.onKeyUp() elif event.key() == QtCore.Qt.Key_Down: self.onKeyDown() elif event.key() == QtCore.Qt.Key_Home: self.onKeyHome() elif event.key() == QtCore.Qt.Key_End: self.onKeyEnd() elif event.key() == QtCore.Qt.Key_PageUp: self.onKeyPageUp() elif event.key() == QtCore.Qt.Key_PageDown: self.onKeyPageDown() elif event.type() == QEvent.Wheel: self.vScrollBar.wheelEvent(event) return False �����������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/customwidgets/updownbtndelegate.py�����������������������������������0000664�0000000�0000000�00000003407�15003540030�0025450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import Qt, QtCore from PyQt5.QtGui import QRegion from PyQt5.QtWidgets import QItemDelegate, QAbstractItemView, QPushButton, QWidget, QVBoxLayout, QSizePolicy from PyQt5.QtCore import pyqtSignal class UpDownButtonDelegate(QItemDelegate): clicked = pyqtSignal(int, QtCore.QModelIndex) UP=-1 DOWN=1 def paint(self, painter, option, index): if ( isinstance(self.parent(), QAbstractItemView) and self.parent().model() is index.model() ): self.parent().openPersistentEditor(index) def createEditor(self, parent, option, index): w = QWidget(parent) w.setContentsMargins(0, 0, 0, 0) w.setAutoFillBackground(True) layout = QVBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) btnUp = QPushButton(parent) btnUp.setText("⇡") btnUp.setFlat(True) btnUp.clicked.connect(lambda: self._cb_button_clicked(self.UP, index)) btnDown = QPushButton(parent) btnDown.setText("⇣") btnDown.setFlat(True) btnDown.clicked.connect(lambda: self._cb_button_clicked(self.DOWN, index)) layout.addWidget(btnUp) layout.addWidget(btnDown) return w def _cb_button_clicked(self, action, idx): self.clicked.emit(action, idx) def updateEditorGeometry(self, editor, option, index): rect = QtCore.QRect(option.rect) minWidth = editor.minimumSizeHint().width() if rect.width() < minWidth: rect.setWidth(minWidth) editor.setGeometry(rect) # create a new mask based on the option rectangle, then apply it mask = QRegion(0, 0, option.rect.width(), option.rect.height()) editor.setProperty('offMask', mask) editor.setMask(mask) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/database/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020222�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/database/__init__.py�������������������������������������������������0000664�0000000�0000000�00000053265�15003540030�0022346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel, QSqlQuery import threading import sys import os from datetime import datetime, timedelta class Database: db = None __instance = None DB_IN_MEMORY = "file::memory:" DB_TYPE_MEMORY = 0 DB_TYPE_FILE = 1 DB_JRNL_WAL = False # Sqlite3 journal modes DB_JOURNAL_MODE_LIST = { 0: "DELETE", 1: "TRUNCATE", 2: "PERSIST", 3: "MEMORY", 4: "WAL", 5: "OFF", } # increase accordingly whenever the schema is updated DB_VERSION = 3 @staticmethod def instance(): if Database.__instance == None: Database.__instance = Database() return Database.__instance def __init__(self, dbname="db"): self._lock = threading.RLock() self.db = None self.db_file = Database.DB_IN_MEMORY self.db_jrnl_wal = Database.DB_JRNL_WAL self.db_name = dbname def initialize(self, dbtype=DB_TYPE_MEMORY, dbfile=DB_IN_MEMORY, dbjrnl_wal=DB_JRNL_WAL, db_name="db"): if dbtype != Database.DB_TYPE_MEMORY: self.db_file = dbfile self.db_jrnl_wal = dbjrnl_wal else: # Always disable under pure memory mode self.db_jrnl_wal = False is_new_file = not os.path.isfile(self.db_file) self.db = QSqlDatabase.addDatabase("QSQLITE", self.db_name) self.db.setDatabaseName(self.db_file) if dbtype == Database.DB_TYPE_MEMORY: self.db.setConnectOptions("QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE") if not self.db.open(): print("\n ** Error opening DB: SQLite driver not loaded. DB name: %s\n" % self.db_file) print("\n Available drivers: ", QSqlDatabase.drivers()) sys.exit(-1) db_status, db_error = self.is_db_ok() if db_status is False: print("db.initialize() error:", db_error) return False, db_error if is_new_file: print("is new file, or IN MEMORY, setting initial schema version") self.set_schema_version(self.DB_VERSION) self._create_tables() self._upgrade_db_schema() return True, None def close(self): try: if self.db.isOpen(): self.db.removeDatabase(self.db_name) self.db.close() except Exception as e: print("db.close() exception:", e) def is_db_ok(self): # XXX: quick_check may not be fast enough with some DBs on slow # hardware. q = QSqlQuery("PRAGMA quick_check;", self.db) if q.exec_() is not True: print(q.lastError().driverText()) return False, q.lastError().driverText() if q.next() and q.value(0) != "ok": return False, "Database is corrupted (1)" return True, None def get_db(self): return self.db def get_db_file(self): return self.db_file def get_new_qsql_model(self): return QSqlQueryModel() def get_db_name(self): return self.db_name def _create_tables(self): # https://www.sqlite.org/wal.html if self.db_file == Database.DB_IN_MEMORY: self.set_schema_version(self.DB_VERSION) # Disable journal (default) self.set_journal_mode(5) elif self.db_jrnl_wal is True: # Set WAL mode (file+memory) self.set_journal_mode(4) else: # Set DELETE mode (file) self.set_journal_mode(0) q = QSqlQuery("create table if not exists connections (" \ "time text, " \ "node text, " \ "action text, " \ "protocol text, " \ "src_ip text, " \ "src_port text, " \ "dst_ip text, " \ "dst_host text, " \ "dst_port text, " \ "uid text, " \ "pid text, " \ "process text, " \ "process_args text, " \ "process_cwd text, " \ "rule text, " \ "UNIQUE(node, action, protocol, src_ip, src_port, dst_ip, dst_port, uid, pid, process, process_args))", self.db) q = QSqlQuery("create index time_index on connections (time)", self.db) q.exec_() q = QSqlQuery("create index action_index on connections (action)", self.db) q.exec_() q = QSqlQuery("create index protocol_index on connections (protocol)", self.db) q.exec_() q = QSqlQuery("create index dst_host_index on connections (dst_host)", self.db) q.exec_() q = QSqlQuery("create index process_index on connections (process)", self.db) q.exec_() q = QSqlQuery("create index dst_ip_index on connections (dst_ip)", self.db) q.exec_() q = QSqlQuery("create index dst_port_index on connections (dst_port)", self.db) q.exec_() q = QSqlQuery("create index rule_index on connections (rule)", self.db) q.exec_() q = QSqlQuery("create index node_index on connections (node)", self.db) q.exec_() q = QSqlQuery("CREATE INDEX details_query_index on connections (process, process_args, uid, pid, dst_ip, dst_host, dst_port, action, node, protocol)", self.db) q.exec_() q = QSqlQuery("create table if not exists nodes (" \ "addr text primary key," \ "hostname text," \ "daemon_version text," \ "daemon_uptime text," \ "daemon_rules text," \ "cons text," \ "cons_dropped text," \ "version text," \ "status text, " \ "last_connection text)" , self.db) q.exec_() q = QSqlQuery("create table if not exists rules (" \ "time text, " \ "node text, " \ "name text, " \ "enabled text, " \ "precedence text, " \ "action text, " \ "duration text, " \ "operator_type text, " \ "operator_sensitive text, " \ "operator_operand text, " \ "operator_data text, " \ "description text, " \ "nolog text, " \ "created text, " \ "UNIQUE(node, name)" ")", self.db) q.exec_() q = QSqlQuery("create index rules_index on rules (time)", self.db) q.exec_() q = QSqlQuery("create table if not exists hosts (what text primary key, hits integer)", self.db) q.exec_() q = QSqlQuery("create table if not exists procs (what text primary key, hits integer)", self.db) q.exec_() q = QSqlQuery("create table if not exists addrs (what text primary key, hits integer)", self.db) q.exec_() q = QSqlQuery("create table if not exists ports (what text primary key, hits integer)", self.db) q.exec_() q = QSqlQuery("create table if not exists users (what text primary key, hits integer)", self.db) q.exec_() def get_schema_version(self): q = QSqlQuery("PRAGMA user_version;", self.db) q.exec_() if q.next(): print("schema version:", q.value(0)) return int(q.value(0)) return 0 def set_schema_version(self, version): print("setting schema version to:", version) q = QSqlQuery("PRAGMA user_version = {0}".format(version), self.db) if q.exec_() == False: print("Error updating updating schema version:", q.lastError().text()) def get_journal_mode(self): q = QSqlQuery("PRAGMA journal_mode;", self.db) q.exec_() if q.next(): return str(q.value(0)) return str("unknown") def set_journal_mode(self, mode): # https://www.sqlite.org/wal.html mode_str = Database.DB_JOURNAL_MODE_LIST[mode] if self.get_journal_mode().lower() != mode_str.lower(): print("Setting journal_mode: ", mode_str) q = QSqlQuery("PRAGMA journal_mode = {modestr};".format(modestr = mode_str), self.db) if q.exec_() == False: print("Error updating PRAGMA journal_mode:", q.lastError().text()) return False if mode == 3 or mode == 5: print("Setting DB memory optimizations") q = QSqlQuery("PRAGMA synchronous = OFF;", self.db) if q.exec_() == False: print("Error updating PRAGMA synchronous:", q.lastError().text()) return False q = QSqlQuery("PRAGMA cache_size=10000;", self.db) if q.exec_() == False: print("Error updating PRAGMA cache_size:", q.lastError().text()) return False else: print("Setting synchronous = NORMAL") q = QSqlQuery("PRAGMA synchronous = NORMAL;", self.db) if q.exec_() == False: print("Error updating PRAGMA synchronous:", q.lastError().text()) return True def _upgrade_db_schema(self): migrations_path = os.path.dirname(os.path.realpath(__file__)) + "/migrations" schema_version = self.get_schema_version() if schema_version == self.DB_VERSION: print("db schema is up to date") return while schema_version < self.DB_VERSION: schema_version += 1 try: print("applying schema upgrade:", schema_version) self._apply_db_upgrade("{0}/upgrade_{1}.sql".format(migrations_path, schema_version)) except Exception as e: print("Not applying upgrade_{0}.sql:".format(schema_version), e) return self.set_schema_version(schema_version) def _apply_db_upgrade(self, file): print("applying upgrade from:", file) q = QSqlQuery(self.db) with open(file) as f: for line in f.readlines(): # skip comments if line.startswith("--"): continue print("applying upgrade:", line, end="") if q.exec(line) == False: print("\tError:", q.lastError().text()) else: print("\tOK") def optimize(self): """https://www.sqlite.org/pragma.html#pragma_optimize """ q = QSqlQuery("PRAGMA optimize;", self.db) q.exec_() def clean(self, table): with self._lock: q = QSqlQuery("delete from " + table, self.db) q.exec_() def vacuum(self): q = QSqlQuery("VACUUM;", self.db) q.exec_() def clone_db(self, name): return QSqlDatabase.cloneDatabase(self.db, name) def clone(self): q = QSqlQuery(".dump", self.db) q.exec_() def transaction(self): self.db.transaction() def commit(self): self.db.commit() def rollback(self): self.db.rollback() def get_total_records(self): try: q = QSqlQuery("SELECT count(*) FROM connections", self.db) if q.exec_() and q.first(): r = q.value(0) except Exception as e: print("db, get_total_records() error:", e) def get_newest_record(self): try: q = QSqlQuery("SELECT time FROM connections ORDER BY 1 DESC LIMIT 1", self.db) if q.exec_() and q.first(): return q.value(0) except Exception as e: print("db, get_newest_record() error:", e) return 0 def get_oldest_record(self): try: q = QSqlQuery("SELECT time FROM connections ORDER BY 1 ASC LIMIT 1", self.db) if q.exec_() and q.first(): return q.value(0) except Exception as e: print("db, get_oldest_record() error:", e) return 0 def purge_oldest(self, max_days_to_keep): try: oldt = self.get_oldest_record() newt = self.get_newest_record() if oldt == None or newt == None or oldt == 0 or newt == 0: return -1 oldest = datetime.strptime(oldt, "%Y-%m-%d %H:%M:%S.%f") newest = datetime.strptime(newt, "%Y-%m-%d %H:%M:%S.%f") diff = newest - oldest date_to_purge = datetime.now() - timedelta(days=max_days_to_keep) if diff.days >= max_days_to_keep: q = QSqlQuery(self.db) q.prepare("DELETE FROM connections WHERE time < ?") q.bindValue(0, str(date_to_purge)) if q.exec_(): print("purge_oldest() {0} records deleted".format(q.numRowsAffected())) return q.numRowsAffected() except Exception as e: print("db, purge_oldest() error:", e) return -1 def select(self, qstr): try: return QSqlQuery(qstr, self.db) except Exception as e: print("db, select() exception: ", e) return None def remove(self, qstr): try: q = QSqlQuery(qstr, self.db) if q.exec_(): return True else: print("db, remove() ERROR: ", qstr) print(q.lastError().driverText()) except Exception as e: print("db, remove exception: ", e) return False def _insert(self, query_str, columns): with self._lock: try: q = QSqlQuery(self.db) q.prepare(query_str) for idx, v in enumerate(columns): q.bindValue(idx, v) if q.exec_(): return True else: print("_insert() ERROR", query_str) print(q.lastError().driverText()) except Exception as e: print("_insert exception", e) finally: q.finish() return False def insert(self, table, fields, columns, update_field=None, update_values=None, action_on_conflict="REPLACE"): if update_field != None: action_on_conflict = "" else: action_on_conflict = "OR " + action_on_conflict qstr = "INSERT " + action_on_conflict + " INTO " + table + " " + fields + " VALUES(" update_fields="" for col in columns: qstr += "?," qstr = qstr[0:len(qstr)-1] + ")" if update_field != None: # NOTE: UPSERTS on sqlite are only supported from v3.24 on. # On Ubuntu16.04/18 for example (v3.11/3.22) updating a record on conflict # fails with "Parameter count error" qstr += " ON CONFLICT (" + update_field + ") DO UPDATE SET " for idx, field in enumerate(update_values): qstr += str(field) + "=excluded." + str(field) + "," qstr = qstr[0:len(qstr)-1] return self._insert(qstr, columns) def update(self, table, fields, values, condition=None, action_on_conflict="OR IGNORE"): qstr = "UPDATE " + action_on_conflict + " " + table + " SET " + fields if condition != None: qstr += " WHERE " + condition try: with self._lock: q = QSqlQuery(qstr, self.db) q.prepare(qstr) for idx, v in enumerate(values): q.bindValue(idx, v) if not q.exec_(): print("update ERROR:", qstr, "values:", values) print(q.lastError().driverText()) except Exception as e: print("update() exception:", e) finally: q.finish() def _insert_batch(self, query_str, fields, values): result=True with self._lock: try: q = QSqlQuery(self.db) q.prepare(query_str) q.addBindValue(fields) q.addBindValue(values) if not q.execBatch(): print("_insert_batch() db error:", query_str) print(q.lastError().driverText()) print("\t", fields) print("\t", values) result=False except Exception as e: print("_insert_batch() exception:", e) finally: q.finish() return result def insert_batch(self, table, db_fields, db_columns, fields, values, update_field=None, update_value=None, action_on_conflict="REPLACE"): action = "OR " + action_on_conflict if update_field != None: action = "" qstr = "INSERT " + action + " INTO " + table + " (" + db_fields[0] + "," + db_fields[1] + ") VALUES(" for idx in db_columns: qstr += "?," qstr = qstr[0:len(qstr)-1] + ")" if self._insert_batch(qstr, fields, values) == False: self.update_batch(table, db_fields, db_columns, fields, values, update_field, update_value, action_on_conflict) def update_batch(self, table, db_fields, db_columns, fields, values, update_field=None, update_value=None, action_on_conflict="REPLACE"): for idx, i in enumerate(values): s = "UPDATE " + table + " SET " + "%s=(select hits from %s)+%s" % (db_fields[1], table, values[idx]) s += " WHERE %s=\"%s\"," % (db_fields[0], fields[idx]) s = s[0:len(s)-1] with self._lock: q = QSqlQuery(s, self.db) if not q.exec_(): print("update batch ERROR", s) print(q.lastError().driverText()) def dump(self): q = QSqlQuery(".dump", db=self.db) q.exec_() def get_query(self, table, fields): return "SELECT " + fields + " FROM " + table def empty_rule(self, name=""): if name == "": return qstr = "DELETE FROM connections WHERE rule = ?" with self._lock: q = QSqlQuery(qstr, self.db) q.prepare(qstr) q.addBindValue(name) if not q.exec_(): print("db, empty_rule() ERROR: ", qstr) print(q.lastError().driverText()) def delete_rule(self, name, node_addr): qstr = "DELETE FROM rules WHERE name=?" if node_addr != None: qstr = qstr + " AND node=?" with self._lock: q = QSqlQuery(qstr, self.db) q.prepare(qstr) q.addBindValue(name) if node_addr != None: q.addBindValue(node_addr) if not q.exec_(): print("db, delete_rule() ERROR: ", qstr) print(q.lastError().driverText()) return False return True def delete_rules_by_field(self, field, values): if len(values) == 0: return True qstr = "DELETE FROM rules WHERE " for v in values: qstr += field + "=? OR " qstr = qstr[:-4] with self._lock: q = QSqlQuery(qstr, self.db) q.prepare(qstr) for v in values: q.addBindValue(v) if not q.exec_(): print("db, delete_rule_by_field() ERROR: ", qstr) print(q.lastError().driverText()) return False return True def get_connection_by_field(self, field, date): """ """ qstr = "SELECT * FROM connections WHERE {0}=?".format(field) q = QSqlQuery(qstr, self.db) q.prepare(qstr) q.addBindValue(date) q.exec_() return q def get_rule(self, rule_name, node_addr=None): """ get rule records, given the name of the rule and the node """ qstr = "SELECT * FROM rules WHERE name=?" if node_addr != None: qstr = qstr + " AND node=?" q = QSqlQuery(qstr, self.db) q.prepare(qstr) q.addBindValue(rule_name) if node_addr != None: q.addBindValue(node_addr) q.exec_() return q def get_rules(self, node_addr): """ get rule records, given the name of the rule and the node """ qstr = "SELECT * FROM rules WHERE node=?" q = QSqlQuery(qstr, self.db) q.prepare(qstr) q.addBindValue(node_addr) if not q.exec_(): return None return q def insert_rule(self, rule, node_addr): self.insert("rules", "(time, node, name, description, enabled, precedence, nolog, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", (datetime.now().strftime("%Y-%m-%d %H:%M:%S"), node_addr, rule.name, rule.description, str(rule.enabled), str(rule.precedence), str(rule.nolog), rule.action, rule.duration, rule.operator.type, str(rule.operator.sensitive), rule.operator.operand, rule.operator.data), action_on_conflict="IGNORE") def rule_exists(self, rule, node_addr): qstr = "SELECT node, name, action, duration, operator_type, operator_operand, operator_data " \ " FROM rules WHERE " \ "name=? AND " \ "node=? AND " \ "action=? AND " \ "duration=? AND " \ "operator_type=? AND " \ "operator_operand=? AND " \ "operator_data=?" q = QSqlQuery(qstr, self.db) q.prepare(qstr) q.addBindValue(rule.name) q.addBindValue(node_addr) q.addBindValue(rule.action) q.addBindValue(rule.duration) q.addBindValue(rule.operator.type) q.addBindValue(rule.operator.operand) q.addBindValue(rule.operator.data) if not q.exec_() or q.next() == False: return None return q �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/database/enums.py����������������������������������������������������0000664�0000000�0000000�00000001050�15003540030�0021717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ class ConnFields(): Time = 0 Node = 1 Action = 2 Protocol = 3 SrcIP = 4 SrcPort = 5 DstIP = 6 DstHost = 7 DstPort = 8 UID = 9 PID = 10 Process = 11 Cmdline = 12 CWD = 13 Rule = 14 class RuleFields(): """These fields must be in the order defined in the DB""" Time = 0 Node = 1 Name = 2 Enabled = 3 Precedence = 4 Action = 5 Duration = 6 OpType = 7 OpSensitive = 8 OpOperand = 9 OpData = 10 Description = 11 NoLog = 12 Created = 13 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/database/migrations/�������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022376�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/database/migrations/upgrade_1.sql������������������������������������0000664�0000000�0000000�00000000052�15003540030�0024763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ALTER TABLE rules ADD COLUMN description; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/database/migrations/upgrade_2.sql������������������������������������0000664�0000000�0000000�00000000044�15003540030�0024765�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ALTER TABLE rules ADD COLUMN nolog; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/database/migrations/upgrade_3.sql������������������������������������0000664�0000000�0000000�00000000046�15003540030�0024770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ALTER TABLE rules ADD COLUMN created; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/desktop_parser.py����������������������������������������������������0000664�0000000�0000000�00000016545�15003540030�0022070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from threading import Lock import configparser import threading import glob import os import re import shutil import locale is_pyinotify_available = True try: import pyinotify except Exception as e: is_pyinotify_available = False print("Error importing pyinotify:", e) DESKTOP_PATHS = tuple([ os.path.join(d, 'applications') for d in os.getenv('XDG_DATA_DIRS', '/usr/share/').split(':') ]) class LinuxDesktopParser(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.lock = Lock() self.daemon = True self.running = False self.apps = {} self.apps_by_name = {} self.get_locale() # some things are just weird # (not really, i don't want to keep track of parent pids # just because of icons though, this hack is way easier) self.fixes = { '/opt/google/chrome/chrome': '/opt/google/chrome/google-chrome', '/usr/lib/firefox/firefox': '/usr/lib/firefox/firefox.sh', '/usr/bin/pidgin.orig': '/usr/bin/pidgin' } for desktop_path in DESKTOP_PATHS: if not os.path.exists(desktop_path): continue for desktop_file in glob.glob(os.path.join(desktop_path, '*.desktop')): self._parse_desktop_file(desktop_file) if is_pyinotify_available: self.start() def get_locale(self): try: self.locale = locale.getlocale()[0] self.locale_country = self.locale.split("_")[0] except Exception: self.locale = "" self.locale_country = "" def _parse_exec(self, cmd): try: is_flatpak = re.search(r'^/usr/[s]*bin/flatpak.*--command=([a-zA-Z0-9-_\/\.\+]+)', cmd) if is_flatpak: return is_flatpak.group(1) # remove stuff like %U cmd = re.sub( r'%[a-zA-Z]+', '', cmd) # remove 'env .... command' cmd = re.sub( r'^env\s+[^\s]+\s', '', cmd) # split && trim cmd = cmd.split(' ')[0].strip() # remove quotes cmd = re.sub( r'["\']+', '', cmd) # check if we need to resolve the path if len(cmd) > 0 and cmd[0] != '/': for path in os.environ["PATH"].split(os.pathsep): filename = os.path.join(path, cmd) if os.path.exists(filename): cmd = filename break except Exception as e: print("desktop_parser._parse_exec() exception:", e) return cmd def get_app_description(self, parser): try: desc = parser.get('Desktop Entry', 'Comment[%s]' % self.locale_country, raw=True, fallback=None) if desc == None: desc = parser.get('Desktop Entry', 'Comment[%s]' % self.locale, raw=True, fallback=None) if desc == None: desc = parser.get('Desktop Entry', 'Comment', raw=True, fallback=None) return desc except: return None def discover_app_icon(self, app_name): # more hacks # normally qt will find icons if the system if configured properly. # if it's not, qt won't be able to find the icon by using QIcon().fromTheme(""), # so we fallback to try to determine if the icon exist in some well known system paths. icon_dirs = ("/usr/share/icons/gnome/48x48/apps/", "/usr/share/pixmaps/", "/usr/share/icons/hicolor/48x48/apps/", "/usr/share/icons/HighContrast/48x48/apps/") icon_exts = (".png", ".xpm", ".svg") for idir in icon_dirs: for iext in icon_exts: if iext in app_name: iconPath = idir + app_name if os.path.exists(iconPath): return iconPath else: iconPath = idir + app_name + iext if os.path.exists(iconPath): return iconPath def _parse_desktop_file(self, desktop_path): parser = configparser.ConfigParser(strict=False) # Allow duplicate config entries try: basename = os.path.basename(desktop_path)[:-8] parser.read(desktop_path, 'utf8') cmd = parser.get('Desktop Entry', 'exec', raw=True, fallback=None) if cmd == None: cmd = parser.get('Desktop Entry', 'Exec', raw=True, fallback=None) if cmd is not None: cmd = self._parse_exec(cmd) icon = parser.get('Desktop Entry', 'Icon', raw=True, fallback=None) name = parser.get('Desktop Entry', 'Name', raw=True, fallback=None) desc = self.get_app_description(parser) if icon == None: # Some .desktop files doesn't have the Icon entry # FIXME: even if we return an icon, if the DE is not properly configured, # it won't be loaded/displayed. icon = self.discover_app_icon(basename) with self.lock: # The Exec entry may have an absolute path to a binary or just the binary with parameters. # /path/binary or binary, so save both self.apps[cmd] = (name, icon, desc, desktop_path) self.apps[basename] = (name, icon, desc, desktop_path) # if the command is a symlink, add the real binary too if os.path.islink(cmd): link_to = os.path.realpath(cmd) self.apps[link_to] = (name, icon, desc, desktop_path) except: print("Exception parsing .desktop file ", desktop_path) def get_info_by_path(self, path, default_icon): def_name = os.path.basename(path) # apply fixes for orig, to in self.fixes.items(): if path == orig: path = to break app_name = self.apps.get(path) if app_name == None: # last try to get a default terminal icon for def_icon in ("utilities-terminal", "gnome-terminal", "xfce-terminal"): test = self.apps.get(def_name, (def_name, def_icon, "", None)) if test != None: return test return self.apps.get(def_name, (def_name, default_icon, "", None)) return self.apps.get(path, (def_name, default_icon, "", None)) def get_info_by_binname(self, name, default_icon): def_name = os.path.basename(name) return self.apps.get(def_name, (def_name, default_icon, None)) def run(self): self.running = True wm = pyinotify.WatchManager() notifier = pyinotify.Notifier(wm) def inotify_callback(event): if event.mask == pyinotify.IN_CLOSE_WRITE: self._parse_desktop_file(event.pathname) elif event.mask == pyinotify.IN_DELETE: with self.lock: for cmd, data in self.apps.items(): if data[2] == event.pathname: del self.apps[cmd] break for p in DESKTOP_PATHS: if os.path.exists(p): wm.add_watch(p, pyinotify.IN_CLOSE_WRITE | pyinotify.IN_DELETE, inotify_callback) notifier.loop() �����������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020100�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/__init__.py��������������������������������������������������0000664�0000000�0000000�00000000000�15003540030�0022177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/conndetails.py�����������������������������������������������0000664�0000000�0000000�00000003665�15003540030�0022767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from opensnitch.nodes import Nodes from opensnitch.database import Database from opensnitch.database.enums import ConnFields from opensnitch.utils import Utils from opensnitch.utils.infowindow import InfoWindow from PyQt5.QtCore import QCoreApplication as QC class ConnDetails(InfoWindow): """Display a small dialog with the details of a connection """ def __init__(self, parent): super().__init__(parent) self._db = Database.instance() self._nodes = Nodes.instance() def showByField(self, field, value): records = self._db.get_connection_by_field(field, value) if not records.next(): return node = records.value(ConnFields.Node) uid = records.value(ConnFields.UID) if self._nodes.is_local(node): uid = Utils.get_user_id(uid) conn_text = QC.translate("stats", """ <b>{0}</b><br><br> <b>Time:</b> {1}<br><br> <b>Process:</b><br>{2}<br> <b>Cmdline:</b><br>{3}<br> <b>CWD:</b><br>{4}<br><br> <b>UID:</b> {5} <b>PID:</b> {6}<br> <br> <b>Node:</b> {7}<br><br> <b>{8}</b> {9}:{10} -> {11} ({12}):{13} <br><br> <b>Rule:</b><br> {14} """.format( records.value(ConnFields.Action).upper(), records.value(ConnFields.Time), records.value(ConnFields.Process), records.value(ConnFields.Cmdline), records.value(ConnFields.CWD), uid, records.value(ConnFields.PID), node, records.value(ConnFields.Protocol).upper(), records.value(ConnFields.SrcPort), records.value(ConnFields.SrcIP), records.value(ConnFields.DstIP), records.value(ConnFields.DstHost), records.value(ConnFields.DstPort), records.value(ConnFields.Rule) )) self.showText(conn_text) ���������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/firewall.py��������������������������������������������������0000664�0000000�0000000�00000036743�15003540030�0022274�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys import time import os import os.path import json from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtCore import QCoreApplication as QC from opensnitch.utils import Icons, Message from opensnitch.config import Config from opensnitch.nodes import Nodes from opensnitch.dialogs.firewall_rule import FwRuleDialog import opensnitch.firewall as Fw import opensnitch.firewall.profiles as FwProfiles import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() DIALOG_UI_PATH = "%s/../res/firewall.ui" % os.path.dirname(sys.modules[__name__].__file__) class FirewallDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): LOG_TAG = "[fw dialog]" COMBO_IN = 0 COMBO_OUT = 1 POLICY_ACCEPT = 0 POLICY_DROP = 1 _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) def __init__(self, parent=None, appicon=None, node=None): QtWidgets.QDialog.__init__(self, parent) self.setupUi(self) self.setWindowIcon(appicon) self.appicon = appicon # TODO: profiles are ready to be used. They need to be tested, and # create some default profiles (home, office, public, ...) self.comboProfile.setVisible(False) self.lblProfile.setVisible(False) self.secHighIcon = Icons.new(self, "security-high") self.secMediumIcon = Icons.new(self, "security-medium") self.secLowIcon = Icons.new(self, "security-low") self.lblStatusIcon.setPixmap(self.secHighIcon.pixmap(96, 96)) self._fwrule_dialog = FwRuleDialog(appicon=self.appicon) self._cfg = Config.get() self._fw = Fw.Firewall.instance() self._nodes = Nodes.instance() self._fw_profiles = {} self._last_profile = { self.COMBO_IN: FwProfiles.ProfileAcceptInput.value, self.COMBO_OUT: FwProfiles.ProfileAcceptInput.value } self._notification_callback.connect(self._cb_notification_callback) self._notifications_sent = {} self._nodes.nodesUpdated.connect(self._cb_nodes_updated) self.cmdNewRule.clicked.connect(self._cb_new_rule_clicked) self.cmdAllowOUTService.clicked.connect(self._cb_allow_out_service_clicked) self.cmdAllowINService.clicked.connect(self._cb_allow_in_service_clicked) self.comboInput.currentIndexChanged.connect(lambda: self._cb_combo_policy_changed(self.COMBO_IN)) self.comboProfile.currentIndexChanged.connect(self._cb_combo_profile_changed) self.sliderFwEnable.valueChanged.connect(self._cb_enable_fw_changed) self.cmdClose.clicked.connect(self._cb_close_clicked) self.cmdHelp.clicked.connect( lambda: QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_SYSFW_URL)) ) # TODO: when output policy is set to Drop, all outbound traffic is # blocked. #self.comboOutput.currentIndexChanged.connect(lambda: self._cb_combo_policy_changed(self.COMBO_OUT)) if QtGui.QIcon.hasThemeIcon("document-new"): return closeIcon = Icons.new(self, "window-close") excludeIcon = Icons.new(self, "go-up") allowInIcon = Icons.new(self, "go-down") newIcon = Icons.new(self, "document-new") helpIcon = Icons.new(self, "help-browser") self.cmdClose.setIcon(closeIcon) self.cmdAllowOUTService.setIcon(excludeIcon) self.cmdAllowINService.setIcon(allowInIcon) self.cmdNewRule.setIcon(newIcon) self.cmdHelp.setIcon(helpIcon) @QtCore.pyqtSlot(ui_pb2.NotificationReply) def _cb_notification_callback(self, reply): self.comboInput.setEnabled(True) if reply.id in self._notifications_sent: if reply.code == ui_pb2.OK: rep = self._notifications_sent[reply.id] self._set_status_successful(QC.translate("firewall", "Configuration applied.")) else: self._set_status_error(QC.translate("firewall", "Error: {0}").format(reply.data)) del self._notifications_sent[reply.id] else: print(self.LOG_TAG, "unknown notification:", reply) @QtCore.pyqtSlot(int) def _cb_nodes_updated(self, total): self._check_fw_status() def _cb_combo_profile_changed(self, idx): combo_profile = self._fw_profiles[idx] json_profile = json.dumps(list(combo_profile.values())[0]['Profile']) for addr in self._nodes.get(): fwcfg = self._nodes.get_node(addr)['firewall'] ok, err = self._fw.apply_profile(addr, json_profile) if ok: self.send_notification(addr, fwcfg) else: self._set_status_error(QC.translate("firewall", "error adding profile extra rules:", err)) def _cb_combo_policy_changed(self, combo): self._reset_status_message() self.comboInput.setEnabled(False) wantedProfile = FwProfiles.ProfileAcceptInput.value if combo == self.COMBO_OUT: wantedProfile = FwProfiles.ProfileAcceptOutput.value if self.comboOutput.currentIndex() == self.POLICY_DROP: wantedProfile = FwProfiles.ProfileDropOutput.value else: if self.comboInput.currentIndex() == self.POLICY_DROP: wantedProfile = FwProfiles.ProfileDropInput.value if combo == self.COMBO_IN and \ self.comboInput.currentIndex() == self.POLICY_ACCEPT: json_profile = json.dumps(FwProfiles.ProfileDropInput.value) for addr in self._nodes.get(): fwcfg = self._nodes.get_node(addr)['firewall'] ok, err = self._fw.delete_profile(addr, json_profile) if not ok: print(err) json_profile = json.dumps(wantedProfile) for addr in self._nodes.get(): fwcfg = self._nodes.get_node(addr)['firewall'] ok, err = self._fw.apply_profile(addr, json_profile) if ok: self.send_notification(addr, fwcfg) else: self._set_status_error(QC.translate("firewall", "Policy not applied: {0}".format(err))) self._last_profile[combo] = wantedProfile def _cb_new_rule_clicked(self): self.new_rule() def _cb_allow_out_service_clicked(self): self.allow_out_service() def _cb_allow_in_service_clicked(self): self.allow_in_service() def _cb_enable_fw_changed(self, enable): if self._nodes.count() == 0: self.sliderFwEnable.blockSignals(True) self.sliderFwEnable.setValue(False) self.sliderFwEnable.blockSignals(False) return self.enable_fw(enable) def _cb_close_clicked(self): self._close() def _load_nodes(self): self._nodes = self._nodes.get() def _close(self): self.hide() def _change_fw_backend(self, addr, node_cfg): nid, notif = self._nodes.change_node_config(addr, node_cfg, self._notification_callback) self._notifications_sent[nid] = notif def showEvent(self, event): super(FirewallDialog, self).showEvent(event) self._reset_fields() self._check_fw_status() self._fw_profiles = FwProfiles.Profiles.load_predefined_profiles() self.comboProfile.blockSignals(True) for pr in self._fw_profiles: self.comboProfile.addItem([pr[k] for k in pr][0]['Name']) self.comboProfile.blockSignals(False) def send_notification(self, node_addr, fw_config): self._set_status_message(QC.translate("firewall", "Applying changes...")) nid, notif = self._nodes.reload_fw(node_addr, fw_config, self._notification_callback) self._notifications_sent[nid] = {'addr': node_addr, 'notif': notif} def _check_fw_status(self): self.lblFwStatus.setText("") self.sliderFwEnable.blockSignals(True) self.comboInput.blockSignals(True) self.comboOutput.blockSignals(True) self.comboProfile.blockSignals(True) self._disable_widgets() try: enableFw = False enableFwBtn = (self._nodes.count() > 0) self.sliderFwEnable.blockSignals(True) self.sliderFwEnable.setEnabled(enableFwBtn) self.sliderFwEnable.blockSignals(False) if not enableFwBtn: return # TODO: handle nodes' firewall properly for addr in self._nodes.get(): node = self._nodes.get_node(addr) self._fwConfig = node['firewall'] enableFw |= self._fwConfig.Enabled if self.fw_is_incompatible(addr, node): enableFw = False return # XXX: Here we loop twice over the chains. We could have 1 loop. pol_in = self._fw.chains.get_policy(addr, Fw.Hooks.INPUT.value) pol_out = self._fw.chains.get_policy(addr, Fw.Hooks.OUTPUT.value) if pol_in != None: self.comboInput.setCurrentIndex( Fw.Policy.values().index(pol_in) ) else: self._set_status_error(QC.translate("firewall", "Error getting INPUT chain policy")) self._disable_widgets() if pol_out != None: self.comboOutput.setCurrentIndex( Fw.Policy.values().index(pol_out) ) else: self._set_status_error(QC.translate("firewall", "Error getting OUTPUT chain policy")) self._disable_widgets() except Exception as e: self._set_status_error("Firewall status error (report on github please): {0}".format(e)) finally: # some nodes may have the firewall disabled whilst other enabled #if not enableFw: # self.lblFwStatus(QC.translate("firewall", "Some nodes have the firewall disabled")) self._disable_widgets(not enableFw) self.lblStatusIcon.setEnabled(enableFw) self.sliderFwEnable.setValue(enableFw) self.sliderFwEnable.blockSignals(False) self.comboInput.blockSignals(False) self.comboOutput.blockSignals(False) self.comboProfile.blockSignals(False) def fw_is_incompatible(self, addr, node): """Check if the fw is compatible with this GUI. If it's incompatible, disable the option to enable it. """ incompatible = False # firewall iptables is not supported from the GUI. # display a warning node_cfg = json.loads(node['data'].config) if node_cfg['Firewall'] == "iptables": self._disable_widgets() self.sliderFwEnable.setEnabled(False) if self.isHidden() == False and self.change_fw(addr, node_cfg): node_cfg['Firewall'] = "nftables" self.sliderFwEnable.setEnabled(True) self.enable_fw(True) self._change_fw_backend(addr, node_cfg) return False incompatible = True if node['data'].systemFirewall.Version == 0: self._disable_widgets() self.sliderFwEnable.setEnabled(False) self.lblFwStatus.setText( QC.translate("firewall", "<html>The firewall configuration is outdated,\n" "you need to update it to the new format: <a href=\"{0}\">learn more</a>" "</html>".format(Config.HELP_SYS_RULES_URL) )) incompatible = True return incompatible def change_fw(self, addr, node_cfg): """Ask the user to change fw iptables to nftables """ ret = Message.yes_no( QC.translate("firewall", "In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'" ), QC.translate("firewall", "Change default firewall to 'nftables' on node {0}?".format(addr)), QtWidgets.QMessageBox.Warning) if ret != QtWidgets.QMessageBox.Cancel: return True return False def enable_fw(self, enable): try: self._disable_widgets(not enable) if enable: self._set_status_message(QC.translate("firewall", "Enabling firewall...")) else: self._set_status_message(QC.translate("firewall", "Disabling firewall...")) # if previous input policy was DROP, when disabling the firewall it # must be ACCEPT to allow output traffic. if not enable and self.comboInput.currentIndex() == self.POLICY_DROP: self.comboInput.blockSignals(True) self.comboInput.setCurrentIndex(self.POLICY_ACCEPT) self.comboInput.blockSignals(False) for addr in self._nodes.get(): json_profile = json.dumps(FwProfiles.ProfileAcceptInput.value) ok, err = self._fw.apply_profile(addr, json_profile) if not ok: self._set_status_error( QC.translate("firewall", "Error applying INPUT ACCEPT profile: {0}".format(err)) ) return for addr in self._nodes.get(): # FIXME: # Due to how the daemon reacts to events when the fw configuration # is modified, changing the policy + disabling the fw doesn't work # as expected. # The daemon detects that the fw is disabled, and it never changes # the policy. # As a workaround to this problem, we send 2 fw changes: # - one for changing the policy # - another one for disabling the fw fwcfg = self._nodes.get_node(addr)['firewall'] self.send_notification(addr, fwcfg) time.sleep(0.5) fwcfg.Enabled = True if enable else False self.send_notification(addr, fwcfg) self.lblStatusIcon.setEnabled(enable) self.policiesBox.setEnabled(enable) time.sleep(0.5) except Exception as e: QC.translate("firewall", "Error: {0}".format(e)) def load_rule(self, addr, uuid): self._fwrule_dialog.load(addr, uuid) def new_rule(self): self._fwrule_dialog.new() def allow_out_service(self): self._fwrule_dialog.exclude_service(self.COMBO_OUT) def allow_in_service(self): self._fwrule_dialog.exclude_service(self.COMBO_IN) def _set_status_error(self, msg): self.statusLabel.show() self.statusLabel.setStyleSheet('color: red') self.statusLabel.setText(msg) def _set_status_successful(self, msg): self.statusLabel.show() self.statusLabel.setStyleSheet('color: green') self.statusLabel.setText(msg) def _set_status_message(self, msg): self.statusLabel.show() self.statusLabel.setStyleSheet('color: darkorange') self.statusLabel.setText(msg) def _reset_status_message(self): self.statusLabel.setText("") self.statusLabel.hide() def _reset_fields(self): self._reset_status_message() def _disable_widgets(self, disable=True): self.comboInput.setEnabled(not disable) #self.comboOutput.setEnabled(not disable) self.cmdNewRule.setEnabled(not disable) self.cmdAllowOUTService.setEnabled(not disable) self.cmdAllowINService.setEnabled(not disable) �����������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/firewall_rule.py���������������������������������������������0000664�0000000�0000000�00000201175�15003540030�0023314�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys import os import os.path import ipaddress from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtCore import QCoreApplication as QC from opensnitch.config import Config from opensnitch.nodes import Nodes from opensnitch.utils import NetworkServices, NetworkInterfaces, QuickHelp, Icons, Utils import opensnitch.firewall as Fw from opensnitch.firewall.utils import Utils as FwUtils import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() DIALOG_UI_PATH = "%s/../res/firewall_rule.ui" % os.path.dirname(sys.modules[__name__].__file__) class FwRuleDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): LOG_TAG = "[fw rule dialog]" ACTION_IDX_DENY = 0 ACTION_IDX_ALLOW = 1 IN = 0 OUT = 1 FORWARD = 2 PREROUTING = 3 POSTROUTING = 4 OP_NEW = 0 OP_SAVE = 1 OP_DELETE = 2 FORM_TYPE_SIMPLE = 0 FORM_TYPE_EXCLUDE_SERVICE = 1 FORM_TYPE_ALLOW_IN_SERVICE = 2 FORM_TYPE = FORM_TYPE_SIMPLE STATM_DPORT = 0 STATM_SPORT = 1 STATM_DEST_IP = 2 STATM_SOURCE_IP = 3 STATM_IIFNAME = 4 STATM_OIFNAME = 5 STATM_CT_SET = 6 STATM_CT_MARK = 7 STATM_CT_STATE = 8 STATM_META_SET_MARK = 9 STATM_META = 10 STATM_ICMP = 11 STATM_ICMPv6 = 12 STATM_LOG = 13 STATM_QUOTA = 14 STATM_COUNTER = 15 STATM_LIMIT = 16 _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) def __init__(self, parent=None, appicon=None): QtWidgets.QDialog.__init__(self, parent) self.setupUi(self) self.setWindowIcon(appicon) self._fw = Fw.Firewall.instance() self._nodes = Nodes.instance() self.net_srv = NetworkServices() self.statements = {} self.st_num = 0 self.STATM_LIST = [ "", QC.translate("firewall", "Dest Port"), QC.translate("firewall", "Source Port"), QC.translate("firewall", "Dest IP"), QC.translate("firewall", "Source IP"), QC.translate("firewall", "Input interface"), QC.translate("firewall", "Output interface"), QC.translate("firewall", "Set conntrack mark"), QC.translate("firewall", "Match conntrack mark"), QC.translate("firewall", "Match conntrack state(s)"), QC.translate("firewall", "Set mark on packet"), QC.translate("firewall", "Match packet information"), #"TCP", #"UDP", "ICMP", "ICMPv6", "LOG", QC.translate("firewall", "Bandwidth quotas"), "COUNTER", QC.translate("firewall", "Rate limit connections"), ] self.STATM_CONF = { self.STATM_DPORT: { 'name': Fw.Statements.TCP.value, # tcp, udp, dccp, sctp 'tooltip': QC.translate("firewall", """ Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 """), 'keys': [ {'key': Fw.Statements.DPORT.value, 'values': self.net_srv.to_array()} ] }, self.STATM_SPORT: { 'name': Fw.Statements.TCP.value, 'tooltip': QC.translate("firewall", """ Supported formats: - Simple: 23 - Ranges: 80-1024 - Multiple ports: 80,443,8080 """), 'keys': [ {'key': Fw.Statements.SPORT.value, 'values': self.net_srv.to_array()} ] }, self.STATM_DEST_IP: { 'name': Fw.Statements.IP.value, # ip or ip6 'tooltip': QC.translate("firewall", """ Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 """), 'keys': [ {'key': Fw.Statements.DADDR.value, 'values': []} ] }, self.STATM_SOURCE_IP: { 'name': Fw.Statements.IP.value, 'tooltip': QC.translate("firewall", """ Supported formats: - Simple: 1.2.3.4 - IP ranges: 1.2.3.100-1.2.3.200 - Network ranges: 1.2.3.4/24 """), 'keys': [ {'key': Fw.Statements.SADDR.value, 'values': []} ] }, self.STATM_IIFNAME: { 'name': Fw.Statements.IIFNAME.value, 'tooltip': QC.translate("firewall", """Match input interface. Regular expressions not allowed. Use * to match multiple interfaces."""), 'keys': [ {'key': "", 'values': []} ] }, self.STATM_OIFNAME: { 'name': Fw.Statements.OIFNAME.value, 'tooltip': QC.translate("firewall", """Match output interface. Regular expressions not allowed. Use * to match multiple interfaces."""), 'keys': [ {'key': "", 'values': []} ] }, self.STATM_CT_SET: { 'name': Fw.Statements.CT.value, 'tooltip': QC.translate("firewall", "Set a conntrack mark on the connection, in decimal format."), 'keys': [ # we need 2 keys for this expr: key: set, value: <empty>, key: mark, value: xxx {'key': Fw.ExprCt.SET.value, 'values': None}, # must be empty {'key': Fw.ExprCt.MARK.value, 'values': []} ] }, # match mark self.STATM_CT_MARK: { 'name': Fw.Statements.CT.value, 'tooltip': QC.translate("firewall", "Match a conntrack mark of the connection, in decimal format."), 'keys': [ {'key': Fw.ExprCt.MARK.value, 'values': []} ] }, self.STATM_CT_STATE: { 'name': Fw.Statements.CT.value, 'tooltip': QC.translate("firewall", """Match conntrack states. Supported formats: - Simple: new - Multiple states separated by commas: related,new """), 'keys': [ { 'key': Fw.ExprCt.STATE.value, 'values': [Fw.ExprCt.NEW.value, Fw.ExprCt.ESTABLISHED.value, Fw.ExprCt.RELATED.value, Fw.ExprCt.INVALID.value] } ] }, self.STATM_META: { 'name': Fw.Statements.META.value, 'tooltip': QC.translate("firewall", """ Match packet's metainformation. Value must be in decimal format, except for the "l4proto" option. For l4proto it can be a lower case string, for example: tcp udp icmp, etc If the value is decimal for protocol or lproto, it'll use it as the code of that protocol. """), 'keys': [ {'key': Fw.ExprMeta.MARK.value, 'values': []}, {'key': Fw.ExprMeta.L4PROTO.value, 'values': Fw.Protocols.values()} ] }, self.STATM_META_SET_MARK: { 'name': Fw.Statements.META.value, 'tooltip': QC.translate("firewall", "Set a mark on the packet matching the specified conditions. The value is in decimal format."), 'keys': [ {'key': Fw.ExprMeta.SET.value, 'values': None}, {'key': Fw.ExprMeta.MARK.value, 'values': []} ] }, self.STATM_ICMP: { 'name': Fw.Statements.ICMP.value, 'tooltip': QC.translate("firewall", """ Match ICMP codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply """), 'keys': [ {'key': "type", 'values': Fw.ExprICMP.values()} ] }, self.STATM_ICMPv6: { 'name': Fw.Statements.ICMPv6.value, 'tooltip': QC.translate("firewall", """ Match ICMPv6 codes. Supported formats: - Simple: echo-request - Multiple separated by commas: echo-request,echo-reply """), 'keys': [ {'key': "type", 'values': Fw.ExprICMP.values()} ] }, self.STATM_LOG: { 'name': Fw.Statements.LOG.value, 'tooltip': QC.translate("firewall", "Print a message when this rule matches a packet."), 'keys': [ {'key': Fw.ExprLog.PREFIX.value, 'values': []} ] }, self.STATM_QUOTA: { 'name': Fw.ExprQuota.QUOTA.value, 'tooltip': QC.translate("firewall", """ Apply quotas on connections. For example when: - "quota over 10/mbytes" -> apply the Action defined (DROP) - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS, for example: - 10mbytes, 1/gbytes, etc """), 'keys': [ {'key': Fw.ExprQuota.OVER.value, 'values': []}, {'key': Fw.ExprQuota.UNIT.value, 'values': [ "1/{0}".format(Fw.RateUnits.BYTES.value), "1/{0}".format(Fw.RateUnits.KBYTES.value), "1/{0}".format(Fw.RateUnits.MBYTES.value), "1/{0}".format(Fw.RateUnits.GBYTES.value), ]} ] }, self.STATM_COUNTER: { 'name': Fw.ExprCounter.COUNTER.value, 'tooltip': QC.translate("firewall", ""), # packets, bytes 'keys': [ {'key': Fw.ExprCounter.PACKETS.value, 'values': None}, {'key': Fw.ExprCounter.NAME.value, 'values': []} ] }, # TODO: https://github.com/evilsocket/opensnitch/wiki/System-rules#rules-expressions self.STATM_LIMIT: { 'name': Fw.ExprLimit.LIMIT.value, 'tooltip': QC.translate("firewall", """ Apply limits on connections. For example when: - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) (When there're more than 10MB per minute, apply an Action) - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) The value must be in the format: VALUE/UNITS/TIME, for example: - 10/mbytes/minute, 1/gbytes/hour, etc """), 'keys': [ {'key': Fw.ExprLimit.OVER.value, 'values': []}, {'key': Fw.ExprLimit.UNITS.value, 'values': [ "1/{0}/{1}".format(Fw.RateUnits.BYTES.value, Fw.TimeUnits.SECOND.value), "1/{0}/{1}".format(Fw.RateUnits.KBYTES.value, Fw.TimeUnits.MINUTE.value), "1/{0}/{1}".format(Fw.RateUnits.MBYTES.value, Fw.TimeUnits.HOUR.value), "1/{0}/{1}".format(Fw.RateUnits.GBYTES.value, Fw.TimeUnits.DAY.value), ]} ] }, #self.STATM_TCP: { # 'name': Fw.Statements.TCP.value, # ['dport', 'sport' ... ] # 'key': Fw.Statements.DADDR.value, # 'values': [] #}, #self.STATM_UDP: { # 'name': Fw.Statements.UDP.value, # 'key': Fw.Statements.DADDR.value, # ['dport', 'sport' ... ] # 'values': [] #}, } self._notification_callback.connect(self._cb_notification_callback) self._notifications_sent = {} self.uuid = "" self.simple_port_idx = None self._nodes.nodesUpdated.connect(self._cb_nodes_updated) self.cmdClose.clicked.connect(self._cb_close_clicked) self.cmdReset.clicked.connect(self._cb_reset_clicked) self.cmdAdd.clicked.connect(self._cb_add_clicked) self.cmdSave.clicked.connect(self._cb_save_clicked) self.cmdDelete.clicked.connect(self._cb_delete_clicked) self.helpButton.clicked.connect(self._cb_help_button_clicked) self.comboVerdict.currentIndexChanged.connect(self._cb_verdict_changed) self.lineVerdictParms.textChanged.connect(self._cb_verdict_parms_changed) self.checkEnable.toggled.connect(self._cb_check_enable_toggled) self.lineDescription.textChanged.connect(self._cb_description_changed) self.cmdAddStatement.clicked.connect(self._cb_add_new_statement) self.cmdDelStatement.clicked.connect(self._cb_del_statement) # remove default page self.toolBoxSimple.removeItem(0) self.add_new_statement("", self.toolBoxSimple) self.hboxAdvanced.setVisible(False) # setTabVisible not available on <= 5.14 #self.tabWidget.setTabVisible(0, True) if QtGui.QIcon.hasThemeIcon("emblem-default"): return # ----------------------------------------------------------- saveIcon = Icons.new(self, "document-save") closeIcon = Icons.new(self, "window-close") delIcon = Icons.new(self, "edit-delete") addIcon = Icons.new(self, "list-add") remIcon = Icons.new(self, "list-remove") helpIcon = Icons.new(self, "help-browser") self.cmdSave.setIcon(saveIcon) self.cmdDelete.setIcon(delIcon) self.cmdClose.setIcon(closeIcon) self.cmdAdd.setIcon(addIcon) self.helpButton.setIcon(helpIcon) self.cmdAddStatement.setIcon(addIcon) self.cmdDelStatement.setIcon(remIcon) def show(self): super(FwRuleDialog, self).show() self._reset_fields() if FwUtils.isProtobufSupported() == False: self._disable_controls() self._disable_buttons() self._set_status_error( QC.translate( "firewall", "Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior\n(pip3 install --ignore-installed protobuf==3.8.0)" ) ) return False self._load_nodes() self.comboDirection.currentIndexChanged.connect(self._cb_direction_changed) return True def _close(self): self.comboDirection.currentIndexChanged.disconnect(self._cb_direction_changed) self.hide() @QtCore.pyqtSlot(ui_pb2.NotificationReply) def _cb_notification_callback(self, reply): self._enable_buttons() try: if reply.id not in self._notifications_sent: return rep = self._notifications_sent[reply.id] if reply.code == ui_pb2.OK: if 'operation' in rep and rep['operation'] == self.OP_DELETE: self.tabWidget.setDisabled(True) self._set_status_successful(QC.translate("firewall", "Rule deleted")) self._disable_controls() del self._notifications_sent[reply.id] return if 'operation' in rep and rep['operation'] == self.OP_SAVE: self._set_status_successful(QC.translate("firewall", "Rule saved")) else: self._set_status_successful(QC.translate("firewall", "Rule added")) else: # XXX: The errors returned by the nftables lib are not really descriptive. # "invalid argument", "no such file or directory", without context # 1st one: invalid combination of table/chain/priorities? # 2nd one: does the table/chain exist? errormsg = QC.translate("firewall", "Error adding rules:\n{0}".format(reply.data)) if 'operation' in rep and rep['operation'] == self.OP_SAVE: if 'uuid' in rep and rep['uuid'] in reply.data: errormsg = QC.translate("firewall", "Error saving rule") else: self._set_status_message(QC.translate("firewall", "Rule saved, but there're other rules with errors (REVIEW):\n{0}".format(reply.data))) return self._set_status_error(errormsg) except Exception as e: print("[fw rule dialog exception] notif error:", e) finally: if reply.id in self._notifications_sent: del self._notifications_sent[reply.id] @QtCore.pyqtSlot(int) def _cb_nodes_updated(self, total): self.tabWidget.setDisabled(True if total == 0 else False) def closeEvent(self, e): self._close() def _cb_check_enable_toggled(self, status): self._enable_save() def _cb_description_changed(self, text): self._enable_save() def _cb_help_button_clicked(self): QuickHelp.show( QC.translate("firewall", "You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>" \ "ports: 22 or 22,443 or 50000-60000<br>" \ "IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>" \ "Values: echo-reply,echo-request<br>" \ "Values: new,established,related" ) ) def _cb_close_clicked(self): self._close() def _cb_delete_clicked(self): node_addr, node, chain, err = self.form_to_protobuf() if err != None: self._set_status_error(QC.translate("firewall", "Invalid rule: {0}".format(err))) return self._set_status_message(QC.translate("firewall", "Deleting rule, wait")) ok, fw_config = self._fw.delete_rule(node_addr, self.uuid) if not ok: self._set_status_error(QC.translate("firewall", "Error updating rule")) return if self.comboNodes.currentIndex() == 0: self.send_notifications(node['firewall'], self.OP_DELETE) else: self.send_notification(node_addr, node['firewall'], self.OP_DELETE) def _cb_save_clicked(self): if len(self.statements) == 0: self._set_status_message(QC.translate("firewall", "Add at least one statement.")) return node_addr, node, chain, err = self.form_to_protobuf() if err != None: self._set_status_error(QC.translate("firewall", "Invalid rule: {0}".format(err))) return self._set_status_message(QC.translate("firewall", "Adding rule, wait")) ok, err = self._fw.update_rule(node_addr, self.uuid, chain) if not ok: self._set_status_error(QC.translate("firewall", "Error updating rule ({0}): {1}".format(node_addr, err))) return self._enable_buttons(False) if self.comboNodes.currentIndex() == 0: self.send_notification(node_addr, node['firewall'], self.OP_SAVE, self.uuid) else: self.send_notifications(node['firewall'], self.OP_SAVE) def _cb_reset_clicked(self): self._reset_widgets("", self.toolBoxSimple) self.add_new_statement(QC.translate("firewall", "<select a statement>"), self.toolBoxSimple) def _cb_add_clicked(self): if len(self.statements) == 0: self._set_status_message(QC.translate("firewall", "Add at least one statement.")) return node_addr, node, chain, err = self.form_to_protobuf() if err != None: self._set_status_error(QC.translate("firewall", "Invalid rule: {0}".format(err))) return ok, err = self._fw.insert_rule(node_addr, chain) if not ok: self._set_status_error(QC.translate("firewall", "Error adding rule: {0}".format(err))) return self._set_status_message(QC.translate("firewall", "Adding rule, wait")) self._enable_buttons(False) if self.comboNodes.currentIndex() == 0: self.send_notification(node_addr, node['firewall'], self.OP_NEW, chain.Rules[0].UUID) else: self.send_notifications(node['firewall'], self.OP_NEW) def _cb_add_new_statement(self): self._enable_save() self.add_new_statement(QC.translate("firewall", "<select a statement>"), self.toolBoxSimple) def _cb_del_statement(self): self._enable_save() idx = self.toolBoxSimple.currentIndex() if idx < 0: return if idx in self.statements: del self.statements[idx] w = self.toolBoxSimple.widget(idx) if w != None: w.setParent(None) self._reorder_toolbox_pages() def _cb_statem_combo_changed(self, idx): self._enable_save() st_idx = self.toolBoxSimple.currentIndex() self._configure_statem_value_opts(st_idx) w = self.statements[st_idx] tidx = 0 if idx == 0 else idx-1 w['value'].setToolTip(self.STATM_CONF[tidx]['tooltip']) self._set_statement_title(st_idx, w['value'].currentText()) def _cb_statem_value_changed(self, val): self._enable_save() st_idx = self.toolBoxSimple.currentIndex() self._set_statement_title(st_idx) def _cb_statem_value_index_changed(self, idx): self._enable_save() st_idx = self.toolBoxSimple.currentIndex() w = self.statements[st_idx] idx = w['what'].currentIndex()-1 # first item is blank val = w['value'].currentText().lower() if idx != -1 and (idx == self.STATM_SPORT or idx == self.STATM_DPORT): # automagically choose the protocol for the selected port: # echo/7 (tcp) -> tcp if Fw.PortProtocols.TCP.value in val: w['opts'].setCurrentIndex( Fw.PortProtocols.values().index(Fw.PortProtocols.TCP.value) ) elif Fw.PortProtocols.UDP.value in val: w['opts'].setCurrentIndex( Fw.PortProtocols.values().index(Fw.PortProtocols.UDP.value) ) self._set_statement_title(st_idx) def _cb_statem_op_changed(self, idx): self._enable_save() st_idx = self.toolBoxSimple.currentIndex() self._set_statement_title(st_idx) def _cb_statem_opts_changed(self, idx): self._enable_save() st_idx = self.toolBoxSimple.currentIndex() self._set_statement_title(st_idx) def _cb_direction_changed(self, idx): self._enable_save() self._is_valid_rule() def _cb_verdict_changed(self, idx): self._enable_save() showVerdictParms = self._has_verdict_parms(idx) self.lineVerdictParms.setVisible(showVerdictParms) self.comboVerdictParms.setVisible(showVerdictParms) self._configure_verdict_parms(idx) def _cb_verdict_parms_changed(self, idx): self._enable_save() def _is_valid_rule(self): if (self.comboVerdict.currentText().lower() == Config.ACTION_REDIRECT or \ self.comboVerdict.currentText().lower() == Config.ACTION_TPROXY or \ self.comboVerdict.currentText().lower() == Config.ACTION_DNAT) and \ (self.comboDirection.currentIndex() == self.IN or self.comboDirection.currentIndex() == self.POSTROUTING): self._set_status_message( QC.translate( "firewall", "{0} cannot be used with IN or POSTROUTING directions.".format(self.comboVerdict.currentText().upper()) ) ) return False elif self.comboVerdict.currentText().lower() == Config.ACTION_SNAT and \ self.comboDirection.currentIndex() != self.POSTROUTING: self._set_status_message( QC.translate( "firewall", "{0} can only be used with POSTROUTING.".format(self.comboVerdict.currentText().upper()) ) ) self.comboDirection.setCurrentIndex(self.POSTROUTING) return False self._set_status_message("") return True def _has_verdict_parms(self, idx): # TODO: # Fw.Verdicts.values()[idx+1] == Config.ACTION_REJECT or \ # Fw.Verdicts.values()[idx+1] == Config.ACTION_JUMP or \ return Fw.Verdicts.values()[idx+1] == Config.ACTION_QUEUE or \ Fw.Verdicts.values()[idx+1] == Config.ACTION_REDIRECT or \ Fw.Verdicts.values()[idx+1] == Config.ACTION_TPROXY or \ Fw.Verdicts.values()[idx+1] == Config.ACTION_DNAT or \ Fw.Verdicts.values()[idx+1] == Config.ACTION_SNAT or \ Fw.Verdicts.values()[idx+1] == Config.ACTION_MASQUERADE def _configure_verdict_parms(self, idx): self.comboVerdictParms.clear() verdict = Fw.Verdicts.values()[idx+1] if verdict == Config.ACTION_QUEUE: self.comboVerdictParms.addItem(QC.translate("firewall", "num"), "num") elif verdict == Config.ACTION_JUMP: self.comboVerdictParms.setVisible(False) elif verdict == Config.ACTION_REDIRECT or \ verdict == Config.ACTION_TPROXY or \ verdict == Config.ACTION_SNAT or \ verdict == Config.ACTION_DNAT: self.comboVerdictParms.addItem(QC.translate("firewall", "to"), "to") elif verdict == Config.ACTION_MASQUERADE: # for persistent,fully-random,etc, options self.comboVerdictParms.addItem("") self.comboVerdictParms.addItem(QC.translate("firewall", "to"), "to") # https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)#Redirect if (verdict == Config.ACTION_REDIRECT or verdict == Config.ACTION_DNAT) and \ (self.comboDirection.currentIndex() != self.OUT and self.comboDirection.currentIndex() != self.PREROUTING): self.comboDirection.setCurrentIndex(self.OUT) elif self.comboVerdict.currentText().lower() == Config.ACTION_SNAT and \ self.comboDirection.currentIndex() != self.POSTROUTING: self.comboDirection.setCurrentIndex(self.POSTROUTING) def _reorder_toolbox_pages(self): tmp = {} for i,k in enumerate(self.statements): tmp[i] = self.statements[k] self.statements = tmp def _reset_widgets(self, title, topWidget): for i in range(topWidget.count()): topWidget.removeItem(i) w = topWidget.widget(i) if w is not None: w.setParent(None) self.statements = {} self.st_num = 0 # if we don't do this, toolbox's subwidgets are not deleted (removed # from the GUI, but not deleted), so sometimes after loading/closing several rules, # you may end up with rules mixed on the same layout/form. self.toolBoxSimple.setParent(None) self.toolBoxSimple = QtWidgets.QToolBox() self.tabWidget.widget(0).layout().addWidget(self.toolBoxSimple) #self.toolBoxSimple.currentChanged.connect(self._cb_toolbox_page_changed) def _set_statement_title(self, st_idx, value=None): """Transform the widgets to nftables rule text format """ self._reset_status_message() self.toolBoxSimple.setItemText(st_idx, "") w = self.statements[st_idx] idx = w['what'].currentIndex()-1 # first item is blank if idx == -1: return st = self.STATM_CONF[idx]['name'] st_opts = w['opts'].currentText() if idx == self.STATM_DEST_IP or idx == self.STATM_SOURCE_IP: st = st_opts if idx == self.STATM_DPORT or idx == self.STATM_SPORT: st = st_opts title = st for keys in self.STATM_CONF[idx]['keys']: title += " " + keys['key'] st_op = Fw.Operator.values()[w['op'].currentIndex()] st_val = w['value'].currentText() if value != None: st_val = value # override previous setup for some statements if idx == self.STATM_META: title = "{0} {1} {2} {3}".format(st, st_opts, st_op, st_val) elif idx == self.STATM_QUOTA: title = "{0} {1} {2}".format(st, st_opts, st_val) elif idx == self.STATM_LIMIT: title = "{0} {1} {2}".format(st, st_opts, st_val) else: title = "{0} {1} {2}".format(title, st_op, st_val) self.toolBoxSimple.setItemText(st_idx, title) def _configure_statem_value_opts(self, st_idx): w = self.statements[st_idx] idx = w['what'].currentIndex()-1 # first item is blank if idx == -1: return w['value'].blockSignals(True); w['opts'].blockSignals(True); oldValue = w['value'].currentText() w['value'].clear() for k in self.STATM_CONF[idx]['keys']: if k['values'] == None: continue w['value'].addItems(k['values']) w['value'].setCurrentText(oldValue) w['opts'].clear() if idx == self.STATM_DPORT or \ idx == self.STATM_SPORT: w['op'].setVisible(True) w['opts'].setVisible(True) w['opts'].addItems(Fw.PortProtocols.values()) elif idx == self.STATM_DEST_IP or \ idx == self.STATM_SOURCE_IP: w['op'].setVisible(True) w['opts'].setVisible(True) w['opts'].addItems(Fw.Family.values()) w['opts'].removeItem(0) # remove 'inet' item elif idx == self.STATM_IIFNAME or idx == self.STATM_OIFNAME: w['op'].setVisible(True) w['opts'].setVisible(False) if self._nodes.is_local(self.comboNodes.currentText()): w['value'].addItems(NetworkInterfaces.list().keys()) w['value'].setCurrentText("") elif idx == self.STATM_META: w['op'].setVisible(True) w['opts'].setVisible(True) # exclude first item of the list w['opts'].addItems(Fw.ExprMeta.values()[1:]) elif idx == self.STATM_ICMP or idx == self.STATM_ICMPv6 or \ idx == self.STATM_CT_STATE or idx == self.STATM_CT_MARK: w['op'].setVisible(True) w['opts'].setVisible(False) elif idx == self.STATM_LOG: w['op'].setVisible(False) w['opts'].setVisible(True) w['opts'].addItems(Fw.ExprLogLevels.values()) w['opts'].setCurrentIndex( # nftables default log level is warn Fw.ExprLogLevels.values().index(Fw.ExprLogLevels.WARN.value) ) elif idx == self.STATM_QUOTA or idx == self.STATM_LIMIT: w['op'].setVisible(False) w['opts'].setVisible(True) w['opts'].addItems([Fw.ExprQuota.OVER.value, Fw.ExprQuota.UNTIL.value]) else: w['op'].setVisible(False) w['opts'].setVisible(False) w['opts'].blockSignals(False); w['value'].blockSignals(False); def add_new_statement(self, title="", topWidget=None): """Creates dynamically the widgets to define firewall rules: statement (dst port, dst ip, log), protocol, operator (==, !=,...) and value (443, 1.1.1.1, etc) Some expressions may have different options (protocol, family, options, etc) """ w = QtWidgets.QWidget() w.setParent(topWidget) l = QtWidgets.QVBoxLayout(w) boxH1 = QtWidgets.QHBoxLayout() boxH2 = QtWidgets.QHBoxLayout() l.addLayout(boxH1) l.addLayout(boxH2) # row 1: | statement | protocol | stWidget = QtWidgets.QComboBox(w) stWidget.addItems(self.STATM_LIST) prots = ["TCP", "UDP", "ICMP"] stOptsWidget = QtWidgets.QComboBox(w) stOptsWidget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) stOptsWidget.addItems(prots) # row 2: | operator | value | ops = [ QC.translate("firewall", "Equal"), QC.translate("firewall", "Not equal"), QC.translate("firewall", "Greater or equal than"), QC.translate("firewall", "Greater than"), QC.translate("firewall", "Less or equal than"), QC.translate("firewall", "Less than") ] stOpWidget = QtWidgets.QComboBox(w) stOpWidget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) stOpWidget.addItems(ops) stValueWidget = QtWidgets.QComboBox(w) stValueWidget.setEditable(True) stValueWidget.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) stValueWidget.setCurrentText("") # add statement, proto/opts, operator and value boxH1.addWidget(stWidget) boxH1.addWidget(stOptsWidget) boxH2.addWidget(stOpWidget) boxH2.addWidget(stValueWidget) w.setLayout(l) # insert page after current index curIdx = self.toolBoxSimple.currentIndex() topWidget.insertItem(curIdx+1, w, title) topWidget.setCurrentIndex(curIdx+1) # if current index is not the last one, reorder statements if curIdx+1 != self.st_num: for i in range(curIdx+1, self.st_num): if i in self.statements: self.statements[i+1] = self.statements[i] self.statements[curIdx+1] = { 'what': stWidget, 'opts': stOptsWidget, 'op': stOpWidget, 'value': stValueWidget } stWidget.currentIndexChanged.connect(self._cb_statem_combo_changed) stOpWidget.currentIndexChanged.connect(self._cb_statem_op_changed) stOptsWidget.currentIndexChanged.connect(self._cb_statem_opts_changed) stValueWidget.currentIndexChanged.connect(self._cb_statem_value_index_changed) stValueWidget.currentTextChanged.connect(self._cb_statem_value_changed) self.st_num += 1 def _load_nodes(self): self.comboNodes.clear() self._node_list = self._nodes.get() #self.comboNodes.addItem(QC.translate("firewall", "All")) for addr in self._node_list: self.comboNodes.addItem(addr) if len(self._node_list) == 0: self.tabWidget.setDisabled(True) def _load_meta_statement(self, exp, idx): try: isMultiProto = False isSetMark = False newStatm = self.STATM_SPORT newValue = "" optsValue = "" for v in exp.Statement.Values: if v.Key == Fw.ExprMeta.SET.value: isSetMark = True continue if isSetMark and v.Key == Fw.ExprMeta.MARK.value: newStatm = self.STATM_META_SET_MARK if self._is_valid_int_value(v.Value): newValue = v.Value else: self._set_status_error( QC.translate( "firewall", "Invalid mark ({0})".format(v.Value) ) ) break if v.Key == Fw.ExprMeta.L4PROTO.value: optsValue = v.Value if v.Key == Fw.Statements.SPORT.value: isMultiProto = True newValue = v.Value break elif v.Key == Fw.Statements.DPORT.value: newStatm = self.STATM_DPORT isMultiProto = True newValue = v.Value break if isSetMark: self.statements[idx]['what'].setCurrentIndex(newStatm+1) self.statements[idx]['value'].setCurrentText(newValue) elif isMultiProto: self.statements[idx]['what'].setCurrentIndex(newStatm+1) self.statements[idx]['opts'].setCurrentIndex( Fw.PortProtocols.values().index(optsValue) ) try: self.statements[idx]['value'].setCurrentIndex( self.net_srv.index_by_port(newValue) ) except: self.statements[idx]['value'].setCurrentText(newValue) else: self.statements[idx]['what'].setCurrentIndex(self.STATM_META+1) self.statements[idx]['opts'].setCurrentIndex( # first item of the list is "set", not present in the combobox Fw.ExprMeta.values().index(exp.Statement.Values[0].Key)-1 ) self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) except Exception as e: print("_load_meta_statement() exception:", e) self._set_status_message(e) def _load_limit_statement(self, exp, idx): try: self.statements[idx]['what'].setCurrentIndex(self.STATM_LIMIT+1) self.statements[idx]['opts'].setCurrentIndex(1) lval = "" for v in exp.Statement.Values: if v.Key == Fw.ExprLimit.OVER.value: self.statements[idx]['opts'].setCurrentIndex(0) elif v.Key == Fw.ExprLimit.UNITS.value: lval = v.Value elif v.Key == Fw.ExprLimit.RATE_UNITS.value: lval = "%s/%s" % (lval, v.Value) elif v.Key == Fw.ExprLimit.TIME_UNITS.value: lval = "%s/%s" % (lval, v.Value) self.statements[idx]['value'].setCurrentText(lval) except Exception as e: print("_load_limit_statement() exception:", e) self._set_status_message(e) def _load_ct_statement(self, exp, idx): """load CT statements, for example: Name: ct, Key: set, Key: mark, Value: 123 Name: ct, Key: mark, Value: 123 Name: ct, Key: state, value: new,established """ try: if exp.Statement.Values[0].Key == Fw.ExprCt.STATE.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_CT_STATE+1) self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) for v in exp.Statement.Values: curText = self.statements[idx]['value'].currentText() if v.Value not in curText: self.statements[idx]['value'].setCurrentText( "{0},{1}".format( curText, v.Value ) ) elif exp.Statement.Values[0].Key == Fw.ExprCt.SET.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_CT_SET+1) markVal = "" for v in exp.Statement.Values: if v.Key == Fw.ExprCt.MARK.value: markVal = v.Value break self.statements[idx]['value'].setCurrentText(markVal) if markVal == "": raise ValueError( QC.translate("firewall", "Warning: ct set mark value is empty, malformed rule?") ) elif exp.Statement.Values[0].Key == Fw.ExprCt.MARK.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_CT_MARK+1) self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) except Exception as e: print("_load_ct_statement() exception:", e) self._set_status_message(e) def load(self, addr, uuid): if not self.show(): return self.FORM_TYPE = self.FORM_TYPE_SIMPLE self.setWindowTitle(QC.translate("firewall", "Firewall rule")) self.cmdDelete.setVisible(True) self.cmdSave.setVisible(True) self.cmdAdd.setVisible(False) self.checkEnable.setVisible(True) self.checkEnable.setEnabled(True) self.checkEnable.setChecked(True) self.frameDirection.setVisible(True) self.comboNodes.setCurrentText(addr) self._enable_buttons() self.uuid = uuid node, rule = self._fw.get_rule_by_uuid(uuid) if rule == None or \ (rule.Hook.lower() != Fw.Hooks.INPUT.value and \ rule.Hook.lower() != Fw.Hooks.FORWARD.value and \ rule.Hook.lower() != Fw.Hooks.PREROUTING.value and \ rule.Hook.lower() != Fw.Hooks.POSTROUTING.value and \ rule.Hook.lower() != Fw.Hooks.OUTPUT.value): hook = "invalid" if rule == None else rule.Hook self._set_status_error(QC.translate("firewall", "Rule hook ({0}) not supported yet".format(hook))) self._disable_controls() return self.checkEnable.setChecked(rule.Rules[0].Enabled) self.lineDescription.setText(rule.Rules[0].Description) self.tabWidget.blockSignals(True); self.hboxAdvanced.setVisible(True) self._reset_widgets("", self.toolBoxSimple) self.tabWidget.setCurrentIndex(0) if len(rule.Rules[0].Expressions) <= 1: self.tabWidget.setTabText(0, QC.translate("firewall", "Simple")) self.add_new_statement("", self.toolBoxSimple) else: for i in enumerate(rule.Rules[0].Expressions): self.add_new_statement("", self.toolBoxSimple) self.tabWidget.setTabText(0, QC.translate("firewall", "Advanced")) self.tabWidget.blockSignals(False); isNotSupported = False idx = 0 for exp in rule.Rules[0].Expressions: #print(idx, "|", exp) # set current page, so the title and opts of each statement is # configured properly. self.toolBoxSimple.setCurrentIndex(idx) if Fw.Utils.isExprPort(exp.Statement.Name): if exp.Statement.Values[0].Key == Fw.Statements.DPORT.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_DPORT+1) elif exp.Statement.Values[0].Key == Fw.Statements.SPORT.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_SPORT+1) try: self.statements[idx]['value'].setCurrentIndex( self.net_srv.index_by_port(exp.Statement.Values[0].Value) ) except: self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) st_name = exp.Statement.Name self.statements[idx]['opts'].setCurrentIndex( Fw.PortProtocols.values().index(st_name.lower()) ) elif exp.Statement.Name == Fw.Statements.IP.value or exp.Statement.Name == Fw.Statements.IP6.value: if exp.Statement.Values[0].Key == Fw.Statements.DADDR.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_DEST_IP+1) elif exp.Statement.Values[0].Key == Fw.Statements.SADDR.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_SOURCE_IP+1) self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) st_name = exp.Statement.Name self.statements[idx]['opts'].setCurrentIndex( Fw.Family.values().index(st_name.lower())-1 # first item does not apply ) elif exp.Statement.Name == Fw.Statements.IIFNAME.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_IIFNAME+1) self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Key) elif exp.Statement.Name == Fw.Statements.OIFNAME.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_OIFNAME+1) self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Key) elif exp.Statement.Name == Fw.Statements.CT.value: self._load_ct_statement(exp, idx) elif exp.Statement.Name == Fw.Statements.META.value: self._load_meta_statement(exp, idx) elif exp.Statement.Name == Fw.Statements.ICMP.value or exp.Statement.Name == Fw.Statements.ICMPv6.value: if exp.Statement.Name == Fw.Statements.ICMP.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_ICMP+1) else: self.statements[idx]['what'].setCurrentIndex(self.STATM_ICMPv6+1) self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) for v in exp.Statement.Values: curText = self.statements[idx]['value'].currentText() if v.Value not in curText: self.statements[idx]['value'].setCurrentText( "{0},{1}".format( curText, v.Value ) ) elif exp.Statement.Name == Fw.Statements.LOG.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_LOG+1) for v in exp.Statement.Values: if v.Key == Fw.ExprLog.PREFIX.value: self.statements[idx]['value'].setCurrentText(v.Value) elif v.Key == Fw.ExprLog.LEVEL.value: try: lvl = Fw.ExprLogLevels.values().index(v.Value) except: lvl = Fw.ExprLogLevels.values().index(Fw.ExprLogLevels.WARN.value) self.statements[idx]['opts'].setCurrentIndex(lvl) elif exp.Statement.Name == Fw.Statements.QUOTA.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_QUOTA+1) self.statements[idx]['opts'].setCurrentIndex(1) for v in exp.Statement.Values: if v.Key == Fw.ExprQuota.OVER.value: self.statements[idx]['opts'].setCurrentIndex(0) else: self.statements[idx]['value'].setCurrentText( "{0}/{1}".format(v.Value, v.Key) ) elif exp.Statement.Name == Fw.Statements.LIMIT.value: self._load_limit_statement(exp, idx) elif exp.Statement.Name == Fw.Statements.COUNTER.value: self.statements[idx]['what'].setCurrentIndex(self.STATM_COUNTER+1) for v in exp.Statement.Values: if v.Key == Fw.ExprCounter.NAME.value: self.statements[idx]['value'].setCurrentText(v.Value) else: isNotSupported = True break # a statement may not have an operator. It's assumed that it's the # equal operator. op = Fw.Operator.EQUAL.value if exp.Statement.Op == "" else exp.Statement.Op self.statements[idx]['op'].setCurrentIndex( Fw.Operator.values().index(op) ) idx+=1 if isNotSupported: self._set_status_error(QC.translate("firewall", "This rule is not supported yet.")) self._disable_controls() return if rule.Hook.lower() == Fw.Hooks.INPUT.value: self.comboDirection.setCurrentIndex(self.IN) elif rule.Hook.lower() == Fw.Hooks.OUTPUT.value: self.comboDirection.setCurrentIndex(self.OUT) elif rule.Hook.lower() == Fw.Hooks.FORWARD.value: self.comboDirection.setCurrentIndex(self.FORWARD) elif rule.Hook.lower() == Fw.Hooks.PREROUTING.value: self.comboDirection.setCurrentIndex(self.PREROUTING) elif rule.Hook.lower() == Fw.Hooks.POSTROUTING.value: self.comboDirection.setCurrentIndex(self.POSTROUTING) # TODO: changing the direction of an existed rule needs work, it causes # some nasty effects. Disabled for now. self.comboDirection.setEnabled(False) try: self.comboVerdict.setCurrentIndex( Fw.Verdicts.values().index( rule.Rules[0].Target.lower() )-1 ) if self._has_verdict_parms(self.comboVerdict.currentIndex()): tparms = rule.Rules[0].TargetParameters.lower() parts = tparms.split(" ") self.lineVerdictParms.setText(parts[1]) if parts[1] == "": print("Firewall Rule: verdict parms error:", parts) except: self._set_status_error(QC.translate("firewall", "Rule target ({0}) not supported yet".format(rule.Rules[0].Target.lower()))) self._disable_controls() self._enable_save(False) def new(self): if not self.show(): return self._reset_widgets("", self.toolBoxSimple) self.FORM_TYPE = self.FORM_TYPE_SIMPLE self.setWindowTitle(QC.translate("firewall", "Firewall rule")) self.cmdDelete.setVisible(False) self.cmdSave.setVisible(False) self.cmdAdd.setVisible(True) self.checkEnable.setVisible(True) self.checkEnable.setEnabled(True) self.checkEnable.setChecked(True) self.frameDirection.setVisible(True) self.cmdSave.setVisible(False) self.cmdDelete.setVisible(False) self.cmdAdd.setVisible(True) self.hboxAdvanced.setVisible(True) self.tabWidget.setTabText(0, "") self.tabWidget.setCurrentIndex(0) self.add_new_statement("", self.toolBoxSimple) def exclude_service(self, direction): if not self.show(): return self._reset_widgets("", self.toolBoxSimple) self.setWindowTitle(QC.translate("firewall", "Exclude service")) self.cmdDelete.setVisible(False) self.cmdSave.setVisible(False) self.cmdReset.setVisible(False) self.cmdAdd.setVisible(True) self.checkEnable.setVisible(False) self.checkEnable.setEnabled(True) self.tabWidget.setTabText(0, "") self.hboxAdvanced.setVisible(False) dirPort = self.STATM_DPORT+1 self.FORM_TYPE = self.FORM_TYPE_ALLOW_IN_SERVICE self.lblExcludeTip.setText(QC.translate("firewall", "Allow inbound connections to the selected port.")) if direction == self.OUT: self.lblExcludeTip.setText(QC.translate("firewall", "Allow outbound connections to the selected port.")) self.FORM_TYPE = self.FORM_TYPE_EXCLUDE_SERVICE dirPort = self.STATM_DPORT+1 self.add_new_statement("", self.toolBoxSimple) self.statements[0]['what'].setCurrentIndex(dirPort) self.statements[0]['what'].setVisible(False) self.statements[0]['op'].setVisible(False) self.statements[0]['value'].setCurrentText("") self.frameDirection.setVisible(False) self.lblExcludeTip.setVisible(True) self.checkEnable.setChecked(True) def form_to_protobuf(self): """Transform form widgets to protobuf struct """ chain = Fw.ChainFilter.input() # XXX: tproxy does not work with destnat+output if self.comboDirection.currentIndex() == self.OUT and \ (self.comboVerdict.currentIndex()+1 == Fw.Verdicts.values().index(Config.ACTION_TPROXY) or \ self.comboVerdict.currentIndex()+1 == Fw.Verdicts.values().index(Config.ACTION_DNAT) or \ self.comboVerdict.currentIndex()+1 == Fw.Verdicts.values().index(Config.ACTION_REDIRECT) ): chain = Fw.ChainDstNAT.output() elif self.comboDirection.currentIndex() == self.FORWARD: chain = Fw.ChainMangle.forward() elif self.comboDirection.currentIndex() == self.PREROUTING: chain = Fw.ChainDstNAT.prerouting() elif self.comboDirection.currentIndex() == self.POSTROUTING: chain = Fw.ChainDstNAT.postrouting() elif self.comboDirection.currentIndex() == self.OUT or self.FORM_TYPE == self.FORM_TYPE_EXCLUDE_SERVICE: chain = Fw.ChainMangle.output() elif self.comboDirection.currentIndex() == self.IN or self.FORM_TYPE == self.FORM_TYPE_ALLOW_IN_SERVICE: chain = Fw.ChainFilter.input() verdict_idx = self.comboVerdict.currentIndex() verdict = Fw.Verdicts.values()[verdict_idx+1] # index 0 is "" _target_parms = "" if self._has_verdict_parms(verdict_idx): if self.lineVerdictParms.text() == "": return None, None, None, QC.translate("firewall", "Verdict ({0}) parameters cannot be empty.".format(verdict)) # these verdicts parameters need ":" to specify a port or ip:port if (self.comboVerdict.currentText().lower() == Config.ACTION_REDIRECT or \ self.comboVerdict.currentText().lower() == Config.ACTION_TPROXY or \ self.comboVerdict.currentText().lower() == Config.ACTION_SNAT or \ self.comboVerdict.currentText().lower() == Config.ACTION_DNAT) and \ ":" not in self.lineVerdictParms.text(): return None, None, None, QC.translate("firewall", "Verdict ({0}) parameters format is: <IP>:port.".format(verdict)) if self.comboVerdict.currentText().lower() == Config.ACTION_QUEUE: try: t = int(self.lineVerdictParms.text()) except: return None, None, None, QC.translate("firewall", "Verdict ({0}) parameters format must be a number".format(verdict)) vidx = self.comboVerdictParms.currentIndex() _target_parms = "{0} {1}".format( self.comboVerdictParms.itemData(vidx), self.lineVerdictParms.text().replace(" ", "") ) rule = Fw.Rules.new( enabled=self.checkEnable.isChecked(), _uuid=self.uuid, description=self.lineDescription.text(), target=verdict, target_parms=_target_parms ) for k in self.statements: st_idx = self.statements[k]['what'].currentIndex()-1 if st_idx == -1: return None, None, None, QC.translate("firewall", "select a statement.") statement = self.STATM_CONF[st_idx]['name'] statem_keys = self.STATM_CONF[st_idx]['keys'] statem_op = Fw.Operator.values()[self.statements[k]['op'].currentIndex()] statem_opts = self.statements[k]['opts'].currentText().lower() key_values = [] for sk in statem_keys: if sk['values'] == None: key_values.append((sk['key'], "")) else: statem_value = self.statements[k]['value'].currentText() val_idx = self.statements[k]['value'].currentIndex() if statem_value == "" or (statem_value == "0" and st_idx != self.STATM_META): return None, None, None, QC.translate("firewall", "value cannot be 0 or empty.") if st_idx == self.STATM_QUOTA: if sk['key'] == Fw.ExprQuota.OVER.value: if self.statements[k]['opts'].currentIndex() == 0: key_values.append((sk['key'], "")) continue elif sk['key'] == Fw.ExprQuota.UNIT.value or sk['key'] in Fw.RateUnits.values(): units = statem_value.split("/") if len(units) != 2: # we expect the format key/value return None, None, None, QC.translate("firewall", "the value format is 1024/kbytes (or bytes, mbytes, gbytes)") if units[1] not in Fw.RateUnits.values(): return None, None, None, QC.translate("firewall", "the value format is 1024/kbytes (or bytes, mbytes, gbytes)") sk['key'] = units[1] statem_value = units[0] if not self._is_valid_int_value(statem_value): raise ValueError("quota value is invalid ({0}). It must be value/unit (1/kbytes)".format(statem_value)) elif st_idx == self.STATM_LIMIT: if sk['key'] == Fw.ExprLimit.OVER.value: if self.statements[k]['opts'].currentIndex() == 0: key_values.append((sk['key'], "")) elif sk['key'] == Fw.ExprLimit.UNITS.value: units = statem_value.split("/") if len(units) != 3: # we expect the format key/value return None, None, None, QC.translate("firewall", "the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)") if units[1] not in Fw.RateUnits.values(): return None, None, None, QC.translate("firewall", "rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.") if units[2] not in Fw.TimeUnits.values(): return None, None, None, QC.translate("firewall", "time-limit not valid, use: second, minute, hour or day") key_values.append((Fw.ExprLimit.UNITS.value, units[0])) key_values.append((Fw.ExprLimit.RATE_UNITS.value, units[1])) key_values.append((Fw.ExprLimit.TIME_UNITS.value, units[2])) continue elif st_idx == self.STATM_LOG: key_values.append((Fw.ExprLog.LEVEL.value, statem_opts)) elif st_idx == self.STATM_META: sk['key'] = self.statements[k]['opts'].currentText() elif st_idx == self.STATM_IIFNAME or st_idx == self.STATM_OIFNAME: # for these statements, the values is set in the Key # field instead of Value. Value must be empty sk['key'] = statem_value statem_value = "" elif st_idx == self.STATM_DEST_IP or st_idx == self.STATM_SOURCE_IP: statement = statem_opts # convert network u.x.y.z/nn to 1.2.3.4-1.255.255.255 # format. # FIXME: This should be supported by the daemon, # instead of converting it here. # TODO: validate IP ranges. if "/" in statem_value: try: net = ipaddress.ip_network(statem_value) hosts = list(net) statem_value = "{0}-{1}".format(str(hosts[0]), str(hosts[-1])) except Exception as e: return None, None, None, QC.translate("firewall", "IP network format error, {0}".format(e)) elif not "-" in statem_value: try: ipaddress.ip_address(statem_value) except Exception as e: return None, None, None, QC.translate("firewall", "{0}".format(e)) elif st_idx == self.STATM_DPORT or st_idx == self.STATM_SPORT: # if it's a tcp+udp port, we need to add a meta+l4proto # statement, with the protos + ports as values. optsIdx = self.statements[k]['opts'].currentIndex() isMultiProto = optsIdx == 0 if isMultiProto: meta = self.STATM_CONF[self.STATM_META]['keys'][1] statement = self.STATM_CONF[self.STATM_META]['name'] # key: l4proto key_values.append((meta['key'], statem_opts)) else: statement = statem_opts # 1. if the value is one of the /etc/services return # the port # 2. if the value contains , or - just use the written # value, to allow multiple ports and ranges. # 3. otherwise validate that the entered value is an # int try: if "," in statem_value or "-" in statem_value: raise ValueError("port entered is multiport or a port range") service_idx = self.net_srv.service_by_name(statem_value) statem_value = self.net_srv.port_by_index(service_idx) except: if "," not in statem_value and "-" not in statem_value: if not self._is_valid_int_value(statem_value): return None, None, None, QC.translate("firewall", "port not valid.") elif st_idx == self.STATM_CT_SET or st_idx == self.STATM_CT_MARK or st_idx == self.STATM_META_SET_MARK: if not self._is_valid_int_value(statem_value): return None, None, None, QC.translate("firewall", "Invalid value {0}, number expected.".format(statem_value)) elif st_idx == self.STATM_ICMP or st_idx == self.STATM_ICMPv6: values = statem_value.split(",") for val in values: if val not in Fw.ExprICMP.values(): return None, None, None, QC.translate("firewall", "Invalid ICMP type \"{0}\".".format(val)) keyVal = (sk['key'], statem_value.replace(" ", "")) if keyVal not in key_values: key_values.append(keyVal) else: print("[REVIEW] statement values duplicated (there shouldn't be):", keyVal) exprs = Fw.Expr.new( statem_op, statement, key_values, ) rule.Expressions.extend([exprs]) chain.Rules.extend([rule]) node_addr = self.comboNodes.currentText() node = self._nodes.get_node(node_addr) return node_addr, node, chain, None def _is_valid_int_value(self, value): try: int(value) except: return False return True def send_notification(self, node_addr, fw_config, op, uuid): nid, notif = self._nodes.reload_fw(node_addr, fw_config, self._notification_callback) self._notifications_sent[nid] = {'addr': node_addr, 'operation': op, 'notif': notif, 'uuid': uuid} def send_notifications(self, fw_config, op): for addr in self._nodes.get_nodes(): nid, notif = self._nodes.reload_fw(addr, fw_config, self._notification_callback) self._notifications_sent[nid] = {'addr': addr, 'operation': op, 'notif': notif} def _set_status_error(self, msg): self.statusLabel.show() self.statusLabel.setStyleSheet('color: red') self.statusLabel.setText(msg) def _set_status_successful(self, msg): self.statusLabel.show() self.statusLabel.setStyleSheet('color: green') self.statusLabel.setText(msg) def _set_status_message(self, msg): self.statusLabel.show() self.statusLabel.setStyleSheet('color: darkorange') self.statusLabel.setText(msg) def _reset_status_message(self): self.statusLabel.setText("") self.statusLabel.hide() def _reset_fields(self): self.FORM_TYPE = self.FORM_TYPE_SIMPLE self.setWindowTitle(QC.translate("firewall", "Firewall rule")) self.cmdDelete.setVisible(False) self.cmdSave.setVisible(False) self.cmdAdd.setVisible(True) self.checkEnable.setVisible(True) self.checkEnable.setEnabled(True) self.checkEnable.setChecked(True) self.frameDirection.setVisible(True) self.lblExcludeTip.setVisible(False) self.lblExcludeTip.setText("") self._reset_status_message() self._enable_buttons() self.tabWidget.setDisabled(False) self.lineDescription.setText("") self.comboDirection.setCurrentIndex(self.IN) self.comboDirection.setEnabled(True) self.comboVerdict.blockSignals(True); self.comboVerdict.setCurrentIndex(0) self.comboVerdict.blockSignals(False); self.lineVerdictParms.setVisible(False) self.comboVerdictParms.setVisible(False) self.lineVerdictParms.setText("") self.uuid = "" def _enable_save(self, enable=True): """Enable Save buton whenever some detail of a route changes. The button may or not be hidden. If we're editing a rule it'll be shown but disabled/enabled. """ self.cmdSave.setEnabled(enable) def _enable_buttons(self, enable=True): """Disable add/save buttons until a response is received from the daemon. """ self.cmdSave.setEnabled(enable) self.cmdAdd.setEnabled(enable) self.cmdDelete.setEnabled(enable) def _disable_buttons(self, disabled=True): self.cmdSave.setDisabled(disabled) self.cmdAdd.setDisabled(disabled) self.cmdDelete.setDisabled(disabled) def _disable_controls(self): self._disable_buttons() self.tabWidget.setDisabled(True) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/preferences.py�����������������������������������������������0000664�0000000�0000000�00000137334�15003540030�0022766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys import time import os import json import stat from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtCore import QCoreApplication as QC from opensnitch.config import Config from opensnitch.nodes import Nodes from opensnitch.database import Database from opensnitch.utils import Message, QuickHelp, Themes, Icons, languages from opensnitch.utils.xdg import Autostart from opensnitch.notifications import DesktopNotifications from opensnitch import auth import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() DIALOG_UI_PATH = "%s/../res/preferences.ui" % os.path.dirname(sys.modules[__name__].__file__) class PreferencesDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): LOG_TAG = "[Preferences] " _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) saved = QtCore.pyqtSignal() TAB_POPUPS = 0 TAB_UI = 1 TAB_RULES = 2 TAB_NODES = 3 TAB_DB = 4 NODE_PAGE_GENERAL = 0 NODE_PAGE_LOGGING = 1 NODE_PAGE_AUTH = 2 SUM = 1 REST = 0 AUTH_SIMPLE = 0 AUTH_TLS_SIMPLE = 1 AUTH_TLS_MUTUAL = 2 NODE_AUTH = { AUTH_SIMPLE: auth.Simple, AUTH_TLS_SIMPLE: auth.TLSSimple, AUTH_TLS_MUTUAL: auth.TLSMutual } NODE_AUTH_VERIFY = { 0: auth.NO_CLIENT_CERT, 1: auth.REQ_CERT, 2: auth.REQ_ANY_CERT, 3: auth.VERIFY_CERT, 4: auth.REQ_AND_VERIFY_CERT } def __init__(self, parent=None, appicon=None): QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) self._themes = Themes.instance() self._saved_theme = "" self._restart_msg = QC.translate("preferences", "Restart the GUI in order changes to take effect") self._changes_needs_restart = None self._cfg = Config.get() self._nodes = Nodes.instance() self._db = Database.instance() self._autostart = Autostart() self._notification_callback.connect(self._cb_notification_callback) self._notifications_sent = {} self._desktop_notifications = DesktopNotifications() self.setupUi(self) self.setWindowIcon(appicon) self.checkDBMaxDays.setEnabled(True) self.dbFileButton.setVisible(False) self.dbLabel.setVisible(False) self.dbType = None intValidator = QtGui.QDoubleValidator(0, 20, 2, self) self.lineUIScreenFactor.setValidator(intValidator) self.acceptButton.clicked.connect(self._cb_accept_button_clicked) self.applyButton.clicked.connect(self._cb_apply_button_clicked) self.cancelButton.clicked.connect(self._cb_cancel_button_clicked) self.helpButton.clicked.connect(self._cb_help_button_clicked) self.popupsCheck.clicked.connect(self._cb_popups_check_toggled) self.dbFileButton.clicked.connect(self._cb_file_db_clicked) self.cmdTimeoutUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUITimeout, self.SUM)) self.cmdTimeoutDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUITimeout, self.REST)) self.cmdRefreshUIUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIRefresh, self.SUM)) self.cmdRefreshUIDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIRefresh, self.REST)) self.cmdUIDensityUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIDensity, self.SUM)) self.cmdUIDensityDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIDensity, self.REST)) self.cmdDBMaxDaysUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBMaxDays, self.SUM)) self.cmdDBMaxDaysDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBMaxDays, self.REST)) self.cmdDBPurgesUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.SUM)) self.cmdDBPurgesDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.REST)) self.cmdTestNotifs.clicked.connect(self._cb_test_notifs_clicked) self.radioSysNotifs.clicked.connect(self._cb_radio_system_notifications) self.helpButton.setToolTipDuration(30 * 1000) self.comboAuthType.currentIndexChanged.connect(self._cb_combo_auth_type_changed) self.comboAuthType.setItemData(PreferencesDialog.AUTH_SIMPLE, auth.Simple) self.comboAuthType.setItemData(PreferencesDialog.AUTH_TLS_SIMPLE, auth.TLSSimple) self.comboAuthType.setItemData(PreferencesDialog.AUTH_TLS_MUTUAL, auth.TLSMutual) self.comboNodeAuthType.setItemData(PreferencesDialog.AUTH_SIMPLE, auth.Simple) self.comboNodeAuthType.setItemData(PreferencesDialog.AUTH_TLS_SIMPLE, auth.TLSSimple) self.comboNodeAuthType.setItemData(PreferencesDialog.AUTH_TLS_MUTUAL, auth.TLSMutual) self.comboNodeAuthVerifyType.setItemData(0, auth.NO_CLIENT_CERT) self.comboNodeAuthVerifyType.setItemData(1, auth.REQ_CERT) self.comboNodeAuthVerifyType.setItemData(2, auth.REQ_ANY_CERT) self.comboNodeAuthVerifyType.setItemData(3, auth.VERIFY_CERT) self.comboNodeAuthVerifyType.setItemData(4, auth.REQ_AND_VERIFY_CERT) self.comboUIRules.currentIndexChanged.connect(self._cb_combo_uirules_changed) if QtGui.QIcon.hasThemeIcon("emblem-default"): return saveIcon = Icons.new(self, "document-save") applyIcon = Icons.new(self, "emblem-default") delIcon = Icons.new(self, "edit-delete") closeIcon = Icons.new(self, "window-close") openIcon = Icons.new(self, "document-open") helpIcon = Icons.new(self, "help-browser") addIcon = Icons.new(self, "list-add") delIcon = Icons.new(self, "list-remove") allowIcon = Icons.new(self, "emblem-default") denyIcon = Icons.new(self, "emblem-important") rejectIcon = Icons.new(self, "window-close") self.applyButton.setIcon(applyIcon) self.cancelButton.setIcon(closeIcon) self.acceptButton.setIcon(saveIcon) self.helpButton.setIcon(helpIcon) self.dbFileButton.setIcon(openIcon) self.cmdTimeoutUp.setIcon(addIcon) self.cmdTimeoutDown.setIcon(delIcon) self.cmdRefreshUIUp.setIcon(addIcon) self.cmdRefreshUIDown.setIcon(delIcon) self.cmdUIDensityUp.setIcon(addIcon) self.cmdUIDensityDown.setIcon(delIcon) self.cmdDBMaxDaysUp.setIcon(addIcon) self.cmdDBMaxDaysDown.setIcon(delIcon) self.cmdDBPurgesUp.setIcon(addIcon) self.cmdDBPurgesDown.setIcon(delIcon) self.comboUIAction.setItemIcon(Config.ACTION_DENY_IDX, denyIcon) self.comboUIAction.setItemIcon(Config.ACTION_ALLOW_IDX, allowIcon) self.comboUIAction.setItemIcon(Config.ACTION_REJECT_IDX, rejectIcon) def showEvent(self, event): super(PreferencesDialog, self).showEvent(event) try: self._changes_needs_restart = None self._settingsSaved = False self._reset_status_message() self._hide_status_label() self.comboNodes.clear() self._load_langs() self.comboNodeAddress.clear() run_path = "/run/user/{0}/opensnitch/".format(os.getuid()) var_run_path = "/var{0}".format(run_path) self.comboNodeAddress.addItem("unix:///tmp/osui.sock") if os.path.exists(run_path): self.comboNodeAddress.addItem("unix://%s/osui.sock" % run_path) if os.path.exists(var_run_path): self.comboNodeAddress.addItem("unix://%s/osui.sock" % var_run_path) self._node_list = self._nodes.get() for addr in self._node_list: self.comboNodes.addItem(addr) if len(self._node_list) == 0: self._reset_node_settings() self._set_status_message(QC.translate("preferences", "There're no nodes connected")) except Exception as e: print(self.LOG_TAG + "exception loading nodes:", e) self._load_settings() # connect the signals after loading settings, to avoid firing # the signals self.comboNodes.currentIndexChanged.connect(self._cb_node_combo_changed) self.comboNodeAction.currentIndexChanged.connect(self._cb_node_needs_update) self.comboNodeDuration.currentIndexChanged.connect(self._cb_node_needs_update) self.comboNodeMonitorMethod.currentIndexChanged.connect(self._cb_node_needs_update) self.comboNodeLogLevel.currentIndexChanged.connect(self._cb_node_needs_update) self.comboNodeLogFile.currentIndexChanged.connect(self._cb_node_needs_update) self.checkNodeLogUTC.clicked.connect(self._cb_node_needs_update) self.checkNodeLogMicro.clicked.connect(self._cb_node_needs_update) self.comboNodeAddress.currentTextChanged.connect(self._cb_node_needs_update) self.checkInterceptUnknown.clicked.connect(self._cb_node_needs_update) self.checkApplyToNodes.clicked.connect(self._cb_node_needs_update) self.comboNodeAction.currentIndexChanged.connect(self._cb_node_needs_update) self.checkNodeAuthSkipVerify.clicked.connect(self._cb_node_needs_update) self.comboNodeAuthVerifyType.currentIndexChanged.connect(self._cb_node_needs_update) self.comboAuthType.currentIndexChanged.connect(self._cb_combo_auth_type_changed) self.comboNodeAuthType.currentIndexChanged.connect(self._cb_combo_node_auth_type_changed) self.lineCACertFile.textChanged.connect(self._cb_line_certs_changed) self.lineCertFile.textChanged.connect(self._cb_line_certs_changed) self.lineCertKeyFile.textChanged.connect(self._cb_line_certs_changed) self.lineNodeCACertFile.textChanged.connect(self._cb_node_line_certs_changed) self.lineNodeCertFile.textChanged.connect(self._cb_node_line_certs_changed) self.lineNodeCertKeyFile.textChanged.connect(self._cb_node_line_certs_changed) self.lineUIScreenFactor.textChanged.connect(self._cb_ui_screen_factor_changed) self.checkUIRules.toggled.connect(self._cb_ui_check_rules_toggled) self.checkUIAutoScreen.toggled.connect(self._cb_ui_check_auto_scale_toggled) self.comboUITheme.currentIndexChanged.connect(self._cb_combo_themes_changed) self.spinUIDensity.valueChanged.connect(self._cb_spin_uidensity_changed) self.comboDBType.currentIndexChanged.connect(self._cb_db_type_changed) self.checkDBMaxDays.toggled.connect(self._cb_db_max_days_toggled) self.checkDBJrnlWal.toggled.connect(self._cb_db_jrnl_wal_toggled) # True when any node option changes self._node_needs_update = False def show_node_prefs(self, addr): self.show() self.comboNodes.setCurrentText(addr) self.tabWidget.setCurrentIndex(self.TAB_NODES) def _load_langs(self): try: self.comboUILang.clear() self.comboUILang.blockSignals(True) self.comboUILang.addItem(QC.translate("preferences", "System default"), "") langs, langNames = languages.get_all() for idx, lang in enumerate(langs): self.comboUILang.addItem(langNames[idx].capitalize(), langs[idx]) self.comboUILang.blockSignals(False) except Exception as e: print(self.LOG_TAG + "exception loading languages:", e) def _load_themes(self): self.comboUITheme.blockSignals(True) theme_idx, self._saved_theme, theme_density = self._themes.get_saved_theme() self.labelThemeError.setVisible(False) self.labelThemeError.setText("") self.comboUITheme.clear() self.comboUITheme.addItem(QC.translate("preferences", "System")) if self._themes.available(): themes = self._themes.list_themes() self.comboUITheme.addItems(themes) else: self._saved_theme = "" self.labelThemeError.setStyleSheet('color: red') self.labelThemeError.setVisible(True) self.labelThemeError.setText(QC.translate("preferences", "Themes not available. Install qt-material: pip3 install qt-material")) self.comboUITheme.setCurrentIndex(theme_idx) self._show_ui_density_widgets(theme_idx) try: self.spinUIDensity.setValue(int(theme_density)) except Exception as e: print("load_theme() invalid theme density scale:", theme_density, ":", e) self.comboUITheme.blockSignals(False) def _load_settings(self): self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) self._default_target = self._cfg.getInt(self._cfg.DEFAULT_TARGET_KEY, 0) self._default_timeout = self._cfg.getInt(self._cfg.DEFAULT_TIMEOUT_KEY, Config.DEFAULT_TIMEOUT) self._disable_popups = self._cfg.getBool(self._cfg.DEFAULT_DISABLE_POPUPS) if self._cfg.hasKey(self._cfg.DEFAULT_DURATION_KEY): self._default_duration = self._cfg.getInt(self._cfg.DEFAULT_DURATION_KEY) else: self._default_duration = self._cfg.DEFAULT_DURATION_IDX self.comboUIDuration.setCurrentIndex(self._default_duration) self.comboUIDialogPos.setCurrentIndex(self._cfg.getInt(self._cfg.DEFAULT_POPUP_POSITION)) self.comboUIAction.setCurrentIndex(self._default_action) self.comboUITarget.setCurrentIndex(self._default_target) self.spinUITimeout.setValue(self._default_timeout) self.spinUITimeout.setEnabled(not self._disable_popups) self.popupsCheck.setChecked(self._disable_popups) self.showAdvancedCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED)) self.dstIPCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP)) self.dstPortCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT)) self.uidCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_UID)) maxmsgsize = self._cfg.getSettings(Config.DEFAULT_SERVER_MAX_MESSAGE_LENGTH) if maxmsgsize: self.comboGrpcMsgSize.setCurrentText(maxmsgsize) else: self.comboGrpcMsgSize.setCurrentIndex(0) self.lineCACertFile.setText(self._cfg.getSettings(Config.AUTH_CA_CERT)) self.lineCertFile.setText(self._cfg.getSettings(Config.AUTH_CERT)) self.lineCertKeyFile.setText(self._cfg.getSettings(Config.AUTH_CERTKEY)) authtype_idx = self.comboAuthType.findData(self._cfg.getSettings(Config.AUTH_TYPE)) if authtype_idx <= 0: authtype_idx = 0 self.lineCACertFile.setEnabled(False) self.lineCertFile.setEnabled(False) self.lineCertKeyFile.setEnabled(False) self.comboAuthType.setCurrentIndex(authtype_idx) self.comboUIRules.blockSignals(True) self.comboUIRules.setCurrentIndex(self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES)) self.checkUIRules.setChecked(self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES)) self.comboUIRules.setEnabled(self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES)) #self._set_rules_duration_filter() self._cfg.setRulesDurationFilter( self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES), self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) ) self.comboUIRules.blockSignals(False) # by default, if no configuration exists, enable notifications. self.groupNotifs.setChecked(self._cfg.getBool(Config.NOTIFICATIONS_ENABLED, True)) self.radioSysNotifs.setChecked( True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_SYSTEM and self._desktop_notifications.is_available() == True else False ) self.radioQtNotifs.setChecked( True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_QT or self._desktop_notifications.is_available() == False else False ) ## db self.dbType = self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY) self.comboDBType.setCurrentIndex(self.dbType) if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY: self.dbFileButton.setVisible(True) self.dbLabel.setVisible(True) self.dbLabel.setText(self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY)) dbMaxDays = self._cfg.getInt(self._cfg.DEFAULT_DB_MAX_DAYS, 1) dbJrnlWal = self._cfg.getBool(self._cfg.DEFAULT_DB_JRNL_WAL) dbPurgeInterval = self._cfg.getInt(self._cfg.DEFAULT_DB_PURGE_INTERVAL, 5) self._enable_db_cleaner_options(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST), dbMaxDays) self._enable_db_jrnl_wal(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST), dbJrnlWal) self.spinDBMaxDays.setValue(dbMaxDays) self.spinDBPurgeInterval.setValue(dbPurgeInterval) self._load_themes() self._load_node_settings() self._load_ui_settings() def _load_ui_settings(self): self._ui_refresh_interval = self._cfg.getInt(self._cfg.STATS_REFRESH_INTERVAL, 0) self.spinUIRefresh.setValue(self._ui_refresh_interval) saved_lang = self._cfg.getSettings(Config.DEFAULT_LANGUAGE) if saved_lang: saved_langname = self._cfg.getSettings(Config.DEFAULT_LANGNAME) self.comboUILang.blockSignals(True) self.comboUILang.setCurrentText(saved_langname) self.comboUILang.blockSignals(False) auto_scale = self._cfg.getBool(Config.QT_AUTO_SCREEN_SCALE_FACTOR, default_value=True) screen_factor = self._cfg.getSettings(Config.QT_SCREEN_SCALE_FACTOR) if screen_factor is None or screen_factor == "": screen_factor = "1" self.lineUIScreenFactor.setText(screen_factor) self.checkUIAutoScreen.blockSignals(True) self.checkUIAutoScreen.setChecked(auto_scale) self.checkUIAutoScreen.blockSignals(False) self._show_ui_scalefactor_widgets(auto_scale) qt_platform = self._cfg.getSettings(Config.QT_PLATFORM_PLUGIN) if qt_platform is not None and qt_platform != "": self.comboUIQtPlatform.setCurrentText(qt_platform) self.checkAutostart.setChecked(self._autostart.isEnabled()) self._load_ui_columns_config() def _load_node_settings(self): addr = self.comboNodes.currentText() if addr == "": return try: node_data = self._node_list[addr]['data'] self.labelNodeVersion.setText(node_data.version) self.labelNodeName.setText(node_data.name) self.comboNodeLogLevel.setCurrentIndex(node_data.logLevel) node_config = json.loads(node_data.config) self.comboNodeAction.setCurrentText(node_config['DefaultAction']) self.comboNodeDuration.setCurrentText(node_config['DefaultDuration']) self.comboNodeMonitorMethod.setCurrentText(node_config['ProcMonitorMethod']) self.checkInterceptUnknown.setChecked(node_config['InterceptUnknown']) self.comboNodeLogLevel.setCurrentIndex(int(node_config['LogLevel'])) if node_config.get('LogUTC') == None: node_config['LogUTC'] = False self.checkNodeLogUTC.setChecked(node_config['LogUTC']) if node_config.get('LogMicro') == None: node_config['LogMicro'] = False self.checkNodeLogMicro.setChecked(node_config['LogMicro']) if node_config.get('Server') != None: self.comboNodeAddress.setEnabled(True) self.comboNodeLogFile.setEnabled(True) self.comboNodeAddress.setCurrentText(node_config['Server']['Address']) self.comboNodeLogFile.setCurrentText(node_config['Server']['LogFile']) self._load_node_auth_settings(node_config['Server']) else: self.comboNodeAddress.setEnabled(False) self.comboNodeLogFile.setEnabled(False) except Exception as e: print(self.LOG_TAG + "exception loading config: ", e) def _load_node_config(self, addr): try: if self.comboNodeAddress.currentText() == "": return None, QC.translate("preferences", "Server address can not be empty") node_action = Config.ACTION_DENY if self.comboNodeAction.currentIndex() == 1: node_action = Config.ACTION_ALLOW elif self.comboNodeAction.currentIndex() == 2: node_action = Config.ACTION_REJECT node_duration = Config.DURATION_ONCE if self.comboNodeDuration.currentIndex() == 1: node_duration = Config.DURATION_UNTIL_RESTART elif self.comboNodeDuration.currentIndex() == 2: node_duration = Config.DURATION_ALWAYS node_config = json.loads(self._nodes.get_node_config(addr)) node_config['DefaultAction'] = node_action node_config['DefaultDuration'] = node_duration node_config['ProcMonitorMethod'] = self.comboNodeMonitorMethod.currentText() node_config['LogLevel'] = self.comboNodeLogLevel.currentIndex() node_config['LogUTC'] = self.checkNodeLogUTC.isChecked() node_config['LogMicro'] = self.checkNodeLogMicro.isChecked() node_config['InterceptUnknown'] = self.checkInterceptUnknown.isChecked() if node_config.get('Server') != None: # skip setting Server Address if we're applying the config to all nodes node_config['Server']['Address'] = self.comboNodeAddress.currentText() node_config['Server']['LogFile'] = self.comboNodeLogFile.currentText() cfg = self._save_node_auth_config(node_config['Server']) if cfg != None: node_config['Server'] = cfg else: print(addr, " doesn't have Server item") return json.dumps(node_config, indent=" "), None except Exception as e: print(self.LOG_TAG + "exception loading node config on %s: " % addr, e) return None, QC.translate("preferences", "Error loading {0} configuration").format(addr) def _load_node_auth_settings(self, config): try: if config == None: return auth = config.get('Authentication') authtype_idx = 0 if auth != None: if auth.get('Type') != None: authtype_idx = self.comboNodeAuthType.findData(auth['Type']) else: config['Authentication'] = {} auth = config.get('Authentication') self.lineNodeCACertFile.setEnabled(authtype_idx >= 0) self.lineNodeServerCertFile.setEnabled(authtype_idx >= 0) self.lineNodeCertFile.setEnabled(authtype_idx >= 0) self.lineNodeCertKeyFile.setEnabled(authtype_idx >= 0) tls = auth.get('TLSOptions') if tls != None and authtype_idx >= 0: if tls.get('CACert') != None: self.lineNodeCACertFile.setText(tls['CACert']) if tls.get('ServerCert') != None: self.lineNodeServerCertFile.setText(tls['ServerCert']) if tls.get('ClientCert') != None: self.lineNodeCertFile.setText(tls['ClientCert']) if tls.get('ClientKey') != None: self.lineNodeCertKeyFile.setText(tls['ClientKey']) if tls.get('SkipVerify') != None: self.checkNodeAuthSkipVerify.setChecked(tls['SkipVerify']) if tls.get('ClientAuthType') != None: clienttype_idx = self.comboNodeAuthVerifyType.findData(tls['ClientAuthType']) if clienttype_idx >= 0: self.comboNodeAuthVerifyType.setCurrentIndex(clienttype_idx) self.comboNodeAuthType.setCurrentIndex(authtype_idx) # signals are connected after this method is called self._cb_combo_node_auth_type_changed(authtype_idx) except Exception as e: print("[prefs] load node auth options exception:", e) self._set_status_error(str(e)) def _save_node_auth_config(self, config): try: auth = config.get('Authentication') if auth == None: auth = {} auth['Type'] = self.NODE_AUTH[self.comboNodeAuthType.currentIndex()] tls = auth.get('TLSOptions') if tls == None: tls = {} tls['CACert'] = self.lineNodeCACertFile.text() tls['ServerCert'] = self.lineNodeServerCertFile.text() tls['ClientCert'] = self.lineNodeCertFile.text() tls['ClientKey'] = self.lineNodeCertKeyFile.text() tls['SkipVerify'] = self.checkNodeAuthSkipVerify.isChecked() tls['ClientAuthType'] = self.NODE_AUTH_VERIFY[self.comboNodeAuthVerifyType.currentIndex()] auth['TLSOptions'] = tls config['Authentication'] = auth return config except Exception as e: print("[prefs] node auth options exception:", e) self._set_status_error(str(e)) return None def _load_ui_columns_config(self): cols = self._cfg.getSettings(Config.STATS_SHOW_COLUMNS) if cols == None: return for c in range(13): checked = str(c) in cols if c == 0: self.checkHideTime.setChecked(checked) elif c == 1: self.checkHideNode.setChecked(checked) elif c == 2: self.checkHideAction.setChecked(checked) elif c == 3: self.checkHideSrcPort.setChecked(checked) elif c == 4: self.checkHideSrcIP.setChecked(checked) elif c == 5: self.checkHideDstIP.setChecked(checked) elif c == 6: self.checkHideDstHost.setChecked(checked) elif c == 7: self.checkHideDstPort.setChecked(checked) elif c == 8: self.checkHideProto.setChecked(checked) elif c == 9: self.checkHideUID.setChecked(checked) elif c == 10: self.checkHidePID.setChecked(checked) elif c == 11: self.checkHideProc.setChecked(checked) elif c == 12: self.checkHideCmdline.setChecked(checked) elif c == 13: self.checkHideRule.setChecked(checked) def _reset_node_settings(self): self.comboNodeAction.setCurrentIndex(0) self.comboNodeDuration.setCurrentIndex(0) self.comboNodeMonitorMethod.setCurrentIndex(0) self.checkInterceptUnknown.setChecked(False) self.comboNodeLogLevel.setCurrentIndex(0) self.checkNodeLogUTC.setChecked(True) self.checkNodeLogMicro.setChecked(False) self.labelNodeName.setText("") self.labelNodeVersion.setText("") self.comboNodeAuthType.setCurrentIndex(self.AUTH_SIMPLE) self.lineNodeCACertFile.setText("") self.lineNodeServerCertFile.setText("") self.lineNodeCertFile.setText("") self.lineNodeCertKeyFile.setText("") self.checkNodeAuthSkipVerify.setChecked(False) self.comboNodeAuthVerifyType.setCurrentIndex(0) self._cb_combo_node_auth_type_changed(0) def _save_settings(self): self._reset_status_message() self._show_status_label() self._save_ui_config() if not self._save_db_config(): return self._save_nodes_config() self.saved.emit() self._settingsSaved = True self._needs_restart() def _save_db_config(self): dbtype = self.comboDBType.currentIndex() db_name = self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY) if self.dbLabel.text() != "" and \ (self.comboDBType.currentIndex() != self.dbType or db_name != self.dbLabel.text()): self._changes_needs_restart = QC.translate("preferences", "DB type changed") if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY: if self.dbLabel.text() != "": db_name = self.dbLabel.text() else: Message.ok( QC.translate("preferences", "Warning"), QC.translate("preferences", "You must select a file for the database<br>or choose \"In memory\" type."), QtWidgets.QMessageBox.Warning) self.dbLabel.setText("") return False else: db_name = Database.DB_IN_MEMORY self._cfg.setSettings(Config.DEFAULT_DB_FILE_KEY, db_name) self._cfg.setSettings(Config.DEFAULT_DB_TYPE_KEY, dbtype) self._cfg.setSettings(Config.DEFAULT_DB_PURGE_OLDEST, bool(self.checkDBMaxDays.isChecked())) self._cfg.setSettings(Config.DEFAULT_DB_MAX_DAYS, int(self.spinDBMaxDays.value())) self._cfg.setSettings(Config.DEFAULT_DB_PURGE_INTERVAL, int(self.spinDBPurgeInterval.value())) self._cfg.setSettings(Config.DEFAULT_DB_JRNL_WAL, bool(self.checkDBJrnlWal.isChecked())) self.dbType = self.comboDBType.currentIndex() return True def _save_ui_config(self): try: self._save_ui_columns_config() maxmsgsize = self.comboGrpcMsgSize.currentText() if maxmsgsize != "": self._cfg.setSettings(Config.DEFAULT_SERVER_MAX_MESSAGE_LENGTH, maxmsgsize.replace(" ", "")) savedauthtype = self._cfg.getSettings(Config.AUTH_TYPE) authtype = self.comboAuthType.itemData(self.comboAuthType.currentIndex()) cacert = self._cfg.getSettings(Config.AUTH_CA_CERT) cert = self._cfg.getSettings(Config.AUTH_CERT) certkey = self._cfg.getSettings(Config.AUTH_CERTKEY) if not self._validate_certs(): return if savedauthtype != authtype or self.lineCertFile.text() != cert or \ self.lineCertKeyFile.text() != certkey or self.lineCACertFile.text() != cacert: self._changes_needs_restart = QC.translate("preferences", "Certificates changed") self._cfg.setSettings(Config.AUTH_TYPE, authtype) self._cfg.setSettings(Config.AUTH_CA_CERT, self.lineCACertFile.text()) self._cfg.setSettings(Config.AUTH_CERT, self.lineCertFile.text()) self._cfg.setSettings(Config.AUTH_CERTKEY, self.lineCertKeyFile.text()) selected_lang = self.comboUILang.itemData(self.comboUILang.currentIndex()) saved_lang = self._cfg.getSettings(Config.DEFAULT_LANGUAGE) saved_lang = "" if saved_lang is None else saved_lang if saved_lang != selected_lang: languages.save(self._cfg, selected_lang) self._changes_needs_restart = QC.translate("preferences", "Language changed") self._cfg.setSettings(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES, int(self.comboUIRules.currentIndex())) self._cfg.setSettings(self._cfg.DEFAULT_IGNORE_RULES, bool(self.checkUIRules.isChecked())) #self._set_rules_duration_filter() self._cfg.setRulesDurationFilter( bool(self.checkUIRules.isChecked()), int(self.comboUIRules.currentIndex()) ) if self.checkUIRules.isChecked(): self._nodes.delete_rule_by_field(Config.DURATION_FIELD, Config.RULES_DURATION_FILTER) self._cfg.setSettings(self._cfg.STATS_REFRESH_INTERVAL, int(self.spinUIRefresh.value())) self._cfg.setSettings(self._cfg.DEFAULT_ACTION_KEY, self.comboUIAction.currentIndex()) self._cfg.setSettings(self._cfg.DEFAULT_DURATION_KEY, int(self.comboUIDuration.currentIndex())) self._cfg.setSettings(self._cfg.DEFAULT_TARGET_KEY, self.comboUITarget.currentIndex()) self._cfg.setSettings(self._cfg.DEFAULT_TIMEOUT_KEY, self.spinUITimeout.value()) self._cfg.setSettings(self._cfg.DEFAULT_DISABLE_POPUPS, bool(self.popupsCheck.isChecked())) self._cfg.setSettings(self._cfg.DEFAULT_POPUP_POSITION, int(self.comboUIDialogPos.currentIndex())) self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED, bool(self.showAdvancedCheck.isChecked())) self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP, bool(self.dstIPCheck.isChecked())) self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT, bool(self.dstPortCheck.isChecked())) self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_UID, bool(self.uidCheck.isChecked())) self._cfg.setSettings(self._cfg.NOTIFICATIONS_ENABLED, bool(self.groupNotifs.isChecked())) self._cfg.setSettings(self._cfg.NOTIFICATIONS_TYPE, int(Config.NOTIFICATION_TYPE_SYSTEM if self.radioSysNotifs.isChecked() else Config.NOTIFICATION_TYPE_QT)) self._themes.save_theme(self.comboUITheme.currentIndex(), self.comboUITheme.currentText(), str(self.spinUIDensity.value())) qt_platform = self._cfg.getSettings(Config.QT_PLATFORM_PLUGIN) if qt_platform != self.comboUIQtPlatform.currentText(): self._changes_needs_restart = QC.translate("preferences", "Qt platform plugin changed") self._cfg.setSettings(Config.QT_PLATFORM_PLUGIN, self.comboUIQtPlatform.currentText()) self._cfg.setSettings(Config.QT_AUTO_SCREEN_SCALE_FACTOR, bool(self.checkUIAutoScreen.isChecked())) self._cfg.setSettings(Config.QT_SCREEN_SCALE_FACTOR, self.lineUIScreenFactor.text()) if self._themes.available() and self._saved_theme != "" and self.comboUITheme.currentText() == QC.translate("preferences", "System"): self._changes_needs_restart = QC.translate("preferences", "UI theme changed") # this is a workaround for not display pop-ups. # see #79 for more information. if self.popupsCheck.isChecked(): self._cfg.setSettings(self._cfg.DEFAULT_TIMEOUT_KEY, 0) self._autostart.enable(self.checkAutostart.isChecked()) except Exception as e: self._set_status_error(str(e)) def _save_ui_columns_config(self): cols=list() if self.checkHideTime.isChecked(): cols.append("0") if self.checkHideNode.isChecked(): cols.append("1") if self.checkHideAction.isChecked(): cols.append("2") if self.checkHideSrcPort.isChecked(): cols.append("3") if self.checkHideSrcIP.isChecked(): cols.append("4") if self.checkHideDstIP.isChecked(): cols.append("5") if self.checkHideDstHost.isChecked(): cols.append("6") if self.checkHideDstPort.isChecked(): cols.append("7") if self.checkHideProto.isChecked(): cols.append("8") if self.checkHideUID.isChecked(): cols.append("9") if self.checkHidePID.isChecked(): cols.append("10") if self.checkHideProc.isChecked(): cols.append("11") if self.checkHideCmdline.isChecked(): cols.append("12") if self.checkHideRule.isChecked(): cols.append("13") self._cfg.setSettings(Config.STATS_SHOW_COLUMNS, cols) def _save_nodes_config(self): addr = self.comboNodes.currentText() if (self._node_needs_update or self.checkApplyToNodes.isChecked()) and addr != "": try: notif = ui_pb2.Notification( id=int(str(time.time()).replace(".", "")), type=ui_pb2.CHANGE_CONFIG, data="", rules=[]) if self.checkApplyToNodes.isChecked(): for addr in self._nodes.get_nodes(): error = self._save_node_config(notif, addr) if error != None: self._set_status_error(error) return else: error = self._save_node_config(notif, addr) if error != None: self._set_status_error(error) return except Exception as e: print(self.LOG_TAG + "exception saving config: ", e) self._set_status_error(QC.translate("preferences", "Exception saving config: {0}").format(str(e))) elif addr == "": self._set_status_message(QC.translate("preferences", "There're no nodes connected")) self._node_needs_update = False def _save_node_config(self, notifObject, addr): try: self._set_status_message(QC.translate("preferences", "Applying configuration on {0} ...").format(addr)) notifObject.data, error = self._load_node_config(addr) if error != None: return error savedAddr = self._cfg.getSettings(Config.DEFAULT_SERVER_ADDR) # exclude this message if there're more than one node connected if self.comboNodes.count() == 1 and savedAddr != None and savedAddr != self.comboNodeAddress.currentText(): self._changes_needs_restart = QC.translate("preferences", "Ok") self._cfg.setSettings(Config.DEFAULT_SERVER_ADDR, self.comboNodeAddress.currentText()) self._nodes.save_node_config(addr, notifObject.data) nid = self._nodes.send_notification(addr, notifObject, self._notification_callback) self._notifications_sent[nid] = notifObject except Exception as e: print(self.LOG_TAG + "exception saving node config on %s: " % addr, e) self._set_status_error(QC.translate("preferences", "Exception saving node config {0}: {1}").format((addr, str(e)))) return addr + ": " + str(e) return None def _validate_certs(self): try: if self.comboAuthType.currentIndex() == PreferencesDialog.AUTH_SIMPLE: return True if self.comboAuthType.currentIndex() > 0 and (self.lineCertFile.text() == "" or self.lineCertKeyFile.text() == ""): raise ValueError(QC.translate("preferences", "Certs fields cannot be empty.")) if oct(stat.S_IMODE(os.lstat(self.lineCertFile.text()).st_mode)) != "0o600": self._set_status_message( QC.translate("preferences", "cert file has excessive permissions, it should have 0600") ) if oct(stat.S_IMODE(os.lstat(self.lineCertFile.text()).st_mode)) != "0o600": self._set_status_message( QC.translate("preferences", "cert key file has excessive permissions, it should have 0600") ) if self.comboAuthType.currentIndex() == PreferencesDialog.AUTH_TLS_MUTUAL: if oct(stat.S_IMODE(os.lstat(self.lineCACertFile.text()).st_mode)) != "0o600": self._set_status_message( QC.translate("preferences", "CA cert file has excessive permissions, it should have 0600") ) return True except Exception as e: self._changes_needs_restart = None self._set_status_error("certs error: {0}".format(e)) return False def _needs_restart(self): if self._changes_needs_restart: Message.ok(self._changes_needs_restart, self._restart_msg, QtWidgets.QMessageBox.Warning) self._changes_needs_restart = None def _show_ui_density_widgets(self, idx): """show ui density widget only for qt-material themes: https://github.com/UN-GCPDS/qt-material?tab=readme-ov-file#density-scale """ hidden = idx == 0 self.labelUIDensity.setHidden(hidden) self.spinUIDensity.setHidden(hidden) self.cmdUIDensityUp.setHidden(hidden) self.cmdUIDensityDown.setHidden(hidden) def _show_ui_scalefactor_widgets(self, show=False): self.labelUIScreenFactor.setHidden(show) self.lineUIScreenFactor.setHidden(show) def _hide_status_label(self): self.statusLabel.hide() def _show_status_label(self): self.statusLabel.show() def _set_status_error(self, msg): self._show_status_label() self.statusLabel.setStyleSheet('color: red') self.statusLabel.setText(msg) def _set_status_successful(self, msg): self._show_status_label() self.statusLabel.setStyleSheet('color: green') self.statusLabel.setText(msg) def _set_status_message(self, msg): self._show_status_label() self.statusLabel.setStyleSheet('color: darkorange') self.statusLabel.setText(msg) def _reset_status_message(self): self.statusLabel.setText("") self._hide_status_label() def _enable_db_cleaner_options(self, enable, db_max_days): self.checkDBMaxDays.setChecked(enable) self.spinDBMaxDays.setEnabled(enable) self.spinDBPurgeInterval.setEnabled(enable) self.labelDBPurgeInterval.setEnabled(enable) self.labelDBPurgeDays.setEnabled(enable) self.labelDBPurgeMinutes.setEnabled(enable) self.cmdDBMaxDaysUp.setEnabled(enable) self.cmdDBMaxDaysDown.setEnabled(enable) self.cmdDBPurgesUp.setEnabled(enable) self.cmdDBPurgesDown.setEnabled(enable) def _enable_db_jrnl_wal(self, enable, db_jrnl_wal): self.checkDBJrnlWal.setChecked(db_jrnl_wal) self.checkDBJrnlWal.setEnabled(enable) def _change_theme(self): extra_opts = { 'density_scale': str(self.spinUIDensity.value()) } self._themes.change_theme(self, self.comboUITheme.currentText(), extra_opts) @QtCore.pyqtSlot(ui_pb2.NotificationReply) def _cb_notification_callback(self, reply): #print(self.LOG_TAG, "Config notification received: ", reply.id, reply.code) if reply.id in self._notifications_sent: if reply.code == ui_pb2.OK: self._set_status_successful(QC.translate("preferences", "Configuration applied.")) else: self._set_status_error(QC.translate("preferences", "Error applying configuration: {0}").format(reply.data)) del self._notifications_sent[reply.id] def _cb_line_certs_changed(self, text): self._changes_needs_restart = QC.translate("preferences", "Certs changed") def _cb_node_line_certs_changed(self, text): self._changes_needs_restart = QC.translate("preferences", "Node certs changed") self._node_needs_update = True def _cb_file_db_clicked(self): options = QtWidgets.QFileDialog.Options() fileName, _ = QtWidgets.QFileDialog.getSaveFileName(self, "", "","All Files (*)", options=options) if fileName: self.dbLabel.setText(fileName) def _cb_combo_uirules_changed(self, idx): self._cfg.setRulesDurationFilter( self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES), idx #self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) ) def _cb_db_type_changed(self): isDBMem = self.comboDBType.currentIndex() == Database.DB_TYPE_MEMORY self.dbFileButton.setVisible(not isDBMem) self.dbLabel.setVisible(not isDBMem) self.checkDBMaxDays.setChecked(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST)) self.checkDBJrnlWal.setEnabled(not isDBMem) self.checkDBJrnlWal.setChecked(False) def _cb_accept_button_clicked(self): self.accept() if not self._settingsSaved: self._save_settings() def _cb_apply_button_clicked(self): self._reset_status_message() self._save_settings() def _cb_cancel_button_clicked(self): self.reject() def _cb_help_button_clicked(self): QuickHelp.show( QC.translate("preferences", "Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href=\"{0}\">{0}</a>" ).format(Config.HELP_URL) ) def _cb_popups_check_toggled(self, checked): self.spinUITimeout.setEnabled(not checked) if not checked: self.spinUITimeout.setValue(20) def _cb_node_combo_changed(self, index): self._load_node_settings() def _cb_node_needs_update(self): self._node_needs_update = True def _cb_ui_check_rules_toggled(self, state): self.comboUIRules.setEnabled(state) def _cb_combo_themes_changed(self, index): self._change_theme() self._show_ui_density_widgets(index) def _cb_spin_uidensity_changed(self, value): self._change_theme() def _cb_ui_check_auto_scale_toggled(self, checked): self._changes_needs_restart = QC.translate("preferences", "Auto scale option changed") self._show_ui_scalefactor_widgets(checked) def _cb_ui_screen_factor_changed(self, text): self._changes_needs_restart = QC.translate("preferences", "Screen factor option changed") def _cb_combo_auth_type_changed(self, index): curtype = self.comboAuthType.itemData(self.comboAuthType.currentIndex()) savedtype = self._cfg.getSettings(Config.AUTH_TYPE) if curtype != savedtype: self._changes_needs_restart = QC.translate("preferences", "Auth type changed") self.lineCACertFile.setEnabled(index == PreferencesDialog.AUTH_TLS_MUTUAL) self.lineCertFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) self.lineCertKeyFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) def _cb_combo_node_auth_type_changed(self, index): curtype = self.comboNodeAuthType.itemData(self.comboNodeAuthType.currentIndex()) #savedtype = self._cfg.getSettings(Config.AUTH_TYPE) #if curtype != savedtype: # self._changes_needs_restart = QC.translate("preferences", "Auth type changed") self.lineNodeCACertFile.setEnabled(index == PreferencesDialog.AUTH_TLS_MUTUAL) self.lineNodeServerCertFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) self.lineNodeCertFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) self.lineNodeCertKeyFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) self.checkNodeAuthSkipVerify.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) self.comboNodeAuthVerifyType.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) self._node_needs_update = True def _cb_db_max_days_toggled(self, state): self._enable_db_cleaner_options(state, 1) def _cb_db_jrnl_wal_toggled(self, state): self._changes_needs_restart = QC.translate("preferences", "DB journal_mode changed") def _cb_cmd_spin_clicked(self, spinWidget, operation): if operation == self.SUM: spinWidget.setValue(spinWidget.value() + 1) else: spinWidget.setValue(spinWidget.value() - 1) if spinWidget == self.popupsCheck: enablePopups = spinWidget.value() > 0 self.popupsCheck.setChecked(not enablePopups) self.spinUITimeout.setEnabled(enablePopups) def _cb_radio_system_notifications(self): if self._desktop_notifications.is_available() == False: self.radioSysNotifs.setChecked(False) self.radioQtNotifs.setChecked(True) self._set_status_error(QC.translate("notifications", "System notifications are not available, you need to install python3-notify2.")) return def _cb_test_notifs_clicked(self): try: self.cmdTestNotifs.setEnabled(False) if self._desktop_notifications.is_available() == False: self._set_status_error(QC.translate("notifications", "System notifications are not available, you need to install python3-notify2.")) return if self.radioSysNotifs.isChecked(): self._desktop_notifications.show("title", "body") else: pass except Exception as e: print(self.LOG_TAG + "exception testing notifications:", e) finally: self.cmdTestNotifs.setEnabled(True) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/processdetails.py��������������������������������������������0000664�0000000�0000000�00000027670�15003540030�0023512�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os import sys import json from PyQt5 import QtCore, QtGui, uic, QtWidgets import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() from opensnitch.nodes import Nodes from opensnitch.desktop_parser import LinuxDesktopParser from opensnitch.utils import Message, Icons DIALOG_UI_PATH = "%s/../res/process_details.ui" % os.path.dirname(sys.modules[__name__].__file__) class ProcessDetailsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): LOG_TAG = "[ProcessDetails]: " _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) TAB_STATUS = 0 TAB_DESCRIPTORS = 1 TAB_IOSTATS = 2 TAB_MAPS = 3 TAB_STACK = 4 TAB_ENVS = 5 TABS = { TAB_STATUS: { "text": None, "scrollPos": 0 }, TAB_DESCRIPTORS: { "text": None, "scrollPos": 0 }, TAB_IOSTATS: { "text": None, "scrollPos": 0 }, TAB_MAPS: { "text": None, "scrollPos": 0 }, TAB_STACK: { "text": None, "scrollPos": 0 }, TAB_ENVS: { "text": None, "scrollPos": 0 } } def __init__(self, parent=None, appicon=None): super(ProcessDetailsDialog, self).__init__(parent) QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) self.setWindowFlags(QtCore.Qt.Window) self.setupUi(self) self.setWindowIcon(appicon) self._app_name = None self._app_icon = None self._apps_parser = LinuxDesktopParser() self._nodes = Nodes.instance() self._notification_callback.connect(self._cb_notification_callback) self._nid = None self._pid = "" self._notifications_sent = {} self.cmdClose.clicked.connect(self._cb_close_clicked) self.cmdAction.clicked.connect(self._cb_action_clicked) self.comboPids.currentIndexChanged.connect(self._cb_combo_pids_changed) self.TABS[self.TAB_STATUS]['text'] = self.textStatus self.TABS[self.TAB_DESCRIPTORS]['text'] = self.textOpenedFiles self.TABS[self.TAB_IOSTATS]['text'] = self.textIOStats self.TABS[self.TAB_MAPS]['text'] = self.textMappedFiles self.TABS[self.TAB_STACK]['text'] = self.textStack self.TABS[self.TAB_ENVS]['text'] = self.textEnv self.TABS[self.TAB_DESCRIPTORS]['text'].setFont(QtGui.QFont("monospace")) self.iconStart = QtGui.QIcon.fromTheme("media-playback-start") self.iconPause = QtGui.QIcon.fromTheme("media-playback-pause") if QtGui.QIcon.hasThemeIcon("window-close"): return closeIcon = Icons.new(self, "window-close") self.cmdClose.setIcon(closeIcon) self.iconStart = Icons.new(self, "media-playback-start") self.iconPause = Icons.new(self, "media-playback-pause") @QtCore.pyqtSlot(ui_pb2.NotificationReply) def _cb_notification_callback(self, reply): if reply.id in self._notifications_sent: noti = self._notifications_sent[reply.id] if reply.code == ui_pb2.ERROR: self._show_message(QtCore.QCoreApplication.translate("proc_details", "<b>Error loading process information:</b> <br><br>\n\n") + reply.data) self._pid = "" self._set_button_running(False) # if we haven't loaded any data yet, just close the window if self._data_loaded == False: # but if there're more than 1 pid keep the window open. # we may have one pid already closed and one alive. if self.comboPids.count() <= 1: self._close() self._delete_notification(reply.id) return if noti.type == ui_pb2.MONITOR_PROCESS and reply.data != "": self._load_data(reply.data) elif noti.type == ui_pb2.STOP_MONITOR_PROCESS: if reply.data != "": self._show_message(QtCore.QCoreApplication.translate("proc_details", "<b>Error stopping monitoring process:</b><br><br>") + reply.data) self._set_button_running(False) self._delete_notification(reply.id) else: print("[stats] unknown notification received: ", reply.id) def closeEvent(self, e): self._close() def _cb_close_clicked(self): self._close() def _cb_combo_pids_changed(self, idx): if idx == -1: return # TODO: this event causes to send to 2 Start notifications #if self._pid != "" and self._pid != self.comboPids.currentText(): # self._stop_monitoring() # self._pid = self.comboPids.currentText() # self._start_monitoring() def _cb_action_clicked(self): if not self.cmdAction.isChecked(): self._stop_monitoring() else: self._start_monitoring() def _show_message(self, text): Message.ok(text, "", QtWidgets.QMessageBox.Warning) def _delete_notification(self, nid): if nid in self._notifications_sent: del self._notifications_sent[nid] def _reset(self): self._app_name = None self._app_icon = None self.comboPids.clear() self.labelProcName.setText(QtCore.QCoreApplication.translate("proc_details", "loading...")) self.labelProcArgs.setText(QtCore.QCoreApplication.translate("proc_details", "loading...")) self.labelProcIcon.clear() self.labelStatm.setText("") self.labelCwd.setText("") for tidx in range(0, len(self.TABS)): self.TABS[tidx]['text'].setPlainText("") def _set_button_running(self, yes): if yes: self.cmdAction.setChecked(True) self.cmdAction.setIcon(self.iconPause) else: self.cmdAction.setChecked(False) self.cmdAction.setIcon(self.iconStart) def _close(self): self._stop_monitoring() self.comboPids.clear() self._pid = "" self.hide() def monitor(self, pids): if self._pid != "": self._stop_monitoring() self._data_loaded = False self._pids = pids self._reset() for pid in pids: if pid != None: self.comboPids.addItem(pid) self.show() self._start_monitoring() def _set_tab_text(self, tab_idx, text): self.TABS[tab_idx]['scrollPos'] = self.TABS[tab_idx]['text'].verticalScrollBar().value() self.TABS[tab_idx]['text'].setPlainText(text) self.TABS[tab_idx]['text'].verticalScrollBar().setValue(self.TABS[tab_idx]['scrollPos']) def _start_monitoring(self): try: # avoid to send notifications without a pid if self._pid != "": return self._pid = self.comboPids.currentText() if self._pid == "": return self._set_button_running(True) noti = ui_pb2.Notification(clientName="", serverName="", type=ui_pb2.MONITOR_PROCESS, data=self._pid, rules=[]) self._nid = self._nodes.send_notification(self._pids[self._pid], noti, self._notification_callback) self._notifications_sent[self._nid] = noti except Exception as e: print(self.LOG_TAG + "exception starting monitoring: ", e) def _stop_monitoring(self): if self._pid == "": return self._set_button_running(False) noti = ui_pb2.Notification(clientName="", serverName="", type=ui_pb2.STOP_MONITOR_PROCESS, data=str(self._pid), rules=[]) self._nid = self._nodes.send_notification(self._pids[self._pid], noti, self._notification_callback) self._notifications_sent[self._nid] = noti self._pid = "" self._app_icon = None def _load_data(self, data): tab_idx = self.tabWidget.currentIndex() try: proc = json.loads(data) self._load_app_icon(proc['Path']) if self._app_name != None: self.labelProcName.setText("<b>" + self._app_name + "</b>") self.labelProcName.setToolTip("<b>" + self._app_name + "</b>") #if proc['Path'] not in proc['Args']: # proc['Args'].insert(0, proc['Path']) self.labelProcArgs.setFixedHeight(30) self.labelProcArgs.setText(" ".join(proc['Args'])) self.labelProcArgs.setToolTip(" ".join(proc['Args'])) self.labelCwd.setText("<b>CWD: </b>" + proc['CWD']) self.labelCwd.setToolTip("<b>CWD: </b>" + proc['CWD']) self._load_mem_data(proc['Statm']) if tab_idx == self.TAB_STATUS: self._set_tab_text(tab_idx, proc['Status']) elif tab_idx == self.TAB_DESCRIPTORS: self._load_descriptors(proc['Descriptors']) elif tab_idx == self.TAB_IOSTATS: self._load_iostats(proc['IOStats']) elif tab_idx == self.TAB_MAPS: self._set_tab_text(tab_idx, proc['Maps']) elif tab_idx == self.TAB_STACK: self._set_tab_text(tab_idx, proc['Stack']) elif tab_idx == self.TAB_ENVS: self._load_env_vars(proc['Env']) self._data_loaded = True except Exception as e: print(self.LOG_TAG + "exception loading data: ", e) def _load_app_icon(self, proc_path): if self._app_icon != None: return self._app_name, self._app_icon, _, _ = self._apps_parser.get_info_by_path(proc_path, "terminal") icon = QtGui.QIcon().fromTheme(self._app_icon) pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) self.labelProcIcon.setPixmap(pixmap) if self._app_name == None: self._app_name = proc_path def _load_iostats(self, iostats): ioText = "%-16s %dMB<br>%-16s %dMB<br>%-16s %d<br>%-16s %d<br>%-16s %dMB<br>%-16s %dMB<br>" % ( "<b>Chars read:</b>", ((iostats['RChar'] / 1024) / 1024), "<b>Chars written:</b>", ((iostats['WChar'] / 1024) / 1024), "<b>Syscalls read:</b>", (iostats['SyscallRead']), "<b>Syscalls write:</b>", (iostats['SyscallWrite']), "<b>KB read:</b>", ((iostats['ReadBytes'] / 1024) / 1024), "<b>KB written: </b>", ((iostats['WriteBytes'] / 1024) / 1024) ) self.textIOStats.setPlainText("") self.textIOStats.appendHtml(ioText) def _load_mem_data(self, mem): # assuming page size == 4096 pagesize = 4096 memText = "<b>VIRT:</b> %dMB, <b>RSS:</b> %dMB, <b>Libs:</b> %dMB, <b>Data:</b> %dMB, <b>Text:</b> %dMB" % ( ((mem['Size'] * pagesize) / 1024) / 1024, ((mem['Resident'] * pagesize) / 1024) / 1024, ((mem['Lib'] * pagesize) / 1024) / 1024, ((mem['Data'] * pagesize) / 1024) / 1024, ((mem['Text'] * pagesize) / 1024) / 1024 ) self.labelStatm.setText(memText) def _load_descriptors(self, descriptors): text = "%-12s%-40s%-8s -> %s\n\n" % ("Size", "Time", "Name", "Symlink") for d in descriptors: text += "{:<12}{:<40}{:<8} -> {}\n".format(str(d['Size']), d['ModTime'], d['Name'], d['SymLink']) self._set_tab_text(self.TAB_DESCRIPTORS, text) def _load_env_vars(self, envs): if envs == {}: self._set_tab_text(self.TAB_ENVS, "<no environment variables>") return text = "%-15s\t%s\n\n" % ("Name", "Value") for env_name in envs: text += "%-15s:\t%s\n" % (env_name, envs[env_name]) self._set_tab_text(self.TAB_ENVS, text) ������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/prompt.py����������������������������������������������������0000664�0000000�0000000�00000101522�15003540030�0021774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import threading import sys import time import os import os.path import pwd import json import ipaddress from datetime import datetime from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtCore import QCoreApplication as QC, QEvent from slugify import slugify from opensnitch.utils import Icons from opensnitch.desktop_parser import LinuxDesktopParser from opensnitch.config import Config from opensnitch.version import version from opensnitch.actions import Actions from opensnitch.rules import Rules, Rule import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() DIALOG_UI_PATH = "%s/../res/prompt.ui" % os.path.dirname(sys.modules[__name__].__file__) class PromptDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): _prompt_trigger = QtCore.pyqtSignal() _tick_trigger = QtCore.pyqtSignal() _timeout_trigger = QtCore.pyqtSignal() DEFAULT_TIMEOUT = 15 # don't translate FIELD_REGEX_HOST = "regex_host" FIELD_REGEX_IP = "regex_ip" FIELD_PROC_PATH = "process_path" FIELD_PROC_ARGS = "process_args" FIELD_PROC_ID = "process_id" FIELD_USER_ID = "user_id" FIELD_DST_IP = "dst_ip" FIELD_DST_PORT = "dst_port" FIELD_DST_NETWORK = "dst_network" FIELD_DST_HOST = "simple_host" FIELD_APPIMAGE = "appimage_path" DURATION_30s = "30s" DURATION_5m = "5m" DURATION_15m = "15m" DURATION_30m = "30m" DURATION_1h = "1h" # don't translate APPIMAGE_PREFIX = "/tmp/.mount_" # label displayed in the pop-up combo DURATION_session = QC.translate("popups", "until reboot") # label displayed in the pop-up combo DURATION_forever = QC.translate("popups", "forever") def __init__(self, parent=None, appicon=None): QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) # Other interesting flags: QtCore.Qt.Tool | QtCore.Qt.BypassWindowManagerHint self._cfg = Config.get() self._rules = Rules.instance() self.setupUi(self) self.setWindowIcon(appicon) self.installEventFilter(self) self._width = self.width() self._height = self.height() self.reset_widgets() dialog_geometry = self._cfg.getSettings("promptDialog/geometry") if dialog_geometry == QtCore.QByteArray: self.restoreGeometry(dialog_geometry) self.setWindowTitle("OpenSnitch v%s" % version) self._lock = threading.Lock() self._con = None self._rule = None self._local = True self._peer = None self._prompt_trigger.connect(self.on_connection_prompt_triggered) self._timeout_trigger.connect(self.on_timeout_triggered) self._tick_trigger.connect(self.on_tick_triggered) self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) if self._cfg.hasKey(self._cfg.DEFAULT_TIMEOUT_KEY) else self.DEFAULT_TIMEOUT self._tick_thread = None self._done = threading.Event() self._timeout_text = "" self._timeout_triggered = False self._apps_parser = LinuxDesktopParser() self.whatIPCombo.setVisible(False) self.checkDstIP.setVisible(False) self.checkDstPort.setVisible(False) self.checkUserID.setVisible(False) self.appDescriptionLabel.setVisible(False) self._ischeckAdvanceded = False self.checkAdvanced.toggled.connect(self._check_advanced_toggled) self.checkAdvanced.clicked.connect(self._button_clicked) self.durationCombo.activated.connect(self._button_clicked) self.whatCombo.activated.connect(self._button_clicked) self.whatIPCombo.activated.connect(self._button_clicked) self.checkDstIP.clicked.connect(self._button_clicked) self.checkDstPort.clicked.connect(self._button_clicked) self.checkUserID.clicked.connect(self._button_clicked) self.allowIcon = Icons.new(self, "emblem-default") denyIcon = Icons.new(self, "emblem-important") rejectIcon = Icons.new(self, "window-close") self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) self.allowButton.clicked.connect(lambda: self._on_action_clicked(Config.ACTION_ALLOW_IDX)) self.allowButton.setIcon(self.allowIcon) self._allow_text = QC.translate("popups", "Allow") self._action_text = [ QC.translate("popups", "Deny"), QC.translate("popups", "Allow"), QC.translate("popups", "Reject") ] self._action_icon = [denyIcon, self.allowIcon, rejectIcon] m = QtWidgets.QMenu() m.addAction(denyIcon, self._action_text[Config.ACTION_DENY_IDX]).triggered.connect( lambda: self._on_action_clicked(Config.ACTION_DENY_IDX) ) m.addAction(self.allowIcon, self._action_text[Config.ACTION_ALLOW_IDX]).triggered.connect( lambda: self._on_action_clicked(Config.ACTION_ALLOW_IDX) ) m.addAction(rejectIcon, self._action_text[Config.ACTION_REJECT_IDX]).triggered.connect( lambda: self._on_action_clicked(Config.ACTION_REJECT_IDX) ) self.actionButton.setMenu(m) self.actionButton.setText(self._action_text[Config.ACTION_DENY_IDX]) self.actionButton.setIcon(self._action_icon[Config.ACTION_DENY_IDX]) if self._default_action != Config.ACTION_ALLOW_IDX: self.actionButton.setText(self._action_text[self._default_action]) self.actionButton.setIcon(self._action_icon[self._default_action]) self.actionButton.clicked.connect(self._on_deny_btn_clicked) def eventFilter(self, obj, event): if event.type() == QEvent.MouseButtonPress: self._stop_countdown() return True return False def showEvent(self, event): super(PromptDialog, self).showEvent(event) self.activateWindow() self.adjust_size() self.move_popup() def reset_widgets(self): # Don't allow labels to grow more than the dialog's width. # This can happen if the path or the binary name is too large. self.appNameLabel.setMaximumWidth(self._width-5) self.appDescriptionLabel.setMaximumWidth(self._width-5) self.appPathLabel.setMaximumWidth(self._width-5) self.argsLabel.setMaximumWidth(self._width-5) self.messageLabel.setMaximumWidth(self._width-5) self.appNameLabel.setText("") self.appDescriptionLabel.setText("") self.appPathLabel.setText("") self.argsLabel.setText("") self.messageLabel.setText("") def adjust_size(self): if self._width is None or self._height is None: self._width = self.width() self._height = self.height() self.resize(QtCore.QSize(self._width, self._height)) def move_popup(self): popup_pos = self._cfg.getInt(self._cfg.DEFAULT_POPUP_POSITION) point = QtWidgets.QDesktopWidget().availableGeometry() if popup_pos == self._cfg.POPUP_TOP_RIGHT: self.move(point.topRight()) elif popup_pos == self._cfg.POPUP_TOP_LEFT: self.move(point.topLeft()) elif popup_pos == self._cfg.POPUP_BOTTOM_RIGHT: self.move(point.bottomRight()) elif popup_pos == self._cfg.POPUP_BOTTOM_LEFT: self.move(point.bottomLeft()) def _stop_countdown(self): action_idx = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) if action_idx == Config.ACTION_ALLOW_IDX: self.allowButton.setText(self._allow_text) self.allowButton.setIcon(self.allowIcon) else: self.actionButton.setText(self._action_text[action_idx]) self.actionButton.setIcon(self._action_icon[action_idx]) self._tick_thread.stop = True def _check_advanced_toggled(self, state): self.checkDstIP.setVisible(state) self.whatIPCombo.setVisible(state) self.destIPLabel.setVisible(not state) self.checkDstPort.setVisible(state == True and (self._con != None and self._con.dst_port != 0)) self.checkUserID.setVisible(state) self._ischeckAdvanceded = state self.adjust_size() self.move_popup() def _button_clicked(self): self._stop_countdown() def truncate_text(self, text, max_size=64): if len(text) > max_size: text = text[:max_size] + "..." return text def _set_elide_text(self, widget, text, max_size=64): text = self.truncate_text(text, max_size) widget.setText(text) def promptUser(self, connection, is_local, peer): # one at a time with self._lock: # reset state self.reset_widgets() if self._tick_thread != None and self._tick_thread.is_alive(): self._tick_thread.join() self._cfg.reload() self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) if self._cfg.hasKey(self._cfg.DEFAULT_TIMEOUT_KEY) else self.DEFAULT_TIMEOUT self._tick_thread = threading.Thread(target=self._timeout_worker) self._tick_thread.stop = self._ischeckAdvanceded self._timeout_triggered = False self._rule = None self._local = is_local self._peer = peer self._con = connection self._done.clear() # trigger and show dialog self._prompt_trigger.emit() # start timeout thread self._tick_thread.start() # wait for user choice or timeout self._done.wait() return self._rule, self._timeout_triggered def _timeout_worker(self): if self._tick == 0: self._timeout_trigger.emit() return while self._tick > 0 and self._done.is_set() is False: t = threading.currentThread() # stop only stops the coundtdown, not the thread itself. if getattr(t, "stop", True): self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) time.sleep(1) continue self._tick -= 1 self._tick_trigger.emit() time.sleep(1) if not self._done.is_set(): self._timeout_trigger.emit() @QtCore.pyqtSlot() def on_connection_prompt_triggered(self): self._render_connection(self._con) if self._tick > 0: self.show() @QtCore.pyqtSlot() def on_tick_triggered(self): self._set_cmd_action_text() @QtCore.pyqtSlot() def on_timeout_triggered(self): self._timeout_triggered = True self._send_rule() def _hide_widget(self, widget, hide): widget.setVisible(not hide) def _configure_default_duration(self): if self._cfg.hasKey(self._cfg.DEFAULT_DURATION_KEY): cur_idx = self._cfg.getInt(self._cfg.DEFAULT_DURATION_KEY) self.durationCombo.setCurrentIndex(cur_idx) else: self.durationCombo.setCurrentIndex(self._cfg.DEFAULT_DURATION_IDX) def _set_cmd_action_text(self): action_idx = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) if action_idx == Config.ACTION_ALLOW_IDX: self.allowButton.setText("{0} ({1})".format(self._allow_text, self._tick)) self.allowButton.setIcon(self.allowIcon) self.actionButton.setText(self._action_text[Config.ACTION_DENY_IDX]) else: self.allowButton.setText(self._allow_text) self.actionButton.setText("{0} ({1})".format(self._action_text[action_idx], self._tick)) self.actionButton.setIcon(self._action_icon[action_idx]) def _set_app_description(self, description): if description != None and description != "": self.appDescriptionLabel.setVisible(True) self.appDescriptionLabel.setFixedHeight(50) self.appDescriptionLabel.setToolTip(description) self._set_elide_text(self.appDescriptionLabel, "%s" % description) else: self.appDescriptionLabel.setVisible(False) self.appDescriptionLabel.setFixedHeight(0) self.appDescriptionLabel.setText("") return self.appDescriptionLabel.setText( "".join( filter(str.isprintable, self.appDescriptionLabel.text()) ) ) def _set_app_path(self, app_name, app_args, con): # show the binary path if it's not part of the cmdline args: # cmdline: telnet 1.1.1.1 (path: /usr/bin/telnet.netkit) # cmdline: /usr/bin/telnet.netkit 1.1.1.1 (the binary path is part of the cmdline args, no need to display it) if con.process_path != "" and len(con.process_args) >= 1 and con.process_path not in con.process_args: self.appPathLabel.setToolTip("Process path: %s" % con.process_path) if app_name.lower() == app_args: self._set_elide_text(self.appPathLabel, "%s" % con.process_path) else: self._set_elide_text(self.appPathLabel, "(%s)" % con.process_path) self.appPathLabel.setVisible(True) elif con.process_path != "" and len(con.process_args) == 0: self._set_elide_text(self.appPathLabel, "%s" % con.process_path) self.appPathLabel.setVisible(True) else: self.appPathLabel.setVisible(False) self.appPathLabel.setText("") return self.appPathLabel.setText( "".join( filter(str.isprintable, self.appPathLabel.text()) ) ) if self.appPathLabel.width() >= self._width: self.appPathLabel.setText("\u200b".join(self.appPathLabel.text())) def _set_app_args(self, app_name, app_args): # if the app name and the args are the same, there's no need to display # the args label (amule for example) if app_name.lower() != app_args: self.argsLabel.setVisible(True) self._set_elide_text(self.argsLabel, app_args, 256) self.argsLabel.setToolTip(app_args) else: self.argsLabel.setVisible(False) self.argsLabel.setText("") return self.argsLabel.setText( "".join( filter(str.isprintable, self.argsLabel.text()) ) ) if self.argsLabel.width() >= self._width: self.argsLabel.setText("\u200b".join(self.argsLabel.text())) def _set_default_target(self, combo, con, app_name, app_args): # set appimage as default target if the process path starts with # /tmp/._mount if con.process_path.startswith(self.APPIMAGE_PREFIX): idx = combo.findData(self.FIELD_APPIMAGE) if idx != -1: combo.setCurrentIndex(idx) return if int(con.process_id) > 0 and app_name != "" and app_args != "": self.whatCombo.setCurrentIndex(int(self._cfg.getSettings(self._cfg.DEFAULT_TARGET_KEY))) else: self.whatCombo.setCurrentIndex(2) def _render_connection(self, con): app_name, app_icon, description, _ = self._apps_parser.get_info_by_path(con.process_path, "terminal") app_args = " ".join(con.process_args) self._set_app_description(description) self._set_app_path(app_name, app_args, con) self._set_app_args(app_name, app_args) if app_name == "": self.appPathLabel.setVisible(False) self.argsLabel.setVisible(False) app_name = QC.translate("popups", "Unknown process %s" % con.process_path) self.appNameLabel.setText(QC.translate("popups", "Outgoing connection")) else: self._set_elide_text(self.appNameLabel, "%s" % app_name, max_size=42) self.appNameLabel.setToolTip(app_name) self.cwdLabel.setToolTip("%s %s" % (QC.translate("popups", "Process launched from:"), con.process_cwd)) self._set_elide_text(self.cwdLabel, con.process_cwd, max_size=32) pixmap = self._get_app_icon(app_icon) self.iconLabel.setPixmap(pixmap) message = self._get_popup_message(app_name, con) self.messageLabel.setText(message) self.messageLabel.setToolTip(message) self.sourceIPLabel.setText(con.src_ip) self.destIPLabel.setText(con.dst_ip) if con.dst_port == 0: self.destPortLabel.setText("") else: self.destPortLabel.setText(str(con.dst_port)) self._hide_widget(self.destPortLabel, con.dst_port == 0) self._hide_widget(self.destPortLabel_1, con.dst_port == 0) self._hide_widget(self.checkDstPort, con.dst_port == 0 or not self._ischeckAdvanceded) if self._local: try: uid = "%d (%s)" % (con.user_id, pwd.getpwuid(con.user_id).pw_name) except: uid = "" else: uid = "%d" % con.user_id self.uidLabel.setText(uid) self.pidLabel.setText("%s" % con.process_id) self.whatCombo.clear() self.whatIPCombo.clear() # the order of these combobox entries must match those in the preferences dialog # prefs -> UI -> Default target self.whatCombo.addItem(QC.translate("popups", "from this executable"), self.FIELD_PROC_PATH) if int(con.process_id) < 0: self.whatCombo.model().item(0).setEnabled(False) self.whatCombo.addItem(QC.translate("popups", "from this command line"), self.FIELD_PROC_ARGS) self.whatCombo.addItem(QC.translate("popups", "to port {0}").format(con.dst_port), self.FIELD_DST_PORT) self.whatCombo.addItem(QC.translate("popups", "to {0}").format(con.dst_ip), self.FIELD_DST_IP) self.whatCombo.addItem(QC.translate("popups", "from user {0}").format(uid), self.FIELD_USER_ID) if int(con.user_id) < 0: self.whatCombo.model().item(4).setEnabled(False) self.whatCombo.addItem(QC.translate("popups", "from this PID"), self.FIELD_PROC_ID) ####################### if con.process_path.startswith(self.APPIMAGE_PREFIX): self._add_appimage_pattern_to_combo(self.whatCombo, con) self._add_dst_networks_to_combo(self.whatCombo, con.dst_ip) if con.dst_host != "" and con.dst_host != con.dst_ip: self._add_dsthost_to_combo(con.dst_host) self.whatIPCombo.addItem(QC.translate("popups", "to {0}").format(con.dst_ip), self.FIELD_DST_IP) parts = con.dst_ip.split('.') nparts = len(parts) for i in range(1, nparts): self.whatCombo.addItem(QC.translate("popups", "to {0}.*").format('.'.join(parts[:i])), self.FIELD_REGEX_IP) self.whatIPCombo.addItem(QC.translate("popups", "to {0}.*").format( '.'.join(parts[:i])), self.FIELD_REGEX_IP) self._add_dst_networks_to_combo(self.whatIPCombo, con.dst_ip) self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) self._configure_default_duration() self._set_default_target(self.whatCombo, con, app_name, app_args) self.checkDstIP.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP)) self.checkDstPort.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT)) self.checkUserID.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_UID)) if self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED): self.checkAdvanced.toggle() self._set_cmd_action_text() self.checkAdvanced.setFocus() self.setFixedSize(self.size()) # https://gis.stackexchange.com/questions/86398/how-to-disable-the-escape-key-for-a-dialog def keyPressEvent(self, event): if not event.key() == QtCore.Qt.Key_Escape: super(PromptDialog, self).keyPressEvent(event) # prevent a click on the window's x # from quitting the whole application def closeEvent(self, e): self._send_rule() e.ignore() def _add_appimage_pattern_to_combo(self, combo, con): """appimages' absolute path usually starts with /tmp/.mount_ """ appimage_bin = os.path.basename(con.process_path) appimage_path = os.path.dirname(con.process_path) appimage_path = appimage_path[0:len(self.APPIMAGE_PREFIX)+6] combo.addItem( QC.translate("popups", "from {0}*/{1}").format(appimage_path, appimage_bin), self.FIELD_APPIMAGE ) def _add_dst_networks_to_combo(self, combo, dst_ip): if type(ipaddress.ip_address(dst_ip)) == ipaddress.IPv4Address: combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/24", strict=False)), self.FIELD_DST_NETWORK) combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/16", strict=False)), self.FIELD_DST_NETWORK) combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/8", strict=False)), self.FIELD_DST_NETWORK) else: combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/64", strict=False)), self.FIELD_DST_NETWORK) combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/128", strict=False)), self.FIELD_DST_NETWORK) def _add_dsthost_to_combo(self, dst_host): self.whatCombo.addItem("%s" % dst_host, self.FIELD_DST_HOST) self.whatIPCombo.addItem("%s" % dst_host, self.FIELD_DST_HOST) parts = dst_host.split('.')[1:] nparts = len(parts) for i in range(0, nparts - 1): self.whatCombo.addItem(QC.translate("popups", "to *.{0}").format('.'.join(parts[i:])), self.FIELD_REGEX_HOST) self.whatIPCombo.addItem(QC.translate("popups", "to *.{0}").format('.'.join(parts[i:])), self.FIELD_REGEX_HOST) def _get_app_icon(self, app_icon): """we try to get the icon of an app from the system. If it's not found, then we'll try to search for it in common directories of the system. """ try: icon = QtGui.QIcon().fromTheme(app_icon) pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) if QtGui.QIcon().hasThemeIcon(app_icon) == False or pixmap.height() == 0: # sometimes the icon is an absolute path, sometimes it's not if os.path.isabs(app_icon): icon = QtGui.QIcon(app_icon) pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) else: icon_path = self._apps_parser.discover_app_icon(app_icon) if icon_path != None: icon = QtGui.QIcon(icon_path) pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) except Exception as e: print("Exception _get_app_icon():", e) return pixmap def _get_popup_message(self, app_name, con): """ _get_popup_message helps constructing the message that is displayed on the pop-up dialog. Example: curl is connecting to www.opensnitch.io on TCP port 443 """ app_name = self.truncate_text(app_name) message = "<b>%s</b>" % app_name if not self._local: message = QC.translate("popups", "<b>Remote</b> process %s running on <b>%s</b>") % ( \ message, self._peer.split(':')[1]) msg_action = QC.translate("popups", "is connecting to <b>%s</b> on %s port %d") % ( \ con.dst_host or con.dst_ip, con.protocol.upper(), con.dst_port ) # icmp port is 0 (i.e.: no port) if con.dst_port == 0: msg_action = QC.translate("popups", "is connecting to <b>%s</b>, %s") % ( \ con.dst_host or con.dst_ip, con.protocol.upper() ) if con.dst_port == 53 and con.dst_ip != con.dst_host and con.dst_host != "": msg_action = QC.translate("popups", "is attempting to resolve <b>%s</b> via %s, %s port %d") % ( \ con.dst_host, con.dst_ip, con.protocol.upper(), con.dst_port) if self.messageLabel.width() >= self._width: self.messageLabel.setText("\u200b".join(self.messageLabel.text())) return "%s %s" % (message, msg_action) def _get_duration(self, duration_idx): if duration_idx == 0: return Config.DURATION_ONCE elif duration_idx == 1: return self.DURATION_30s elif duration_idx == 2: return self.DURATION_5m elif duration_idx == 3: return self.DURATION_15m elif duration_idx == 4: return self.DURATION_30m elif duration_idx == 5: return self.DURATION_1h elif duration_idx == 6: return Config.DURATION_UNTIL_RESTART else: return Config.DURATION_ALWAYS def _get_combo_operator(self, combo, what_idx, con): if combo.itemData(what_idx) == self.FIELD_PROC_PATH: return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_PATH, con.process_path elif combo.itemData(what_idx) == self.FIELD_PROC_ARGS: # this should not happen if len(con.process_args) == 0 or con.process_args[0] == "": return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_PATH, con.process_path return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_COMMAND, ' '.join(con.process_args) elif combo.itemData(what_idx) == self.FIELD_PROC_ID: return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_ID, "{0}".format(con.process_id) elif combo.itemData(what_idx) == self.FIELD_USER_ID: return Config.RULE_TYPE_SIMPLE, Config.OPERAND_USER_ID, "%s" % con.user_id elif combo.itemData(what_idx) == self.FIELD_DST_PORT: return Config.RULE_TYPE_SIMPLE, Config.OPERAND_DEST_PORT, "%s" % con.dst_port elif combo.itemData(what_idx) == self.FIELD_DST_IP: return Config.RULE_TYPE_SIMPLE, Config.OPERAND_DEST_IP, con.dst_ip elif combo.itemData(what_idx) == self.FIELD_DST_HOST: return Config.RULE_TYPE_SIMPLE, Config.OPERAND_DEST_HOST, combo.currentText() elif combo.itemData(what_idx) == self.FIELD_DST_NETWORK: # strip "to ": "to x.x.x/20" -> "x.x.x/20" # we assume that to is one word in all languages parts = combo.currentText().split(' ') text = parts[len(parts)-1] return Config.RULE_TYPE_NETWORK, Config.OPERAND_DEST_NETWORK, text elif combo.itemData(what_idx) == self.FIELD_REGEX_HOST: parts = combo.currentText().split(' ') text = parts[len(parts)-1] # ^(|.*\.)yahoo\.com dsthost = r'\.'.join(text.split('.')).replace("*", "") dsthost = r'^(|.*\.)%s$' % dsthost[2:] return Config.RULE_TYPE_REGEXP, Config.OPERAND_DEST_HOST, dsthost elif combo.itemData(what_idx) == self.FIELD_REGEX_IP: parts = combo.currentText().split(' ') text = parts[len(parts)-1] return Config.RULE_TYPE_REGEXP, Config.OPERAND_DEST_IP, "%s" % r'\.'.join(text.split('.')).replace("*", ".*") elif combo.itemData(what_idx) == self.FIELD_APPIMAGE: appimage_bin = os.path.basename(con.process_path) appimage_path = os.path.dirname(con.process_path).replace('.', r'\.') appimage_path = appimage_path[0:len(self.APPIMAGE_PREFIX)+7] return Config.RULE_TYPE_REGEXP, Config.OPERAND_PROCESS_PATH, r'^{0}[0-9A-Za-z]{{6}}\/.*{1}$'.format(appimage_path, appimage_bin) def _on_action_clicked(self, action): self._default_action = action self._send_rule() def _on_deny_btn_clicked(self, action): self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) if self._default_action == Config.ACTION_ALLOW_IDX: self._default_action = Config.ACTION_DENY_IDX self._send_rule() def _is_list_rule(self): return self.checkUserID.isChecked() or self.checkDstPort.isChecked() or self.checkDstIP.isChecked() def _get_rule_name(self, rule): rule_temp_name = slugify("%s %s" % (rule.action, rule.duration)) if self._is_list_rule(): rule_temp_name = "%s-list" % rule_temp_name else: rule_temp_name = "%s-simple" % rule_temp_name rule_temp_name = slugify("%s %s" % (rule_temp_name, rule.operator.data)) return rule_temp_name[:128] def _send_rule(self): try: self._cfg.setSettings("promptDialog/geometry", self.saveGeometry()) self._rule = ui_pb2.Rule(name="user.choice") self._rule.created = int(datetime.now().timestamp()) self._rule.enabled = True self._rule.duration = self._get_duration(self.durationCombo.currentIndex()) self._rule.action = Config.ACTION_ALLOW if self._default_action == Config.ACTION_DENY_IDX: self._rule.action = Config.ACTION_DENY elif self._default_action == Config.ACTION_REJECT_IDX: self._rule.action = Config.ACTION_REJECT what_idx = self.whatCombo.currentIndex() self._rule.operator.type, self._rule.operator.operand, self._rule.operator.data = self._get_combo_operator(self.whatCombo, what_idx, self._con) if self._rule.operator.data == "": print("Invalid rule, discarding: ", self._rule) self._rule = None return rule_temp_name = self._get_rule_name(self._rule) self._rule.name = rule_temp_name # TODO: move to a method data=[] if self.checkDstIP.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_DST_IP: _type, _operand, _data = self._get_combo_operator(self.whatIPCombo, self.whatIPCombo.currentIndex(), self._con) data.append({"type": _type, "operand": _operand, "data": _data}) rule_temp_name = slugify("%s %s" % (rule_temp_name, _data)) if self.checkDstPort.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_DST_PORT: data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_DEST_PORT, "data": str(self._con.dst_port)}) rule_temp_name = slugify("%s %s" % (rule_temp_name, str(self._con.dst_port))) if self.checkUserID.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_USER_ID: data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_USER_ID, "data": str(self._con.user_id)}) rule_temp_name = slugify("%s %s" % (rule_temp_name, str(self._con.user_id))) is_list_rule = self._is_list_rule() # If the user has selected to filter by cmdline, but the launched # command path is not absolute or the first component contains # "/proc/" (/proc/self/fd.., /proc/1234/fd...), we can't trust it. # In these cases, also filter by the absolute path to the binary. if self._rule.operator.operand == Config.OPERAND_PROCESS_COMMAND: proc_args = " ".join(self._con.process_args) proc_args = proc_args.split(" ") if os.path.isabs(proc_args[0]) == False or proc_args[0].startswith("/proc"): is_list_rule = True data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_PROCESS_PATH, "data": str(self._con.process_path)}) if is_list_rule: data.append({ "type": self._rule.operator.type, "operand": self._rule.operator.operand, "data": self._rule.operator.data }) # We need to send back the operator list to the AskRule() call # as json string, in order to add it to the DB. self._rule.operator.data = json.dumps(data) self._rule.operator.type = Config.RULE_TYPE_LIST self._rule.operator.operand = Config.RULE_TYPE_LIST for op in data: self._rule.operator.list.extend([ ui_pb2.Operator( type=op['type'], operand=op['operand'], sensitive=False if op.get('sensitive') == None else op['sensitive'], data="" if op.get('data') == None else op['data'] ) ]) exists = self._rules.exists(self._rule, self._peer) if not exists: self._rule.name = self._rules.new_unique_name(rule_temp_name, self._peer, "") self.hide() if self._ischeckAdvanceded: self.checkAdvanced.toggle() self._ischeckAdvanceded = False except Exception as e: print("[pop-up] exception creating a rule:", e) finally: # signal that the user took a decision and # a new rule is available self._done.set() self.hide() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/ruleseditor.py�����������������������������������������������0000664�0000000�0000000�00000130037�15003540030�0023017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtCore import QCoreApplication as QC from slugify import slugify from datetime import datetime import re import sys import os import pwd import time import ipaddress import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() from opensnitch.config import Config from opensnitch.nodes import Nodes from opensnitch.database import Database from opensnitch.database.enums import RuleFields, ConnFields from opensnitch.version import version from opensnitch.utils import ( Message, FileDialog, Icons, NetworkInterfaces, qvalidator ) from opensnitch.rules import Rule, Rules DIALOG_UI_PATH = "%s/../res/ruleseditor.ui" % os.path.dirname(sys.modules[__name__].__file__) class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): LOG_TAG = "[rules editor]" classA_net = r'10\.\d{1,3}\.\d{1,3}\.\d{1,3}' classB_net = r'172\.1[6-9]\.\d+\.\d+|172\.2[0-9]\.\d+\.\d+|172\.3[0-1]+\.\d{1,3}\.\d{1,3}' classC_net = r'192\.168\.\d{1,3}\.\d{1,3}' others_net = r'127\.\d{1,3}\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}' multinets = r'2[32][23459]\.\d{1,3}\.\d{1,3}\.\d{1,3}' MULTICAST_RANGE = "^(" + multinets + ")$" LAN_RANGES = "^(" + others_net + "|" + classC_net + "|" + classB_net + "|" + classA_net + "|::1|f[cde].*::.*)$" LAN_LABEL = "LAN" MULTICAST_LABEL = "MULTICAST" INVALID_RULE_NAME_CHARS = '/' ADD_RULE = 0 EDIT_RULE = 1 WORK_MODE = ADD_RULE PW_USER = 0 PW_UID = 2 _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) def __init__(self, parent=None, _rule=None, appicon=None): super(RulesEditorDialog, self).__init__(parent) self._notifications_sent = {} self._nodes = Nodes.instance() self._db = Database.instance() self._rules = Rules.instance() self._notification_callback.connect(self._cb_notification_callback) self._old_rule_name = None self.setupUi(self) self.setWindowIcon(appicon) self.ruleNameValidator = qvalidator.RestrictChars(RulesEditorDialog.INVALID_RULE_NAME_CHARS) self.ruleNameValidator.result.connect(self._cb_rule_name_validator_result) self.ruleNameEdit.setValidator(self.ruleNameValidator) self.buttonBox.setStandardButtons( QtWidgets.QDialogButtonBox.Help | QtWidgets.QDialogButtonBox.Reset | QtWidgets.QDialogButtonBox.Close | QtWidgets.QDialogButtonBox.Save ) self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect(self._cb_reset_clicked) self.buttonBox.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self._cb_close_clicked) self.buttonBox.button(QtWidgets.QDialogButtonBox.Save).clicked.connect(self._cb_save_clicked) self.buttonBox.button(QtWidgets.QDialogButtonBox.Help).clicked.connect(self._cb_help_clicked) self.selectListButton.clicked.connect(self._cb_select_list_button_clicked) self.selectListRegexpButton.clicked.connect(self._cb_select_regexp_list_button_clicked) self.selectIPsListButton.clicked.connect(self._cb_select_ips_list_button_clicked) self.selectNetsListButton.clicked.connect(self._cb_select_nets_list_button_clicked) self.protoCheck.toggled.connect(self._cb_proto_check_toggled) self.procCheck.toggled.connect(self._cb_proc_check_toggled) self.cmdlineCheck.toggled.connect(self._cb_cmdline_check_toggled) self.ifaceCheck.toggled.connect(self._cb_iface_check_toggled) self.dstPortCheck.toggled.connect(self._cb_dstport_check_toggled) self.srcPortCheck.toggled.connect(self._cb_srcport_check_toggled) self.uidCheck.toggled.connect(self._cb_uid_check_toggled) self.pidCheck.toggled.connect(self._cb_pid_check_toggled) self.srcIPCheck.toggled.connect(self._cb_srcip_check_toggled) self.dstIPCheck.toggled.connect(self._cb_dstip_check_toggled) self.dstHostCheck.toggled.connect(self._cb_dsthost_check_toggled) self.dstListsCheck.toggled.connect(self._cb_dstlists_check_toggled) self.dstListRegexpCheck.toggled.connect(self._cb_dstregexplists_check_toggled) self.dstListIPsCheck.toggled.connect(self._cb_dstiplists_check_toggled) self.dstListNetsCheck.toggled.connect(self._cb_dstnetlists_check_toggled) self.uidCombo.currentIndexChanged.connect(self._cb_uid_combo_changed) self._users_list = pwd.getpwall() if QtGui.QIcon.hasThemeIcon("emblem-default"): return applyIcon = Icons.new(self, "emblem-default") denyIcon = Icons.new(self, "emblem-important") rejectIcon = Icons.new(self, "window-close") openIcon = Icons.new(self, "document-open") self.actionAllowRadio.setIcon(applyIcon) self.actionDenyRadio.setIcon(denyIcon) self.actionRejectRadio.setIcon(rejectIcon) self.selectListButton.setIcon(openIcon) self.selectListRegexpButton.setIcon(openIcon) self.selectNetsListButton.setIcon(openIcon) self.selectIPsListButton.setIcon(openIcon) if _rule != None: self._load_rule(rule=_rule) def showEvent(self, event): super(RulesEditorDialog, self).showEvent(event) # save old combo values so we don't overwrite them here. oldIface = self.ifaceCombo.currentText() oldUid = self.uidCombo.currentText() self.ifaceCombo.clear() self.uidCombo.clear() if self._nodes.is_local(self.nodesCombo.currentText()): self.ifaceCombo.addItems(NetworkInterfaces.list().keys()) try: for ip in NetworkInterfaces.list().values(): if self.srcIPCombo.findText(ip) == -1: self.srcIPCombo.insertItem(0, ip) if self.dstIPCombo.findText(ip) == -1: self.dstIPCombo.insertItem(0, ip) self._users_list = pwd.getpwall() self.uidCombo.blockSignals(True); for user in self._users_list: self.uidCombo.addItem("{0} ({1})".format(user[self.PW_USER], user[self.PW_UID]), user[self.PW_UID]) except Exception as e: print("[ruleseditor] Error adding IPs:", e) finally: self.uidCombo.blockSignals(False); self.ifaceCombo.setCurrentText(oldIface) self.uidCombo.setCurrentText(oldUid) def _bool(self, s): return s == 'True' def _cb_rule_name_validator_result(self, result): if result == QtGui.QValidator.Invalid: self._set_status_error( QC.translate("rules", "Invalid rule name (not allowed characters: '{0}' )".format(RulesEditorDialog.INVALID_RULE_NAME_CHARS) ) ) else: self._set_status_message("") def _cb_accept_clicked(self): pass def _cb_close_clicked(self): self.hide() def _cb_reset_clicked(self): self._reset_state() def _cb_help_clicked(self): QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_URL)) def _cb_select_list_button_clicked(self): dirName = FileDialog.select_dir(self, self.dstListsLine.text()) if dirName != None and dirName != "": self.dstListsLine.setText(dirName) def _cb_select_nets_list_button_clicked(self): dirName = FileDialog.select_dir(self, self.dstListNetsLine.text()) if dirName != None and dirName != "": self.dstListNetsLine.setText(dirName) def _cb_select_ips_list_button_clicked(self): dirName = FileDialog.select_dir(self, self.dstListIPsLine.text()) if dirName != None and dirName != "": self.dstListIPsLine.setText(dirName) def _cb_select_regexp_list_button_clicked(self): dirName = FileDialog.select_dir(self, self.dstRegexpListsLine.text()) if dirName != None and dirName != "": self.dstRegexpListsLine.setText(dirName) def _cb_proto_check_toggled(self, state): self.protoCombo.setEnabled(state) def _cb_proc_check_toggled(self, state): self.procLine.setEnabled(state) self.checkProcRegexp.setEnabled(state) def _cb_cmdline_check_toggled(self, state): self.cmdlineLine.setEnabled(state) self.checkCmdlineRegexp.setEnabled(state) def _cb_iface_check_toggled(self, state): self.ifaceCombo.setEnabled(state) def _cb_dstport_check_toggled(self, state): self.dstPortLine.setEnabled(state) def _cb_srcport_check_toggled(self, state): self.srcPortLine.setEnabled(state) def _cb_uid_check_toggled(self, state): self.uidCombo.setEnabled(state) def _cb_pid_check_toggled(self, state): self.pidLine.setEnabled(state) def _cb_srcip_check_toggled(self, state): self.srcIPCombo.setEnabled(state) def _cb_dstip_check_toggled(self, state): self.dstIPCombo.setEnabled(state) def _cb_dsthost_check_toggled(self, state): self.dstHostLine.setEnabled(state) def _cb_dstlists_check_toggled(self, state): self.dstListsLine.setEnabled(state) self.selectListButton.setEnabled(state) def _cb_dstregexplists_check_toggled(self, state): self.dstRegexpListsLine.setEnabled(state) self.selectListRegexpButton.setEnabled(state) def _cb_dstiplists_check_toggled(self, state): self.dstListIPsLine.setEnabled(state) self.selectIPsListButton.setEnabled(state) def _cb_dstnetlists_check_toggled(self, state): self.dstListNetsLine.setEnabled(state) self.selectNetsListButton.setEnabled(state) def _cb_uid_combo_changed(self, index): self.uidCombo.setCurrentText(str(self._users_list[index][self.PW_UID])) def _set_status_error(self, msg): self.statusLabel.setStyleSheet('color: red') self.statusLabel.setText(msg) def _set_status_message(self, msg): self.statusLabel.setStyleSheet('color: green') self.statusLabel.setText(msg) def _cb_save_clicked(self): if self.nodesCombo.count() == 0: self._set_status_error(QC.translate("rules", "There're no nodes connected.")) return rule_name = self.ruleNameEdit.text() if rule_name == "": return node = self.nodesCombo.currentText() # avoid to overwrite rules when: # - adding a new rule. # - when a rule is renamed, i.e., the rule is edited or added and the # user changes the name. if self.WORK_MODE == self.ADD_RULE and self._db.get_rule(rule_name, node).next() == True: self._set_status_error(QC.translate("rules", "There's already a rule with this name.")) return elif self.WORK_MODE == self.EDIT_RULE and rule_name != self._old_rule_name and \ self._db.get_rule(rule_name, node).next() == True: self._set_status_error(QC.translate("rules", "There's already a rule with this name.")) return result, error = self._save_rule() if result == False: self._set_status_error(error) return self._add_rule() if self._old_rule_name != None and self._old_rule_name != self.rule.name: self._delete_rule() self._old_rule_name = rule_name # after adding a new rule, we enter into EDIT mode, to allow further # changes without closing the dialog. if self.WORK_MODE == self.ADD_RULE: self.WORK_MODE = self.EDIT_RULE self._rules.updated.emit(0) @QtCore.pyqtSlot(ui_pb2.NotificationReply) def _cb_notification_callback(self, reply): #print(self.LOG_TAG, "Rule notification received: ", reply.id, reply.code) if reply.id in self._notifications_sent: if reply.code == ui_pb2.OK: self._set_status_message(QC.translate("rules", "Rule applied.")) else: self._set_status_error(QC.translate("rules", "Error applying rule: {0}").format(reply.data)) del self._notifications_sent[reply.id] def _get_duration(self, duration_idx): if duration_idx == 0: return Config.DURATION_ONCE elif duration_idx == 1: return Config.DURATION_30s elif duration_idx == 2: return Config.DURATION_5m elif duration_idx == 3: return Config.DURATION_15m elif duration_idx == 4: return Config.DURATION_30m elif duration_idx == 5: return Config.DURATION_1h elif duration_idx == 6: return Config.DURATION_UNTIL_RESTART else: return Config.DURATION_ALWAYS def _load_duration(self, duration): if duration == Config.DURATION_ONCE: return 0 elif duration == Config.DURATION_30s: return 1 elif duration == Config.DURATION_5m: return 2 elif duration == Config.DURATION_15m: return 3 elif duration == Config.DURATION_30m: return 4 elif duration == Config.DURATION_1h: return 5 elif duration == Config.DURATION_UNTIL_RESTART: return 6 else: # always return 7 def _is_regex(self, text): charset="\\*{[|^?$" for c in charset: if c in text: return True return False def _is_valid_regex(self, regex): try: re.compile(regex) return True except re.error as e: self.statusLabel.setText(str(e)) return False def _is_valid_list_path(self, listWidget): if listWidget.text() == "": return QC.translate("rules", "Lists field cannot be empty") if self._nodes.is_local(self.nodesCombo.currentText()) and \ self.nodeApplyAllCheck.isChecked() == False and \ os.path.isdir(listWidget.text()) == False: return QC.translate("rules", "Lists field must be a directory") return None def set_fields_from_connection(self, records): self.nodesCombo.setCurrentText(records.value(ConnFields.Node)) self.protoCombo.setCurrentText(records.value(ConnFields.Protocol).upper()) self.srcIPCombo.setCurrentText(records.value(ConnFields.SrcIP)) self.dstIPCombo.setCurrentText(records.value(ConnFields.DstIP)) self.dstHostLine.setText(records.value(ConnFields.DstHost)) self.dstPortLine.setText(records.value(ConnFields.DstPort)) self.srcPortLine.setText(records.value(ConnFields.SrcPort)) self.uidCombo.setCurrentText(records.value(ConnFields.UID)) self.pidLine.setText(records.value(ConnFields.PID)) self.procLine.setText(records.value(ConnFields.Process)) self.cmdlineLine.setText(records.value(ConnFields.Cmdline)) def _reset_state(self): self._old_rule_name = None self.rule = None self.ruleNameEdit.setText("") self.ruleDescEdit.setPlainText("") self.statusLabel.setText("") self.actionDenyRadio.setChecked(True) self.durationCombo.setCurrentIndex(0) self.protoCheck.setChecked(False) self.protoCombo.setCurrentText("") self.procCheck.setChecked(False) self.checkProcRegexp.setEnabled(False) self.checkProcRegexp.setChecked(False) self.procLine.setText("") self.cmdlineCheck.setChecked(False) self.checkCmdlineRegexp.setEnabled(False) self.checkCmdlineRegexp.setChecked(False) self.cmdlineLine.setText("") self.uidCheck.setChecked(False) self.uidCombo.setCurrentText("") self.pidCheck.setChecked(False) self.pidLine.setText("") self.ifaceCheck.setChecked(False) self.ifaceCombo.setCurrentText("") self.dstPortCheck.setChecked(False) self.dstPortLine.setText("") self.srcPortCheck.setChecked(False) self.srcPortLine.setText("") self.srcIPCheck.setChecked(False) self.srcIPCombo.setCurrentText("") self.dstIPCheck.setChecked(False) self.dstIPCombo.setCurrentText("") self.dstHostCheck.setChecked(False) self.dstHostLine.setText("") self.selectListButton.setEnabled(False) self.dstListsCheck.setChecked(False) self.dstListsLine.setText("") self.selectListRegexpButton.setEnabled(False) self.dstListRegexpCheck.setChecked(False) self.dstRegexpListsLine.setText("") self.selectIPsListButton.setEnabled(False) self.dstListIPsCheck.setChecked(False) self.dstListIPsLine.setText("") self.selectNetsListButton.setEnabled(False) self.dstListNetsCheck.setChecked(False) self.dstListNetsLine.setText("") def _load_rule(self, addr=None, rule=None): if self._load_nodes(addr) == False: return False self.ruleNameEdit.setText(rule.name) self.ruleDescEdit.setPlainText(rule.description) self.enableCheck.setChecked(rule.enabled) self.precedenceCheck.setChecked(rule.precedence) self.nologCheck.setChecked(rule.nolog) if rule.action == Config.ACTION_DENY: self.actionDenyRadio.setChecked(True) elif rule.action == Config.ACTION_ALLOW: self.actionAllowRadio.setChecked(True) elif rule.action == Config.ACTION_REJECT: self.actionRejectRadio.setChecked(True) self.durationCombo.setCurrentIndex(self._load_duration(self.rule.duration)) if self.rule.operator.type != Config.RULE_TYPE_LIST: self._load_rule_operator(self.rule.operator) else: for op in self.rule.operator.list: self._load_rule_operator(op) return True def _load_rule_operator(self, operator): self.sensitiveCheck.setChecked(operator.sensitive) if operator.operand == Config.OPERAND_PROTOCOL: self.protoCheck.setChecked(True) self.protoCombo.setEnabled(True) self.protoCombo.setCurrentText(operator.data.upper()) if operator.operand == Config.OPERAND_PROCESS_PATH: self.procCheck.setChecked(True) self.procLine.setEnabled(True) self.procLine.setText(operator.data) self.checkProcRegexp.setEnabled(True) self.checkProcRegexp.setChecked(operator.type == Config.RULE_TYPE_REGEXP) if operator.operand == Config.OPERAND_PROCESS_COMMAND: self.cmdlineCheck.setChecked(True) self.cmdlineLine.setEnabled(True) self.cmdlineLine.setText(operator.data) self.checkCmdlineRegexp.setEnabled(True) self.checkCmdlineRegexp.setChecked(operator.type == Config.RULE_TYPE_REGEXP) if operator.operand == Config.OPERAND_USER_ID: self.uidCheck.setChecked(True) self.uidCombo.setEnabled(True) self.uidCombo.setCurrentText(operator.data) if operator.operand == Config.OPERAND_PROCESS_ID: self.pidCheck.setChecked(True) self.pidLine.setEnabled(True) self.pidLine.setText(operator.data) if operator.operand == Config.OPERAND_IFACE_OUT: self.ifaceCheck.setChecked(True) self.ifaceCombo.setEnabled(True) self.ifaceCombo.setCurrentText(operator.data) if operator.operand == Config.OPERAND_SOURCE_PORT: self.srcPortCheck.setChecked(True) self.srcPortLine.setEnabled(True) self.srcPortLine.setText(operator.data) if operator.operand == Config.OPERAND_DEST_PORT: self.dstPortCheck.setChecked(True) self.dstPortLine.setEnabled(True) self.dstPortLine.setText(operator.data) if operator.operand == Config.OPERAND_SOURCE_IP or operator.operand == Config.OPERAND_SOURCE_NETWORK: self.srcIPCheck.setChecked(True) self.srcIPCombo.setEnabled(True) if operator.data == self.LAN_RANGES: self.srcIPCombo.setCurrentText(self.LAN_LABEL) elif operator.data == self.MULTICAST_RANGE: self.srcIPCombo.setCurrentText(self.MULTICAST_LABEL) else: self.srcIPCombo.setCurrentText(operator.data) if operator.operand == Config.OPERAND_DEST_IP or operator.operand == Config.OPERAND_DEST_NETWORK: self.dstIPCheck.setChecked(True) self.dstIPCombo.setEnabled(True) if operator.data == self.LAN_RANGES: self.dstIPCombo.setCurrentText(self.LAN_LABEL) elif operator.data == self.MULTICAST_RANGE: self.dstIPCombo.setCurrentText(self.MULTICAST_LABEL) else: self.dstIPCombo.setCurrentText(operator.data) if operator.operand == Config.OPERAND_DEST_HOST: self.dstHostCheck.setChecked(True) self.dstHostLine.setEnabled(True) self.dstHostLine.setText(operator.data) if operator.operand == Config.OPERAND_LIST_DOMAINS: self.dstListsCheck.setChecked(True) self.dstListsCheck.setEnabled(True) self.dstListsLine.setText(operator.data) self.selectListButton.setEnabled(True) if operator.operand == Config.OPERAND_LIST_DOMAINS_REGEXP: self.dstListRegexpCheck.setChecked(True) self.dstListRegexpCheck.setEnabled(True) self.dstRegexpListsLine.setText(operator.data) self.selectListRegexpButton.setEnabled(True) if operator.operand == Config.OPERAND_LIST_IPS: self.dstListIPsCheck.setChecked(True) self.dstListIPsCheck.setEnabled(True) self.dstListIPsLine.setText(operator.data) self.selectIPsListButton.setEnabled(True) if operator.operand == Config.OPERAND_LIST_NETS: self.dstListNetsCheck.setChecked(True) self.dstListNetsCheck.setEnabled(True) self.dstListNetsLine.setText(operator.data) self.selectNetsListButton.setEnabled(True) def _load_nodes(self, addr=None): try: self.nodesCombo.clear() self._node_list = self._nodes.get() if addr != None and addr not in self._node_list: Message.ok(QC.translate("rules", "<b>Error loading rule</b>"), QC.translate("rules", "node {0} not connected".format(addr)), QtWidgets.QMessageBox.Warning) return False if len(self._node_list) < 2: self.nodeApplyAllCheck.setVisible(False) for node in self._node_list: self.nodesCombo.addItem(node) if addr != None: self.nodesCombo.setCurrentText(addr) except Exception as e: print(self.LOG_TAG, "exception loading nodes: ", e, addr) return False return True def _insert_rule_to_db(self, node_addr): # the order of the fields doesn't matter here, as long as we use the # name of the field. self._rules.add_rules(node_addr, [self.rule]) def _add_rule(self): try: if self.nodeApplyAllCheck.isChecked(): for pos in range(self.nodesCombo.count()): self._insert_rule_to_db(self.nodesCombo.itemText(pos)) else: self._insert_rule_to_db(self.nodesCombo.currentText()) notif = ui_pb2.Notification( id=int(str(time.time()).replace(".", "")), type=ui_pb2.CHANGE_RULE, data="", rules=[self.rule]) if self.nodeApplyAllCheck.isChecked(): nid = self._nodes.send_notifications(notif, self._notification_callback) else: nid = self._nodes.send_notification(self.nodesCombo.currentText(), notif, self._notification_callback) self._notifications_sent[nid] = notif except Exception as e: print(self.LOG_TAG, "add_rule() exception: ", e) def _delete_rule(self): try: # if the rule name has changed, we need to remove the old one if self._old_rule_name != self.rule.name: node = self.nodesCombo.currentText() old_rule = self.rule old_rule.name = self._old_rule_name if self.nodeApplyAllCheck.isChecked(): nid, noti = self._nodes.delete_rule(rule_name=self._old_rule_name, addr=None, callback=self._notification_callback) self._notifications_sent[nid] = noti else: nid, noti = self._nodes.delete_rule(self._old_rule_name, node, self._notification_callback) self._notifications_sent[nid] = noti except Exception as e: print(self.LOG_TAG, "delete_rule() exception: ", e) def _save_rule(self): """ Create a new rule based on the fields selected. Ensure that some constraints are met: - Determine if a field can be a regexp. - Validate regexp. - Fields cannot be empty. - If the user has not provided a rule name, auto assign one. """ self.rule = ui_pb2.Rule() self.rule.created = int(datetime.now().timestamp()) self.rule.name = self.ruleNameEdit.text() self.rule.description = self.ruleDescEdit.toPlainText() self.rule.enabled = self.enableCheck.isChecked() self.rule.precedence = self.precedenceCheck.isChecked() self.rule.nolog = self.nologCheck.isChecked() self.rule.operator.type = Config.RULE_TYPE_SIMPLE self.rule.action = Config.ACTION_DENY if self.actionAllowRadio.isChecked(): self.rule.action = Config.ACTION_ALLOW elif self.actionRejectRadio.isChecked(): self.rule.action = Config.ACTION_REJECT self.rule.duration = self._get_duration(self.durationCombo.currentIndex()) # FIXME: there should be a sensitive checkbox per operand self.rule.operator.sensitive = self.sensitiveCheck.isChecked() rule_data = [] if self.protoCheck.isChecked(): if self.protoCombo.currentText() == "": return False, QC.translate("rules", "protocol can not be empty, or uncheck it") self.rule.operator.operand = Config.OPERAND_PROTOCOL self.rule.operator.data = self.protoCombo.currentText() rule_data.append( { "type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_PROTOCOL, "data": self.protoCombo.currentText().lower(), "sensitive": self.sensitiveCheck.isChecked() }) if self._is_regex(self.protoCombo.currentText()): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.protoCombo.currentText()) == False: return False, QC.translate("rules", "Protocol regexp error") if self.procCheck.isChecked(): if self.procLine.text() == "": return False, QC.translate("rules", "process path can not be empty") self.rule.operator.operand = Config.OPERAND_PROCESS_PATH self.rule.operator.data = self.procLine.text() rule_data.append( { "type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_PROCESS_PATH, "data": self.procLine.text(), "sensitive": self.sensitiveCheck.isChecked() }) if self.checkProcRegexp.isChecked(): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.procLine.text()) == False: return False, QC.translate("rules", "Process path regexp error") if self.cmdlineCheck.isChecked(): if self.cmdlineLine.text() == "": return False, QC.translate("rules", "command line can not be empty") self.rule.operator.operand = Config.OPERAND_PROCESS_COMMAND self.rule.operator.data = self.cmdlineLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, 'operand': Config.OPERAND_PROCESS_COMMAND, 'data': self.cmdlineLine.text(), "sensitive": self.sensitiveCheck.isChecked() }) if self.checkCmdlineRegexp.isChecked(): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.cmdlineLine.text()) == False: return False, QC.translate("rules", "Command line regexp error") if self.ifaceCheck.isChecked(): if self.ifaceCombo.currentText() == "": return False, QC.translate("rules", "Network interface can not be empty") self.rule.operator.operand = Config.OPERAND_IFACE_OUT self.rule.operator.data = self.ifaceCombo.currentText() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, 'operand': Config.OPERAND_IFACE_OUT, 'data': self.ifaceCombo.currentText(), "sensitive": self.sensitiveCheck.isChecked() }) if self._is_regex(self.ifaceCombo.currentText()): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.ifaceCombo.currentText()) == False: return False, QC.translate("rules", "Network interface regexp error") if self.srcPortCheck.isChecked(): if self.srcPortLine.text() == "": return False, QC.translate("rules", "Source port can not be empty") self.rule.operator.operand = Config.OPERAND_SOURCE_PORT self.rule.operator.data = self.srcPortLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, 'operand': Config.OPERAND_SOURCE_PORT, 'data': self.srcPortLine.text(), "sensitive": self.sensitiveCheck.isChecked() }) if self._is_regex(self.srcPortLine.text()): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.srcPortLine.text()) == False: return False, QC.translate("rules", "Source port regexp error") if self.dstPortCheck.isChecked(): if self.dstPortLine.text() == "": return False, QC.translate("rules", "Dest port can not be empty") self.rule.operator.operand = Config.OPERAND_DEST_PORT self.rule.operator.data = self.dstPortLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, 'operand': Config.OPERAND_DEST_PORT, 'data': self.dstPortLine.text(), "sensitive": self.sensitiveCheck.isChecked() }) if self._is_regex(self.dstPortLine.text()): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.dstPortLine.text()) == False: return False, QC.translate("rules", "Dst port regexp error") if self.dstHostCheck.isChecked(): if self.dstHostLine.text() == "": return False, QC.translate("rules", "Dest host can not be empty") self.rule.operator.operand = Config.OPERAND_DEST_HOST self.rule.operator.data = self.dstHostLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, 'operand': Config.OPERAND_DEST_HOST, 'data': self.dstHostLine.text(), "sensitive": self.sensitiveCheck.isChecked() }) if self._is_regex(self.dstHostLine.text()): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.dstHostLine.text()) == False: return False, QC.translate("rules", "Dst host regexp error") if self.srcIPCheck.isChecked(): if self.srcIPCombo.currentText() == "": return False, QC.translate("rules", "Source IP/Network can not be empty") srcIPtext = self.srcIPCombo.currentText() if srcIPtext == self.LAN_LABEL: self.rule.operator.operand = Config.OPERAND_SOURCE_IP self.rule.operator.type = Config.RULE_TYPE_REGEXP srcIPtext = self.LAN_RANGES elif srcIPtext == self.MULTICAST_LABEL: self.rule.operator.operand = Config.OPERAND_SOURCE_IP self.rule.operator.type = Config.RULE_TYPE_REGEXP srcIPtext = self.MULTICAST_RANGE else: try: if type(ipaddress.ip_address(self.srcIPCombo.currentText())) == ipaddress.IPv4Address \ or type(ipaddress.ip_address(self.srcIPCombo.currentText())) == ipaddress.IPv6Address: self.rule.operator.operand = Config.OPERAND_SOURCE_IP self.rule.operator.type = Config.RULE_TYPE_SIMPLE except Exception: self.rule.operator.operand = Config.OPERAND_SOURCE_NETWORK self.rule.operator.type = Config.RULE_TYPE_NETWORK if self._is_regex(srcIPtext): self.rule.operator.operand = Config.OPERAND_SOURCE_IP self.rule.operator.type = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.srcIPCombo.currentText()) == False: return False, QC.translate("rules", "Source IP regexp error") rule_data.append( { 'type': self.rule.operator.type, 'operand': self.rule.operator.operand, 'data': srcIPtext, "sensitive": self.sensitiveCheck.isChecked() }) if self.dstIPCheck.isChecked(): if self.dstIPCombo.currentText() == "": return False, QC.translate("rules", "Dest IP/Network can not be empty") dstIPtext = self.dstIPCombo.currentText() if dstIPtext == self.LAN_LABEL: self.rule.operator.operand = Config.OPERAND_DEST_IP self.rule.operator.type = Config.RULE_TYPE_REGEXP dstIPtext = self.LAN_RANGES elif dstIPtext == self.MULTICAST_LABEL: self.rule.operator.operand = Config.OPERAND_DEST_IP self.rule.operator.type = Config.RULE_TYPE_REGEXP dstIPtext = self.MULTICAST_RANGE else: try: if type(ipaddress.ip_address(self.dstIPCombo.currentText())) == ipaddress.IPv4Address \ or type(ipaddress.ip_address(self.dstIPCombo.currentText())) == ipaddress.IPv6Address: self.rule.operator.operand = Config.OPERAND_DEST_IP self.rule.operator.type = Config.RULE_TYPE_SIMPLE except Exception: self.rule.operator.operand = Config.OPERAND_DEST_NETWORK self.rule.operator.type = Config.RULE_TYPE_NETWORK if self._is_regex(dstIPtext): self.rule.operator.operand = Config.OPERAND_DEST_IP self.rule.operator.type = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.dstIPCombo.currentText()) == False: return False, QC.translate("rules", "Dst IP regexp error") rule_data.append( { 'type': self.rule.operator.type, 'operand': self.rule.operator.operand, 'data': dstIPtext, "sensitive": self.sensitiveCheck.isChecked() }) if self.uidCheck.isChecked(): uidType = Config.RULE_TYPE_SIMPLE uid = self.uidCombo.currentText() if uid == "": return False, QC.translate("rules", "User ID can not be empty") try: # sometimes when loading a rule, instead of the UID, the format # "user (uid)" is set. So try to parse it, in order not to save # a wrong uid. uidtmp = uid.split(" ") if len(uidtmp) == 1: int(uidtmp[0]) else: uid = str(pwd.getpwnam(uidtmp[0])[self.PW_UID]) except: # if it's not a digit and nor a system user (user (id)), see if # it's a regexp. if self._is_regex(self.uidCombo.currentText()): uidType = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.uidCombo.currentText()) == False: return False, QC.translate("rules", "User ID regexp error") else: return False, QC.translate("rules", "Invalid UID, it must be a digit.") self.rule.operator.operand = Config.OPERAND_USER_ID self.rule.operator.data = self.uidCombo.currentText() rule_data.append( { 'type': uidType, 'operand': Config.OPERAND_USER_ID, 'data': uid, "sensitive": self.sensitiveCheck.isChecked() }) if self.pidCheck.isChecked(): if self.pidLine.text() == "": return False, QC.translate("rules", "PID field can not be empty") self.rule.operator.operand = Config.OPERAND_PROCESS_ID self.rule.operator.data = self.pidLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, 'operand': Config.OPERAND_PROCESS_ID, 'data': self.pidLine.text(), "sensitive": self.sensitiveCheck.isChecked() }) if self._is_regex(self.pidLine.text()): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.pidLine.text()) == False: return False, QC.translate("rules", "PID field regexp error") if self.dstListsCheck.isChecked(): error = self._is_valid_list_path(self.dstListsLine) if error: return False, error self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = Config.OPERAND_LIST_DOMAINS rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': Config.OPERAND_LIST_DOMAINS, 'data': self.dstListsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = "" if self.dstListRegexpCheck.isChecked(): error = self._is_valid_list_path(self.dstRegexpListsLine) if error: return False, error self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = Config.OPERAND_LIST_DOMAINS_REGEXP rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': Config.OPERAND_LIST_DOMAINS_REGEXP, 'data': self.dstRegexpListsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = "" if self.dstListNetsCheck.isChecked(): error = self._is_valid_list_path(self.dstListNetsLine) if error: return False, error self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = Config.OPERAND_LIST_NETS rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': Config.OPERAND_LIST_NETS, 'data': self.dstListNetsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = "" if self.dstListIPsCheck.isChecked(): error = self._is_valid_list_path(self.dstListIPsLine) if error: return False, error self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = Config.OPERAND_LIST_IPS rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': Config.OPERAND_LIST_IPS, 'data': self.dstListIPsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = "" if len(rule_data) >= 2: self.rule.operator.type = Config.RULE_TYPE_LIST self.rule.operator.operand = Config.RULE_TYPE_LIST self.rule.operator.data = "" for rd in rule_data: self.rule.operator.list.extend([ ui_pb2.Operator( type=rd['type'], operand=rd['operand'], data=rd['data'], sensitive=rd['sensitive'] ) ]) print(self.rule.operator.list) elif len(rule_data) == 1: self.rule.operator.operand = rule_data[0]['operand'] self.rule.operator.data = rule_data[0]['data'] if self.checkProcRegexp.isChecked(): self.rule.operator.type = Config.RULE_TYPE_REGEXP elif self.checkCmdlineRegexp.isChecked(): self.rule.operator.type = Config.RULE_TYPE_REGEXP elif (self.procCheck.isChecked() == False and self.cmdlineCheck.isChecked() == False) \ and self._is_regex(self.rule.operator.data): self.rule.operator.type = Config.RULE_TYPE_REGEXP else: return False, QC.translate("rules", "Select at least one field.") if self.ruleNameEdit.text() == "": self.rule.name = slugify("%s %s %s" % (self.rule.action, self.rule.operator.type, self.rule.operator.data)) return True, "" def edit_rule(self, records, _addr=None): self.WORK_MODE = self.EDIT_RULE self._reset_state() self.rule = Rule.new_from_records(records) if self.rule.operator.type not in Config.RulesTypes: Message.ok(QC.translate("rules", "<b>Rule not supported</b>"), QC.translate("rules", "This type of rule ({0}) is not supported by version {1}".format(self.rule.operator.type, version)), QtWidgets.QMessageBox.Warning) self.hide() return self._old_rule_name = records.value(RuleFields.Name) if self._load_rule(addr=_addr, rule=self.rule): self.show() def new_rule(self): self.WORK_MODE = self.ADD_RULE self._reset_state() self._load_nodes() self.show() def new_rule_from_connection(self, coltime): self.WORK_MODE = self.ADD_RULE self._reset_state() self._load_nodes() try: records = self._db.get_connection_by_field("time", coltime) if records.next() == False: print(self.LOG_TAG, "error loading connection fields by time: {0}".format(coltime)) return False self.set_fields_from_connection(records) self.show() except Exception as e: print(self.LOG_TAG, "exception creating new rule from connection:", e) return False return True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/dialogs/stats.py�����������������������������������������������������0000664�0000000�0000000�00000375411�15003540030�0021623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import threading import datetime import sys import os import csv import io from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtCore import QCoreApplication as QC from opensnitch.config import Config from opensnitch.version import version from opensnitch.nodes import Nodes from opensnitch.firewall import Firewall, Rules as FwRules from opensnitch.dialogs.firewall import FirewallDialog from opensnitch.dialogs.preferences import PreferencesDialog from opensnitch.dialogs.ruleseditor import RulesEditorDialog from opensnitch.dialogs.processdetails import ProcessDetailsDialog from opensnitch.dialogs.conndetails import ConnDetails from opensnitch.customwidgets.colorizeddelegate import ColorizedDelegate from opensnitch.customwidgets.firewalltableview import FirewallTableModel from opensnitch.customwidgets.generictableview import GenericTableModel from opensnitch.customwidgets.addresstablemodel import AddressTableModel from opensnitch.utils import Message, QuickHelp, AsnDB, Icons from opensnitch.utils.xdg import xdg_current_desktop from opensnitch.actions import Actions from opensnitch.rules import Rule, Rules import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() DIALOG_UI_PATH = "%s/../res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__) class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): _trigger = QtCore.pyqtSignal(bool, bool) settings_saved = QtCore.pyqtSignal() close_trigger = QtCore.pyqtSignal() _status_changed_trigger = QtCore.pyqtSignal(bool) _shown_trigger = QtCore.pyqtSignal() _notification_trigger = QtCore.pyqtSignal(ui_pb2.Notification) _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) SORT_ORDER = ["ASC", "DESC"] LIMITS = ["LIMIT 50", "LIMIT 100", "LIMIT 200", "LIMIT 300", ""] LAST_GROUP_BY = "" # general COL_TIME = 0 COL_NODE = 1 COL_ACTION = 2 COL_SRCPORT = 3 COL_SRCIP = 4 COL_DSTIP = 5 COL_DSTHOST = 6 COL_DSTPORT = 7 COL_PROTO = 8 COL_UID = 9 COL_PID = 10 COL_PROCS = 11 COL_CMDLINE = 12 COL_RULES = 13 # total number of columns: cols + 1 GENERAL_COL_NUM = 14 # stats COL_WHAT = 0 # rules COL_R_NODE = 1 COL_R_NAME = 2 COL_R_ENABLED = 3 COL_R_ACTION = 4 COL_R_DURATION = 5 COL_R_OP_TYPE = 6 COL_R_OP_OPERAND = 7 COL_R_CREATED = 8 # procs COL_PROC_PID = 11 TAB_MAIN = 0 TAB_NODES = 1 TAB_RULES = 2 TAB_HOSTS = 3 TAB_PROCS = 4 TAB_ADDRS = 5 TAB_PORTS = 6 TAB_USERS = 7 TAB_FIREWALL = 8 # tree's top level items RULES_TREE_APPS = 0 RULES_TREE_NODES = 1 RULES_TREE_FIREWALL = 2 RULES_TREE_PERMANENT = 0 RULES_TREE_TEMPORARY = 1 RULES_COMBO_PERMANENT = 1 RULES_COMBO_TEMPORARY = 2 RULES_COMBO_FW = 3 RULES_TYPE_PERMANENT = 0 RULES_TYPE_TEMPORARY = 1 FILTER_TREE_APPS = 0 FILTER_TREE_NODES = 3 FILTER_TREE_FW_NODE = 0 FILTER_TREE_FW_TABLE = 1 FILTER_TREE_FW_CHAIN = 2 # FIXME: don't translate, used only for default argument on _update_status_label FIREWALL_DISABLED = "Disabled" # if the user clicks on an item of a table, it'll enter into the detail # view. From there, deny further clicks on the items. IN_DETAIL_VIEW = { TAB_MAIN: False, TAB_NODES: False, TAB_RULES: False, TAB_HOSTS: False, TAB_PROCS: False, TAB_ADDRS: False, TAB_PORTS: False, TAB_USERS: False, TAB_FIREWALL: False } # restore scrollbar position when going back from a detail view LAST_SCROLL_VALUE = None # try to restore last selection LAST_SELECTED_ITEM = "" TABLES = { TAB_MAIN: { "name": "connections", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "commonDelegateConfig", "display_fields": "time as Time, " \ "node, " \ "action, " \ "src_port, " \ "src_ip, " \ "dst_ip, " \ "dst_host, " \ "dst_port, " \ "protocol, " \ "uid, " \ "pid, " \ "process, " \ "process_args, " \ "rule", "group_by": LAST_GROUP_BY, "last_order_by": "1", "last_order_to": 1, "tracking_column:": COL_TIME }, TAB_NODES: { "name": "nodes", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "commonDelegateConfig", "display_fields": "last_connection as LastConnection, "\ "addr as Addr, " \ "status as Status, " \ "hostname as Hostname, " \ "daemon_version as Version, " \ "daemon_uptime as Uptime, " \ "daemon_rules as Rules," \ "cons as Connections," \ "cons_dropped as Dropped," \ "version as Version", "header_labels": [], "last_order_by": "1", "last_order_to": 1, "tracking_column:": COL_TIME }, TAB_RULES: { "name": "rules", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "defaultRulesDelegateConfig", "display_fields": "time as Time," \ "node as Node," \ "name as Name," \ "enabled as Enabled," \ "action as Action," \ "duration as Duration," \ "description as Description, " \ "created as Created", "header_labels": [], "last_order_by": "2", "last_order_to": 0, "tracking_column:": COL_R_NAME }, TAB_FIREWALL: { "name": "firewall", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "defaultFWDelegateConfig", "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 0, "tracking_column:": COL_TIME }, TAB_HOSTS: { "name": "hosts", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "commonDelegateConfig", "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "tracking_column:": COL_TIME }, TAB_PROCS: { "name": "procs", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "commonDelegateConfig", "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "tracking_column:": COL_TIME }, TAB_ADDRS: { "name": "addrs", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "commonDelegateConfig", "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "tracking_column:": COL_TIME }, TAB_PORTS: { "name": "ports", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "commonDelegateConfig", "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "tracking_column:": COL_TIME }, TAB_USERS: { "name": "users", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": "commonDelegateConfig", "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "tracking_column:": COL_TIME } } def __init__(self, parent=None, address=None, db=None, dbname="db", appicon=None): super(StatsDialog, self).__init__(parent) self.setWindowFlags(QtCore.Qt.Window) self.setupUi(self) self.setWindowIcon(appicon) # columns names. Must be added here in order to names be translated. self.COL_STR_NAME = QC.translate("stats", "Name", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_ADDR = QC.translate("stats", "Address", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_STATUS = QC.translate("stats", "Status", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_HOSTNAME = QC.translate("stats", "Hostname", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_UPTIME = QC.translate("stats", "Uptime", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_VERSION = QC.translate("stats", "Version", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_RULES_NUM = QC.translate("stats", "Rules", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_TIME = QC.translate("stats", "Time", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_CREATED = QC.translate("stats", "Created", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_ACTION = QC.translate("stats", "Action", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_DURATION = QC.translate("stats", "Duration", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_DESCRIPTION = QC.translate("stats", "Description", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_NODE = QC.translate("stats", "Node", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_ENABLED = QC.translate("stats", "Enabled", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_PRECEDENCE = QC.translate("stats", "Precedence", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_HITS = QC.translate("stats", "Hits", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_PROTOCOL = QC.translate("stats", "Protocol", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_PROCESS = QC.translate("stats", "Process", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_PROC_CMDLINE = QC.translate("stats", "Cmdline", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_DESTINATION = QC.translate("stats", "Destination", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_SRC_PORT = QC.translate("stats", "SrcPort", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_SRC_IP = QC.translate("stats", "SrcIP", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_DST_IP = QC.translate("stats", "DstIP", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_DST_HOST = QC.translate("stats", "DstHost", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_DST_PORT = QC.translate("stats", "DstPort", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_RULE = QC.translate("stats", "Rule", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_UID = QC.translate("stats", "UserID", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_PID = QC.translate("stats", "PID", "This is a word, without spaces and symbols.").replace(" ", "") self.COL_STR_LAST_CONNECTION = QC.translate("stats", "LastConnection", "This is a word, without spaces and symbols.").replace(" ", "") self.FIREWALL_STOPPED = QC.translate("stats", "Not running") self.FIREWALL_DISABLED = QC.translate("stats", "Disabled") self.FIREWALL_RUNNING = QC.translate("stats", "Running") self._db = db self._db_sqlite = self._db.get_db() self._db_name = dbname self.asndb = AsnDB.instance() self._cfg = Config.get() self._nodes = Nodes.instance() self._fw = Firewall().instance() self._rules = Rules.instance() self._fw.rules.rulesUpdated.connect(self._cb_fw_rules_updated) self._rules.updated.connect(self._cb_app_rules_updated) self._actions = Actions().instance() self._actions.loadAll() self._last_update = datetime.datetime.now() # TODO: allow to display multiples dialogs self._proc_details_dialog = ProcessDetailsDialog(appicon=appicon) # TODO: allow to navigate records by offsets self.prevButton.setVisible(False) self.nextButton.setVisible(False) self.fwTable.setVisible(False) self.rulesTable.setVisible(True) self.daemon_connected = False # skip table updates if a context menu is active self._context_menu_active = False # used to skip updates while the user is moving the scrollbar self.scrollbar_active = False self._lock = threading.RLock() self._address = address self._stats = None self._notifications_sent = {} self._fw_dialog = FirewallDialog(appicon=appicon) self._prefs_dialog = PreferencesDialog(appicon=appicon) self._rules_dialog = RulesEditorDialog(appicon=appicon) self._prefs_dialog.saved.connect(self._on_settings_saved) self._trigger.connect(self._on_update_triggered) self._notification_callback.connect(self._cb_notification_callback) self.nodeLabel.setText("") self.nodeLabel.setStyleSheet('color: green;font-size:12pt; font-weight:600;') self.rulesSplitter.setStretchFactor(0,0) self.rulesSplitter.setStretchFactor(1,2) self.rulesTreePanel.resizeColumnToContents(0) self.rulesTreePanel.resizeColumnToContents(1) self.rulesTreePanel.itemExpanded.connect(self._cb_rules_tree_item_expanded) self.startButton.clicked.connect(self._cb_start_clicked) self.nodeStartButton.clicked.connect(self._cb_node_start_clicked) self.nodeStartButton.setVisible(False) self.nodePrefsButton.setVisible(False) self.nodeActionsButton.setVisible(False) self.nodeDeleteButton.setVisible(False) self.nodeDeleteButton.clicked.connect(self._cb_node_delete_clicked) self.prefsButton.clicked.connect(self._cb_prefs_clicked) self.nodePrefsButton.clicked.connect(self._cb_node_prefs_clicked) self.fwButton.clicked.connect(lambda: self._fw_dialog.show()) self.comboAction.currentIndexChanged.connect(self._cb_combo_action_changed) self.limitCombo.currentIndexChanged.connect(self._cb_limit_combo_changed) self.tabWidget.currentChanged.connect(self._cb_tab_changed) self.delRuleButton.clicked.connect(self._cb_del_rule_clicked) self.rulesSplitter.splitterMoved.connect(self._cb_rules_splitter_moved) self.rulesTreePanel.itemClicked.connect(self._cb_rules_tree_item_clicked) self.rulesTreePanel.itemDoubleClicked.connect(self._cb_rules_tree_item_double_clicked) self.enableRuleCheck.clicked.connect(self._cb_enable_rule_toggled) self.editRuleButton.clicked.connect(self._cb_edit_rule_clicked) self.newRuleButton.clicked.connect(self._cb_new_rule_clicked) self.cmdProcDetails.clicked.connect(self._cb_proc_details_clicked) self.comboRulesFilter.currentIndexChanged.connect(self._cb_rules_filter_combo_changed) self.helpButton.clicked.connect(self._cb_help_button_clicked) self.nextButton.clicked.connect(self._cb_next_button_clicked) self.prevButton.clicked.connect(self._cb_prev_button_clicked) self.enableRuleCheck.setVisible(False) self.delRuleButton.setVisible(False) self.editRuleButton.setVisible(False) self.nodeRuleLabel.setVisible(False) self.comboRulesFilter.setVisible(False) menu = QtWidgets.QMenu() menu.addAction(Icons.new(self, "go-up"), QC.translate("stats", "Export rules")).triggered.connect(self._on_menu_node_export_clicked) menu.addAction(Icons.new(self, "go-down"), QC.translate("stats", "Import rules")).triggered.connect(self._on_menu_node_import_clicked) self.nodeActionsButton.setMenu(menu) menuActions = QtWidgets.QMenu() menuActions.addAction(Icons.new(self, "go-up"), QC.translate("stats", "Export rules")).triggered.connect(self._on_menu_export_clicked) menuActions.addAction(Icons.new(self, "go-down"), QC.translate("stats", "Import rules")).triggered.connect(self._on_menu_import_clicked) menuActions.addAction(Icons.new(self, "document-save"), QC.translate("stats", "Export events to CSV")).triggered.connect(self._on_menu_export_csv_clicked) menuActions.addAction(Icons.new(self, "application-exit"), QC.translate("stats", "Quit")).triggered.connect(self._on_menu_exit_clicked) self.actionsButton.setMenu(menuActions) # translations must be done here, otherwise they don't take effect self.TABLES[self.TAB_NODES]['header_labels'] = [ self.COL_STR_LAST_CONNECTION, self.COL_STR_ADDR, self.COL_STR_STATUS, self.COL_STR_HOSTNAME, self.COL_STR_VERSION, self.COL_STR_UPTIME, QC.translate("stats", "Rules", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Connections", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Dropped", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Version", "This is a word, without spaces and symbols.").replace(" ", ""), ] self.TABLES[self.TAB_RULES]['header_labels'] = [ self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_NAME, self.COL_STR_ENABLED, self.COL_STR_ACTION, self.COL_STR_DURATION, self.COL_STR_DESCRIPTION, self.COL_STR_CREATED, ] stats_headers = [ QC.translate("stats", "What", "This is a word, without spaces and symbols.").replace(" ", ""), QC.translate("stats", "Hits", "This is a word, without spaces and symbols.").replace(" ", ""), ] self.TABLES[self.TAB_HOSTS]['header_labels'] = stats_headers self.TABLES[self.TAB_PROCS]['header_labels'] = stats_headers self.TABLES[self.TAB_ADDRS]['header_labels'] = stats_headers self.TABLES[self.TAB_PORTS]['header_labels'] = stats_headers self.TABLES[self.TAB_USERS]['header_labels'] = stats_headers self.TABLES[self.TAB_MAIN]['view'] = self._setup_table(QtWidgets.QTableView, self.eventsTable, "connections", self.TABLES[self.TAB_MAIN]['display_fields'], order_by="1", group_by=self.TABLES[self.TAB_MAIN]['group_by'], delegate=self.TABLES[self.TAB_MAIN]['delegate'], resize_cols=(), model=GenericTableModel("connections", [ self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_ACTION, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_IP, self.COL_STR_DST_HOST, self.COL_STR_DST_PORT, self.COL_STR_PROTOCOL, self.COL_STR_UID, self.COL_STR_PID, self.COL_STR_PROCESS, self.COL_STR_PROC_CMDLINE, self.COL_STR_RULE, ]), verticalScrollBar=self.connectionsTableScrollBar, limit=self._get_limit() ) self.TABLES[self.TAB_NODES]['view'] = self._setup_table(QtWidgets.QTableView, self.nodesTable, "nodes", self.TABLES[self.TAB_NODES]['display_fields'], order_by="3,2,1", resize_cols=(self.COL_NODE,), model=GenericTableModel("nodes", self.TABLES[self.TAB_NODES]['header_labels']), verticalScrollBar=self.verticalScrollBar, sort_direction=self.SORT_ORDER[1], delegate=self.TABLES[self.TAB_NODES]['delegate']) self.TABLES[self.TAB_RULES]['view'] = self._setup_table(QtWidgets.QTableView, self.rulesTable, "rules", fields=self.TABLES[self.TAB_RULES]['display_fields'], model=GenericTableModel("rules", self.TABLES[self.TAB_RULES]['header_labels']), verticalScrollBar=self.rulesScrollBar, delegate=self.TABLES[self.TAB_RULES]['delegate'], order_by="2", sort_direction=self.SORT_ORDER[0], tracking_column=self.COL_R_NAME) self.TABLES[self.TAB_FIREWALL]['view'] = self._setup_table(QtWidgets.QTableView, self.fwTable, "firewall", model=FirewallTableModel("firewall"), verticalScrollBar=None, delegate=self.TABLES[self.TAB_FIREWALL]['delegate'], order_by="2", sort_direction=self.SORT_ORDER[0]) self.TABLES[self.TAB_HOSTS]['view'] = self._setup_table(QtWidgets.QTableView, self.hostsTable, "hosts", model=GenericTableModel("hosts", self.TABLES[self.TAB_HOSTS]['header_labels']), verticalScrollBar=self.hostsScrollBar, resize_cols=(self.COL_WHAT,), delegate=self.TABLES[self.TAB_HOSTS]['delegate'], order_by="2", limit=self._get_limit() ) self.TABLES[self.TAB_PROCS]['view'] = self._setup_table(QtWidgets.QTableView, self.procsTable, "procs", model=GenericTableModel("procs", self.TABLES[self.TAB_PROCS]['header_labels']), verticalScrollBar=self.procsScrollBar, resize_cols=(self.COL_WHAT,), delegate=self.TABLES[self.TAB_PROCS]['delegate'], order_by="2", limit=self._get_limit() ) self.TABLES[self.TAB_ADDRS]['view'] = self._setup_table(QtWidgets.QTableView, self.addrTable, "addrs", model=AddressTableModel("addrs", self.TABLES[self.TAB_ADDRS]['header_labels']), verticalScrollBar=self.addrsScrollBar, resize_cols=(self.COL_WHAT,), delegate=self.TABLES[self.TAB_ADDRS]['delegate'], order_by="2", limit=self._get_limit() ) self.TABLES[self.TAB_PORTS]['view'] = self._setup_table(QtWidgets.QTableView, self.portsTable, "ports", model=GenericTableModel("ports", self.TABLES[self.TAB_PORTS]['header_labels']), verticalScrollBar=self.portsScrollBar, resize_cols=(self.COL_WHAT,), delegate=self.TABLES[self.TAB_PORTS]['delegate'], order_by="2", limit=self._get_limit() ) self.TABLES[self.TAB_USERS]['view'] = self._setup_table(QtWidgets.QTableView, self.usersTable, "users", model=GenericTableModel("users", self.TABLES[self.TAB_USERS]['header_labels']), verticalScrollBar=self.usersScrollBar, resize_cols=(self.COL_WHAT,), delegate=self.TABLES[self.TAB_USERS]['delegate'], order_by="2", limit=self._get_limit() ) self.TABLES[self.TAB_NODES]['label'] = self.nodesLabel self.TABLES[self.TAB_RULES]['label'] = self.ruleLabel self.TABLES[self.TAB_HOSTS]['label'] = self.hostsLabel self.TABLES[self.TAB_PROCS]['label'] = self.procsLabel self.TABLES[self.TAB_ADDRS]['label'] = self.addrsLabel self.TABLES[self.TAB_PORTS]['label'] = self.portsLabel self.TABLES[self.TAB_USERS]['label'] = self.usersLabel self.TABLES[self.TAB_NODES]['cmd'] = self.cmdNodesBack self.TABLES[self.TAB_RULES]['cmd'] = self.cmdRulesBack self.TABLES[self.TAB_HOSTS]['cmd'] = self.cmdHostsBack self.TABLES[self.TAB_PROCS]['cmd'] = self.cmdProcsBack self.TABLES[self.TAB_ADDRS]['cmd'] = self.cmdAddrsBack self.TABLES[self.TAB_PORTS]['cmd'] = self.cmdPortsBack self.TABLES[self.TAB_USERS]['cmd'] = self.cmdUsersBack self.TABLES[self.TAB_MAIN]['cmdCleanStats'] = self.cmdCleanSql self.TABLES[self.TAB_NODES]['cmdCleanStats'] = self.cmdCleanSql self.TABLES[self.TAB_RULES]['cmdCleanStats'] = self.cmdCleanSql self.TABLES[self.TAB_HOSTS]['cmdCleanStats'] = self.cmdCleanSql self.TABLES[self.TAB_PROCS]['cmdCleanStats'] = self.cmdCleanSql self.TABLES[self.TAB_ADDRS]['cmdCleanStats'] = self.cmdCleanSql self.TABLES[self.TAB_PORTS]['cmdCleanStats'] = self.cmdCleanSql self.TABLES[self.TAB_USERS]['cmdCleanStats'] = self.cmdCleanSql # the rules clean button is only for a particular rule, not all. self.TABLES[self.TAB_MAIN]['cmdCleanStats'].clicked.connect(lambda: self._cb_clean_sql_clicked(self.TAB_MAIN)) self.TABLES[self.TAB_MAIN]['filterLine'] = self.filterLine self.TABLES[self.TAB_MAIN]['view'].doubleClicked.connect(self._cb_main_table_double_clicked) self.TABLES[self.TAB_MAIN]['view'].installEventFilter(self) self.TABLES[self.TAB_MAIN]['filterLine'].textChanged.connect(self._cb_events_filter_line_changed) self.TABLES[self.TAB_MAIN]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.TABLES[self.TAB_MAIN]['view'].customContextMenuRequested.connect(self._cb_table_context_menu) self.TABLES[self.TAB_RULES]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.TABLES[self.TAB_RULES]['view'].customContextMenuRequested.connect(self._cb_table_context_menu) self.TABLES[self.TAB_FIREWALL]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.TABLES[self.TAB_FIREWALL]['view'].customContextMenuRequested.connect(self._cb_table_context_menu) for idx in range(1,9): if self.TABLES[idx]['cmd'] != None: self.TABLES[idx]['cmd'].hide() self.TABLES[idx]['cmd'].setVisible(False) self.TABLES[idx]['cmd'].clicked.connect(lambda: self._cb_cmd_back_clicked(idx)) if self.TABLES[idx]['cmdCleanStats'] != None: self.TABLES[idx]['cmdCleanStats'].clicked.connect(lambda: self._cb_clean_sql_clicked(idx)) if self.TABLES[idx]['label'] != None: self.TABLES[idx]['label'].setStyleSheet('font-weight:600;') self.TABLES[idx]['label'].setVisible(False) self.TABLES[idx]['view'].doubleClicked.connect(self._cb_table_double_clicked) self.TABLES[idx]['view'].installEventFilter(self) self.TABLES[self.TAB_FIREWALL]['view'].rowsReordered.connect(self._cb_fw_table_rows_reordered) self._load_settings() self._tables = ( \ self.TABLES[self.TAB_MAIN]['view'], self.TABLES[self.TAB_NODES]['view'], self.TABLES[self.TAB_RULES]['view'], self.TABLES[self.TAB_HOSTS]['view'], self.TABLES[self.TAB_PROCS]['view'], self.TABLES[self.TAB_ADDRS]['view'], self.TABLES[self.TAB_PORTS]['view'], self.TABLES[self.TAB_USERS]['view'] ) self._file_names = ( \ 'events.csv', 'nodes.csv', 'rules.csv', 'hosts.csv', 'procs.csv', 'addrs.csv', 'ports.csv', 'users.csv' ) self.iconStart = Icons.new(self, "media-playback-start") self.iconPause = Icons.new(self, "media-playback-pause") self.fwTreeEdit = QtWidgets.QPushButton() self.fwTreeEdit.setIcon(QtGui.QIcon().fromTheme("preferences-desktop")) self.fwTreeEdit.autoFillBackground = True self.fwTreeEdit.setFlat(True) self.fwTreeEdit.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) ) self.fwTreeEdit.clicked.connect(self._cb_tree_edit_firewall_clicked) self._configure_buttons_icons() #Sometimes a maximized window which had been minimized earlier won't unminimize #To workaround, we explicitely maximize such windows when unminimizing happens def changeEvent(self, event): if event.type() == QtCore.QEvent.WindowStateChange: if event.oldState() & QtCore.Qt.WindowMinimized and event.oldState() & QtCore.Qt.WindowMaximized: #a previously minimized maximized window ... if self.windowState() ^ QtCore.Qt.WindowMinimized and xdg_current_desktop == "KDE": # is not minimized anymore, i.e. it was unminimized # docs: https://doc.qt.io/qt-5/qwidget.html#setWindowState self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) def showEvent(self, event): super(StatsDialog, self).showEvent(event) self._shown_trigger.emit() window_title = QC.translate("stats", "OpenSnitch Network Statistics {0}").format(version) if self._address is not None: window_title = QC.translate("stats", "OpenSnitch Network Statistics for {0}").format(self._address) self.nodeLabel.setText(self._address) self._load_settings() self._add_rulesTree_nodes() self._add_rulesTree_fw_chains() self.setWindowTitle(window_title) self._refresh_active_table() self._show_columns() def eventFilter(self, source, event): if event.type() == QtCore.QEvent.KeyPress: if event.matches(QtGui.QKeySequence.Copy): self._copy_selected_rows() return True elif event.key() == QtCore.Qt.Key_Delete: table = self._get_active_table() selection = table.selectionModel().selectedRows() if selection: model = table.model() self._table_menu_delete(self.tabWidget.currentIndex(), model, selection) # we need to manually refresh the model table.selectionModel().clear() self._refresh_active_table() return True return super(StatsDialog, self).eventFilter(source, event) def _configure_buttons_icons(self): newRuleIcon = Icons.new(self, "document-new") delRuleIcon = Icons.new(self, "edit-delete") editRuleIcon = Icons.new(self, "accessories-text-editor") prefsIcon = Icons.new(self, "preferences-system") searchIcon = Icons.new(self, "system-search") clearIcon = Icons.new(self, "edit-clear-all") leftArrowIcon = Icons.new(self, "go-previous") fwIcon = Icons.new(self, "security-high") optsIcon = Icons.new(self, "format-justify-fill") helpIcon = Icons.new(self, "help-browser") eventsIcon = Icons.new(self, "view-sort-ascending") rulesIcon = Icons.new(self, "address-book-new") procsIcon = Icons.new(self, "system-run") if QtGui.QIcon().hasThemeIcon("preferences-desktop") == False: self.fwTreeEdit.setText("+") self.tabWidget.setTabIcon(self.TAB_MAIN, eventsIcon) self.tabWidget.setTabIcon(self.TAB_RULES, rulesIcon) self.tabWidget.setTabIcon(self.TAB_PROCS, procsIcon) self.newRuleButton.setIcon(newRuleIcon) self.delRuleButton.setIcon(delRuleIcon) self.editRuleButton.setIcon(editRuleIcon) self.prefsButton.setIcon(prefsIcon) self.helpButton.setIcon(helpIcon) self.startButton.setIcon(self.iconStart) self.fwButton.setIcon(fwIcon) self.cmdProcDetails.setIcon(searchIcon) self.nodeStartButton.setIcon(self.iconStart) self.nodePrefsButton.setIcon(prefsIcon) self.nodeDeleteButton.setIcon(clearIcon) self.nodeActionsButton.setIcon(optsIcon) self.actionsButton.setIcon(optsIcon) self.TABLES[self.TAB_MAIN]['cmdCleanStats'].setIcon(clearIcon) for idx in range(1,8): self.TABLES[idx]['cmd'].setIcon(leftArrowIcon) if self.TABLES[idx]['cmdCleanStats'] != None: self.TABLES[idx]['cmdCleanStats'].setIcon(clearIcon) def _load_settings(self): self._ui_refresh_interval = self._cfg.getInt(Config.STATS_REFRESH_INTERVAL, 0) dialog_geometry = self._cfg.getSettings(Config.STATS_GEOMETRY) dialog_last_tab = self._cfg.getSettings(Config.STATS_LAST_TAB) dialog_general_filter_text = self._cfg.getSettings(Config.STATS_FILTER_TEXT) dialog_general_filter_action = self._cfg.getSettings(Config.STATS_FILTER_ACTION) dialog_general_limit_results = self._cfg.getSettings(Config.STATS_LIMIT_RESULTS) if dialog_geometry != None: self.restoreGeometry(dialog_geometry) if dialog_last_tab != None: self.tabWidget.setCurrentIndex(int(dialog_last_tab)) if dialog_general_filter_action != None: self.comboAction.setCurrentIndex(int(dialog_general_filter_action)) if dialog_general_limit_results != None: # XXX: a little hack, because if the saved index is 0, the signal is not fired. # XXX: this causes to fire the event twice self.limitCombo.blockSignals(True); self.limitCombo.setCurrentIndex(4) self.limitCombo.setCurrentIndex(int(dialog_general_limit_results)) self.limitCombo.blockSignals(False); rules_splitter_pos = self._cfg.getSettings(Config.STATS_RULES_SPLITTER_POS) if type(rules_splitter_pos) == QtCore.QByteArray: self.rulesSplitter.restoreState(rules_splitter_pos) rulesSizes = self.rulesSplitter.sizes() if self.IN_DETAIL_VIEW[self.TAB_RULES] == True: self.comboRulesFilter.setVisible(False) elif len(rulesSizes) > 0: self.comboRulesFilter.setVisible(rulesSizes[0] == 0) else: w = self.rulesSplitter.width() self.rulesSplitter.setSizes([int(w/3), int(w/2)]) self._restore_details_view_columns(self.eventsTable.horizontalHeader(), Config.STATS_GENERAL_COL_STATE) self._restore_details_view_columns(self.nodesTable.horizontalHeader(), Config.STATS_NODES_COL_STATE) self._restore_details_view_columns(self.rulesTable.horizontalHeader(), Config.STATS_RULES_COL_STATE) self._restore_details_view_columns(self.fwTable.horizontalHeader(), Config.STATS_FW_COL_STATE) rulesTreeNodes_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_1) if rulesTreeNodes_expanded != None: rules_tree_nodes = self._get_rulesTree_item(self.RULES_TREE_NODES) if rules_tree_nodes != None: rules_tree_nodes.setExpanded(rulesTreeNodes_expanded) rulesTreeApps_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_0) if rulesTreeApps_expanded != None: rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS) if rules_tree_apps != None: rules_tree_apps.setExpanded(rulesTreeApps_expanded) if dialog_general_filter_text != None: self.filterLine.setText(dialog_general_filter_text) def _save_settings(self): self._cfg.setSettings(Config.STATS_GEOMETRY, self.saveGeometry()) self._cfg.setSettings(Config.STATS_LAST_TAB, self.tabWidget.currentIndex()) self._cfg.setSettings(Config.STATS_LIMIT_RESULTS, self.limitCombo.currentIndex()) self._cfg.setSettings(Config.STATS_FILTER_TEXT, self.filterLine.text()) header = self.eventsTable.horizontalHeader() self._cfg.setSettings(Config.STATS_GENERAL_COL_STATE, header.saveState()) nodesHeader = self.nodesTable.horizontalHeader() self._cfg.setSettings(Config.STATS_NODES_COL_STATE, nodesHeader.saveState()) rulesHeader = self.rulesTable.horizontalHeader() self._cfg.setSettings(Config.STATS_RULES_COL_STATE, rulesHeader.saveState()) fwHeader = self.fwTable.horizontalHeader() self._cfg.setSettings(Config.STATS_FW_COL_STATE, fwHeader.saveState()) rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS) if rules_tree_apps != None: self._cfg.setSettings(Config.STATS_RULES_TREE_EXPANDED_0, rules_tree_apps.isExpanded()) rules_tree_nodes = self._get_rulesTree_item(self.RULES_TREE_NODES) if rules_tree_nodes != None: self._cfg.setSettings(Config.STATS_RULES_TREE_EXPANDED_1, rules_tree_nodes.isExpanded()) def _del_by_field(self, cur_idx, table, value): model = self._get_active_table().model() # get left side of the query: * GROUP BY ... qstr = model.query().lastQuery().split("GROUP BY")[0] # get right side of the query: ... WHERE * q = qstr.split("WHERE") field = "dst_host" if cur_idx == self.TAB_NODES: field = "node" elif cur_idx == self.TAB_PROCS: field = "process" elif cur_idx == self.TAB_ADDRS: field = "dst_ip" elif cur_idx == self.TAB_PORTS: field = "dst_port" elif cur_idx == self.TAB_USERS: field = "uid" ret1 = self._db.remove("DELETE FROM {0} WHERE what = '{1}'".format(table, value)) ret2 = self._db.remove("DELETE FROM connections WHERE {0} = '{1}'".format(field, value)) return ret1 and ret2 def _del_rule(self, rule_name, node_addr): if rule_name == None or node_addr == None: print("_del_rule() invalid parameters") return nid, noti = self._nodes.delete_rule(rule_name, node_addr, self._notification_callback) if nid == None: return self._notifications_sent[nid] = noti # https://stackoverflow.com/questions/40225270/copy-paste-multiple-items-from-qtableview-in-pyqt4 def _copy_selected_rows(self): cur_idx = self.tabWidget.currentIndex() if self.tabWidget.currentIndex() == self.TAB_RULES and self.fwTable.isVisible(): cur_idx = self.TAB_FIREWALL elif self.tabWidget.currentIndex() == self.TAB_RULES and not self.fwTable.isVisible(): cur_idx = self.TAB_RULES selection = self.TABLES[cur_idx]['view'].copySelection() if selection: stream = io.StringIO() csv.writer(stream, delimiter=',').writerows(selection) QtWidgets.qApp.clipboard().setText(stream.getvalue()) def _configure_events_contextual_menu(self, pos): try: cur_idx = self.tabWidget.currentIndex() table = self._get_active_table() model = table.model() selection = table.selectionModel().selectedRows() if not selection: return False menu = QtWidgets.QMenu() _menu_details = menu.addAction(QC.translate("stats", "Details")) rulesMenu = QtWidgets.QMenu(QC.translate("stats", "Rules")) _menu_new_rule = rulesMenu.addAction(QC.translate("stats", "New")) menu.addMenu(rulesMenu) # move away menu a few pixels to the right, to avoid clicking on it by mistake point = QtCore.QPoint(pos.x()+10, pos.y()+5) action = menu.exec_(table.mapToGlobal(point)) model = table.model() if action == _menu_new_rule: self._table_menu_new_rule_from_row(cur_idx, model, selection) elif action == _menu_details: coltime = model.index(selection[0].row(), self.COL_TIME).data() o = ConnDetails(self) o.showByField("time", coltime) except Exception as e: print(e) finally: self._clear_rows_selection() return True def _configure_fwrules_contextual_menu(self, pos): try: cur_idx = self.tabWidget.currentIndex() table = self._get_active_table() model = table.model() menu = QtWidgets.QMenu() exportMenu = QtWidgets.QMenu(QC.translate("stats", "Export")) selection = table.selectionModel().selectedRows() if not selection: return False is_rule_enabled = model.index(selection[0].row(), FirewallTableModel.COL_ENABLED).data() rule_action = model.index(selection[0].row(), FirewallTableModel.COL_ACTION).data() rule_action = rule_action.lower() if rule_action == Config.ACTION_ACCEPT or \ rule_action == Config.ACTION_DROP or \ rule_action == Config.ACTION_RETURN or \ rule_action == Config.ACTION_REJECT: actionsMenu = QtWidgets.QMenu(QC.translate("stats", "Action")) _action_accept = actionsMenu.addAction(Config.ACTION_ACCEPT) _action_drop = actionsMenu.addAction(Config.ACTION_DROP) _action_reject = actionsMenu.addAction(Config.ACTION_REJECT) _action_return = actionsMenu.addAction(Config.ACTION_RETURN) menu.addSeparator() menu.addMenu(actionsMenu) _label_enable = QC.translate("stats", "Disable") if is_rule_enabled == "False": _label_enable = QC.translate("stats", "Enable") _menu_enable = menu.addAction(_label_enable) _menu_delete = menu.addAction(QC.translate("stats", "Delete")) _menu_edit = menu.addAction(QC.translate("stats", "Edit")) menu.addSeparator() _toClipboard = exportMenu.addAction(QC.translate("stats", "To clipboard")) #_toDisk = exportMenu.addAction(QC.translate("stats", "To disk")) menu.addMenu(exportMenu) # move away menu a few pixels to the right, to avoid clicking on it by mistake point = QtCore.QPoint(pos.x()+10, pos.y()+5) action = menu.exec_(table.mapToGlobal(point)) model = table.model() # block fw rules signals, to prevent reloading them per operation, # which can lead to race conditions. self._fw.rules.blockSignals(True) if action == _menu_delete: self._table_menu_delete(cur_idx, model, selection) elif action == _menu_enable: self._table_menu_enable(cur_idx, model, selection, is_rule_enabled) elif action == _menu_edit: self._table_menu_edit(cur_idx, model, selection) elif action == _action_accept or \ action == _action_drop or \ action == _action_reject or \ action == _action_return: self._table_menu_change_rule_field(cur_idx, model, selection, FwRules.FIELD_TARGET, action.text()) elif action == _toClipboard: self._table_menu_export_clipboard(cur_idx, model, selection) #elif action == _toDisk: # self._table_menu_export_disk(cur_idx, model, selection) self._fw.rules.blockSignals(False) except Exception as e: print("fwrules contextual menu error:", e) finally: self._clear_rows_selection() return True def _configure_rules_contextual_menu(self, pos): try: cur_idx = self.tabWidget.currentIndex() table = self._get_active_table() model = table.model() selection = table.selectionModel().selectedRows() if not selection: return False menu = QtWidgets.QMenu() durMenu = QtWidgets.QMenu(self.COL_STR_DURATION) actionMenu = QtWidgets.QMenu(self.COL_STR_ACTION) nodesMenu = QtWidgets.QMenu(QC.translate("stats", "Apply to")) exportMenu = QtWidgets.QMenu(QC.translate("stats", "Export")) nodes_menu = [] if self._nodes.count() > 0: for node in self._nodes.get_nodes(): nodes_menu.append([nodesMenu.addAction(node), node]) menu.addMenu(nodesMenu) _actAllow = actionMenu.addAction(QC.translate("stats", "Allow")) _actDeny = actionMenu.addAction(QC.translate("stats", "Deny")) _actReject = actionMenu.addAction(QC.translate("stats", "Reject")) menu.addMenu(actionMenu) _durAlways = durMenu.addAction(QC.translate("stats", "Always")) _durUntilReboot = durMenu.addAction(QC.translate("stats", "Until reboot")) _dur1h = durMenu.addAction(Config.DURATION_1h) _dur30m = durMenu.addAction(Config.DURATION_30m) _dur15m = durMenu.addAction(Config.DURATION_15m) _dur5m = durMenu.addAction(Config.DURATION_5m) menu.addMenu(durMenu) is_rule_enabled = model.index(selection[0].row(), self.COL_R_ENABLED).data() menu_label_enable = QC.translate("stats", "Disable") if is_rule_enabled == "False": menu_label_enable = QC.translate("stats", "Enable") _menu_enable = menu.addAction(QC.translate("stats", menu_label_enable)) _menu_duplicate = menu.addAction(QC.translate("stats", "Duplicate")) _menu_edit = menu.addAction(QC.translate("stats", "Edit")) _menu_delete = menu.addAction(QC.translate("stats", "Delete")) menu.addSeparator() _toClipboard = exportMenu.addAction(QC.translate("stats", "To clipboard")) _toDisk = exportMenu.addAction(QC.translate("stats", "To disk")) menu.addMenu(exportMenu) # move away menu a few pixels to the right, to avoid clicking on it by mistake point = QtCore.QPoint(pos.x()+10, pos.y()+5) action = menu.exec_(table.mapToGlobal(point)) model = table.model() if self._nodes.count() > 0: for nmenu in nodes_menu: node_action = nmenu[0] node_addr = nmenu[1] if action == node_action: ret = Message.yes_no( QC.translate("stats", " Apply this rule to {0} ".format(node_addr)), QC.translate("stats", " Are you sure?"), QtWidgets.QMessageBox.Warning) if ret == QtWidgets.QMessageBox.Cancel: return False self._table_menu_apply_to_node(cur_idx, model, selection, node_addr) return False if action == _menu_delete: self._table_menu_delete(cur_idx, model, selection) elif action == _menu_edit: self._table_menu_edit(cur_idx, model, selection) elif action == _menu_enable: self._table_menu_enable(cur_idx, model, selection, is_rule_enabled) elif action == _menu_duplicate: self._table_menu_duplicate(cur_idx, model, selection) elif action == _durAlways: self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_ALWAYS) elif action == _dur1h: self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_1h) elif action == _dur30m: self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_30m) elif action == _dur15m: self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_15m) elif action == _dur5m: self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_5m) elif action == _durUntilReboot: self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_UNTIL_RESTART) elif action == _actAllow: self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_ALLOW) elif action == _actDeny: self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_DENY) elif action == _actReject: self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_REJECT) elif action == _toClipboard: self._table_menu_export_clipboard(cur_idx, model, selection) elif action == _toDisk: self._table_menu_export_disk(cur_idx, model, selection) except Exception as e: print("rules contextual menu exception:", e) finally: self._clear_rows_selection() return True def _table_menu_export_clipboard(self, cur_idx, model, selection): rules_list = [] if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): for idx in selection: rule_name = model.index(idx.row(), self.COL_R_NAME).data() node_addr = model.index(idx.row(), self.COL_R_NODE).data() json_rule = self._nodes.rule_to_json(node_addr, rule_name) if json_rule != None: rules_list.append(json_rule) else: print("export to clipboard: ERROR converting \"{0}\" to json".format(rule_name)) elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): for idx in selection: uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() r = self._fw.get_protorule_by_uuid(node, uuid) if r: rules_list.append(self._fw.rule_to_json(r)) cliptext="" for r in rules_list: cliptext = "{0}\n{1}".format(cliptext, r) QtWidgets.qApp.clipboard().setText(cliptext) def _table_menu_export_disk(self, cur_idx, model, selection): outdir = QtWidgets.QFileDialog.getExistingDirectory(self, os.path.expanduser("~"), QC.translate("stats", 'Select a directory to export rules'), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if outdir == "": return error_list = [] for idx in selection: rule_name = model.index(idx.row(), self.COL_R_NAME).data() node_addr = model.index(idx.row(), self.COL_R_NODE).data() ok = self._nodes.export_rule(node_addr, rule_name, outdir) if not ok: error_list.append(rule_name) if len(error_list) == 0: Message.ok("Rules export", QC.translate("stats", "Rules exported to {0}".format(outdir)), QtWidgets.QMessageBox.Information) else: error_text = "" for e in error_list: error_text = "{0}<br>{1}".format(error_text, e) Message.ok("Rules export error", QC.translate("stats", "Error exporting the following rules:<br><br>".format(error_text) ), QtWidgets.QMessageBox.Warning) def _table_menu_duplicate(self, cur_idx, model, selection): for idx in selection: rule_name = model.index(idx.row(), self.COL_R_NAME).data() node_addr = model.index(idx.row(), self.COL_R_NODE).data() records = None for idx in range(0,100): records = self._get_rule(rule_name, node_addr) if records == None or records.size() == -1: rule = Rule.new_from_records(records) rule.name = "cloned-{0}-{1}".format(idx, rule.name) self._rules.add_rules(node_addr, [rule]) break if records != None and records.size() == -1: noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule]) nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) if nid != None: self._notifications_sent[nid] = noti def _table_menu_apply_to_node(self, cur_idx, model, selection, node_addr): for idx in selection: rule_name = model.index(idx.row(), self.COL_R_NAME).data() records = self._get_rule(rule_name, None) rule = Rule.new_from_records(records) noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule]) nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) if nid != None: self._rules.add_rules(node_addr, [rule]) self._notifications_sent[nid] = noti def _table_menu_change_rule_field(self, cur_idx, model, selection, field, value): if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): for idx in selection: rule_name = model.index(idx.row(), self.COL_R_NAME).data() node_addr = model.index(idx.row(), self.COL_R_NODE).data() records = self._get_rule(rule_name, node_addr) rule = Rule.new_from_records(records) self._db.update(table="rules", fields="{0}=?".format(field), values=[value], condition="name='{0}' AND node='{1}'".format(rule_name, node_addr), action_on_conflict="") if field == "action": rule.action = value elif field == "duration": rule.duration = value elif field == "precedence": rule.precedence = value noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule]) nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) if nid != None: self._notifications_sent[nid] = noti elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): nodes_updated = [] for idx in selection: uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() updated, err = self._fw.change_rule_field(node, uuid, field, value) if updated: nodes_updated.append(node) else: print("error updating fw rule field", field, "value:", value) for addr in nodes_updated: node = self._nodes.get_node(addr) nid, noti = self._nodes.reload_fw(addr, node['firewall'], self._notification_callback) self._notifications_sent[nid] = noti def _table_menu_enable(self, cur_idx, model, selection, is_rule_enabled): rule_status = "False" if is_rule_enabled == "True" else "True" enable_rule = False if is_rule_enabled == "True" else True if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): for idx in selection: rule_name = model.index(idx.row(), self.COL_R_NAME).data() node_addr = model.index(idx.row(), self.COL_R_NODE).data() records = self._get_rule(rule_name, node_addr) rule = Rule.new_from_records(records) rule_type = ui_pb2.DISABLE_RULE if is_rule_enabled == "True" else ui_pb2.ENABLE_RULE self._db.update(table="rules", fields="enabled=?", values=[rule_status], condition="name='{0}' AND node='{1}'".format(rule_name, node_addr), action_on_conflict="") noti = ui_pb2.Notification(type=rule_type, rules=[rule]) nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) if nid != None: self._notifications_sent[nid] = noti elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): nodes_updated = [] for idx in selection: uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() updated, err = self._fw.enable_rule(node, uuid, enable_rule) if updated: nodes_updated.append(node) for addr in nodes_updated: node = self._nodes.get_node(addr) nid, noti = self._nodes.reload_fw(addr, node['firewall'], self._notification_callback) self._notifications_sent[nid] = noti def _table_menu_delete(self, cur_idx, model, selection): if cur_idx == self.TAB_MAIN or cur_idx == self.TAB_NODES or self.IN_DETAIL_VIEW[cur_idx]: return msg = QC.translate("stats", " You are about to delete this rule. ") if cur_idx != self.TAB_RULES: msg = QC.translate("stats", " You are about to delete this entry. ") ret = Message.yes_no(msg, QC.translate("stats", " Are you sure?"), QtWidgets.QMessageBox.Warning) if ret == QtWidgets.QMessageBox.Cancel: return False if cur_idx == self.TAB_RULES and self.fwTable.isVisible(): nodes_updated = {} for idx in selection: uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() ok, fw_config = self._fw.delete_rule(node, uuid) if ok: nodes_updated[node] = fw_config else: print("error deleting fw rule:", uuid, "row:", idx.row()) for addr in nodes_updated: nid, noti = self._nodes.reload_fw(addr, nodes_updated[addr], self._notification_callback) self._notifications_sent[nid] = noti elif cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): for idx in selection: name = model.index(idx.row(), self.COL_R_NAME).data() node = model.index(idx.row(), self.COL_R_NODE).data() self._del_rule(name, node) self._refresh_active_table() elif cur_idx == self.TAB_HOSTS or cur_idx == self.TAB_PROCS or cur_idx == self.TAB_ADDRS or \ cur_idx == self.TAB_USERS or cur_idx == self.TAB_PORTS: do_refresh = False for idx in selection: field = model.index(idx.row(), self.COL_WHAT).data() if field == "": continue ok = self._del_by_field(cur_idx, self.TABLES[cur_idx]['name'], field) do_refresh |= ok if do_refresh: self._refresh_active_table() def _table_menu_new_rule_from_row(self, cur_idx, model, selection): coltime = model.index(selection[0].row(), self.COL_TIME).data() if self._rules_dialog.new_rule_from_connection(coltime) == False: Message.ok(QC.translate("stats", "New rule error"), QC.translate("stats", "Error creating new rule from event ({0})".format(coltime) ), QtWidgets.QMessageBox.Warning) def _table_menu_edit(self, cur_idx, model, selection): if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): for idx in selection: name = model.index(idx.row(), self.COL_R_NAME).data() node = model.index(idx.row(), self.COL_R_NODE).data() records = self._get_rule(name, node) if records == None or records == -1: Message.ok(QC.translate("stats", "New rule error"), QC.translate("stats", "Rule not found by that name and node"), QtWidgets.QMessageBox.Warning) return self._rules_dialog.edit_rule(records, node) break elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): for idx in selection: uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() self._fw_dialog.load_rule(node, uuid) break def _cb_fw_rules_updated(self): self._add_rulesTree_fw_chains() def _cb_app_rules_updated(self, what): self._refresh_active_table() @QtCore.pyqtSlot(str) def _cb_fw_table_rows_reordered(self, node_addr): node = self._nodes.get_node(node_addr) nid, notif = self._nodes.reload_fw(node_addr, node['firewall'], self._notification_callback) self._notifications_sent[nid] = {'addr': node_addr, 'notif': notif} # ignore updates while the user is using the scrollbar. def _cb_scrollbar_pressed(self): self.scrollbar_active = True def _cb_scrollbar_released(self): self.scrollbar_active = False def _cb_tree_edit_firewall_clicked(self): self._fw_dialog.show() def _cb_proc_details_clicked(self): table = self._tables[self.tabWidget.currentIndex()] nrows = table.model().rowCount() pids = {} for row in range(0, nrows): pid = table.model().index(row, self.COL_PROC_PID).data() node = table.model().index(row, self.COL_NODE).data() if pid not in pids: pids[pid] = node self._proc_details_dialog.monitor(pids) @QtCore.pyqtSlot(ui_pb2.NotificationReply) def _cb_notification_callback(self, reply): if reply.id in self._notifications_sent: if reply.code == ui_pb2.ERROR: Message.ok( QC.translate("stats", "Error:"), "{0}".format(reply.data), QtWidgets.QMessageBox.Warning) del self._notifications_sent[reply.id] else: Message.ok( QC.translate("stats", "Warning:"), "{0}".format(reply.data), QtWidgets.QMessageBox.Warning) def _cb_tab_changed(self, index): self.comboAction.setVisible(index == self.TAB_MAIN) self.TABLES[index]['cmdCleanStats'].setVisible(True) if index == self.TAB_MAIN: self._set_events_query() else: if index == self.TAB_RULES: # display the clean buton only if not in detail view self.TABLES[index]['cmdCleanStats'].setVisible( self.IN_DETAIL_VIEW[index] ) self._add_rulesTree_nodes() elif index == self.TAB_PROCS: # make the button visible depending if we're in the detail view nrows = self._get_active_table().model().rowCount() self.cmdProcDetails.setVisible(self.IN_DETAIL_VIEW[index] and nrows > 0) elif index == self.TAB_NODES: self.TABLES[index]['cmdCleanStats'].setVisible( self.IN_DETAIL_VIEW[index] ) self._refresh_active_table() def _cb_table_context_menu(self, pos): cur_idx = self.tabWidget.currentIndex() if cur_idx != self.TAB_RULES and cur_idx != self.TAB_MAIN: # the only tables with context menu for now are events and rules table return if self.IN_DETAIL_VIEW[self.TAB_RULES] == True: return self._context_menu_active = True if cur_idx == self.TAB_MAIN: refresh_table = self._configure_events_contextual_menu(pos) elif cur_idx == self.TAB_RULES: if self.fwTable.isVisible(): refresh_table = self._configure_fwrules_contextual_menu(pos) else: refresh_table = self._configure_rules_contextual_menu(pos) self._context_menu_active = False if refresh_table: self._refresh_active_table() def _cb_table_header_clicked(self, pos, sortIdx): cur_idx = self.tabWidget.currentIndex() # TODO: allow ordering by Network column if cur_idx == self.TAB_ADDRS and pos == 2: return model = self._get_active_table().model() qstr = model.query().lastQuery().split("ORDER BY")[0] q = qstr.strip(" ") + " ORDER BY %d %s" % (pos+1, self.SORT_ORDER[sortIdx]) if cur_idx > 0 and self.TABLES[cur_idx]['cmd'].isVisible() == False: self.TABLES[cur_idx]['last_order_by'] = pos+1 self.TABLES[cur_idx]['last_order_to'] = sortIdx q = qstr.strip(" ") + self._get_order() q += self._get_limit() self.setQuery(model, q) def _cb_events_filter_line_changed(self, text): cur_idx = self.tabWidget.currentIndex() model = self.TABLES[cur_idx]['view'].model() qstr = None if cur_idx == StatsDialog.TAB_MAIN: self._cfg.setSettings(Config.STATS_FILTER_TEXT, text) self._set_events_query() return elif cur_idx == StatsDialog.TAB_NODES: qstr = self._get_nodes_filter_query(model.query().lastQuery(), text) elif cur_idx == StatsDialog.TAB_RULES and self.fwTable.isVisible(): self.TABLES[self.TAB_FIREWALL]['view'].filterByQuery(text) return elif self.IN_DETAIL_VIEW[cur_idx] == True: qstr = self._get_indetail_filter_query(model.query().lastQuery(), text) else: where_clause = self._get_filter_line_clause(cur_idx, text) qstr = self._db.get_query( self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['display_fields'] ) + \ where_clause + self._get_order() if text == "": qstr = qstr + self._get_limit() if qstr != None: self.setQuery(model, qstr) def _cb_limit_combo_changed(self, idx): if self.tabWidget.currentIndex() == self.TAB_MAIN: self._set_events_query() else: model = self._get_active_table().model() qstr = model.query().lastQuery() if "LIMIT" in qstr: qs = qstr.split(" LIMIT ") q = qs[0] l = qs[1] qstr = q + self._get_limit() else: qstr = qstr + self._get_limit() self.setQuery(model, qstr) def _cb_combo_action_changed(self, idx): if self.tabWidget.currentIndex() != self.TAB_MAIN: return self._cfg.setSettings(Config.STATS_GENERAL_FILTER_ACTION, idx) self._set_events_query() def _cb_clean_sql_clicked(self, idx): cur_idx = self.tabWidget.currentIndex() if self.tabWidget.currentIndex() == StatsDialog.TAB_RULES: self._db.empty_rule(self.TABLES[cur_idx]['label'].text()) elif self.IN_DETAIL_VIEW[cur_idx]: self._del_by_field(cur_idx, self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['label'].text()) else: self._db.clean(self.TABLES[cur_idx]['name']) self._refresh_active_table() def _cb_cmd_back_clicked(self, idx): try: cur_idx = self.tabWidget.currentIndex() self.IN_DETAIL_VIEW[cur_idx] = False self._set_active_widgets(cur_idx, False) if cur_idx == StatsDialog.TAB_RULES: self._restore_rules_tab_widgets(True) return elif cur_idx == StatsDialog.TAB_PROCS: self.cmdProcDetails.setVisible(False) model = self._get_active_table().model() where_clause = "" if self.TABLES[cur_idx]['filterLine'] != None: filter_text = self.TABLES[cur_idx]['filterLine'].text() where_clause = self._get_filter_line_clause(cur_idx, filter_text) self.setQuery(model, self._db.get_query( self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['display_fields']) + where_clause + " " + self._get_order() + self._get_limit() ) finally: self._restore_details_view_columns( self.TABLES[cur_idx]['view'].horizontalHeader(), "{0}{1}".format(Config.STATS_VIEW_COL_STATE, cur_idx) ) self._restore_scroll_value() self._restore_last_selected_row() def _cb_main_table_double_clicked(self, row): prev_idx = self.tabWidget.currentIndex() data = row.data() idx = row.column() cur_idx = 1 if idx == StatsDialog.COL_NODE: cur_idx = self.TAB_NODES self.IN_DETAIL_VIEW[cur_idx] = True self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_NODE).data() self.tabWidget.setCurrentIndex(cur_idx) self._set_active_widgets(prev_idx, True, str(data)) self._set_nodes_query(data) elif idx == StatsDialog.COL_RULES: cur_idx = self.TAB_RULES self.IN_DETAIL_VIEW[cur_idx] = True self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_RULES).data() r_name, node = self._set_rules_tab_active(row, cur_idx, self.COL_RULES, self.COL_NODE) self._set_active_widgets(prev_idx, True, str(data)) self._set_rules_query(r_name, node) elif idx == StatsDialog.COL_DSTIP: cur_idx = self.TAB_ADDRS self.IN_DETAIL_VIEW[cur_idx] = True rowdata = row.model().index(row.row(), self.COL_DSTIP).data() ip = rowdata self.LAST_SELECTED_ITEM = ip self.tabWidget.setCurrentIndex(cur_idx) self._set_active_widgets(prev_idx, True, ip) self._set_addrs_query(ip) elif idx == StatsDialog.COL_DSTHOST: cur_idx = self.TAB_HOSTS self.IN_DETAIL_VIEW[cur_idx] = True rowdata = row.model().index(row.row(), self.COL_DSTHOST).data() host = rowdata self.LAST_SELECTED_ITEM = host self.tabWidget.setCurrentIndex(cur_idx) self._set_active_widgets(prev_idx, True, host) self._set_hosts_query(host) elif idx == StatsDialog.COL_DSTPORT: cur_idx = self.TAB_PORTS self.IN_DETAIL_VIEW[cur_idx] = True rowdata = row.model().index(row.row(), self.COL_DSTPORT).data() port = rowdata self.LAST_SELECTED_ITEM = port self.tabWidget.setCurrentIndex(cur_idx) self._set_active_widgets(prev_idx, True, port) self._set_ports_query(port) elif idx == StatsDialog.COL_UID: cur_idx = self.TAB_USERS self.IN_DETAIL_VIEW[cur_idx] = True rowdata = row.model().index(row.row(), self.COL_UID).data() uid = rowdata self.LAST_SELECTED_ITEM = uid self.tabWidget.setCurrentIndex(cur_idx) self._set_active_widgets(prev_idx, True, uid) self._set_users_query(uid) else: cur_idx = self.TAB_PROCS self.IN_DETAIL_VIEW[cur_idx] = True self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_PROCS).data() self.tabWidget.setCurrentIndex(cur_idx) self._set_active_widgets(prev_idx, True, self.LAST_SELECTED_ITEM) self._set_process_query(self.LAST_SELECTED_ITEM) self._restore_details_view_columns( self.TABLES[cur_idx]['view'].horizontalHeader(), "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx) ) def _cb_table_double_clicked(self, row): cur_idx = self.tabWidget.currentIndex() if self.IN_DETAIL_VIEW[cur_idx]: return if cur_idx == self.TAB_RULES and self.fwTable.isVisible(): uuid = row.model().index(row.row(), 1).data(QtCore.Qt.UserRole+1) addr = row.model().index(row.row(), 2).data(QtCore.Qt.UserRole+1) self._fw_dialog.load_rule(addr, uuid) return self.IN_DETAIL_VIEW[cur_idx] = True self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_TIME).data() self.LAST_SCROLL_VALUE = self.TABLES[cur_idx]['view'].vScrollBar.value() data = row.data() if cur_idx == self.TAB_RULES: rule_name = row.model().index(row.row(), self.COL_R_NAME).data() self._set_active_widgets(cur_idx, True, rule_name) r_name, node = self._set_rules_tab_active(row, cur_idx, self.COL_R_NAME, self.COL_R_NODE) self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_R_NAME).data() self._set_rules_query(r_name, node) self._restore_details_view_columns( self.TABLES[cur_idx]['view'].horizontalHeader(), "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx) ) return if cur_idx == self.TAB_NODES: data = row.model().index(row.row(), self.COL_NODE).data() self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_NODE).data() if cur_idx > self.TAB_RULES: self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_WHAT).data() data = row.model().index(row.row(), self.COL_WHAT).data() self._set_active_widgets(cur_idx, True, str(data)) if cur_idx == StatsDialog.TAB_NODES: self._set_nodes_query(data) elif cur_idx == StatsDialog.TAB_HOSTS: self._set_hosts_query(data) elif cur_idx == StatsDialog.TAB_PROCS: self._set_process_query(data) elif cur_idx == StatsDialog.TAB_ADDRS: lbl_text = self.TABLES[cur_idx]['label'].text() if lbl_text != "": asn = self.asndb.get_asn(lbl_text) if asn != "": lbl_text += " (" + asn + ")" self.TABLES[cur_idx]['label'].setText(lbl_text) self._set_addrs_query(data) elif cur_idx == StatsDialog.TAB_PORTS: self._set_ports_query(data) elif cur_idx == StatsDialog.TAB_USERS: self._set_users_query(data) self._restore_details_view_columns( self.TABLES[cur_idx]['view'].horizontalHeader(), "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx) ) def _cb_prefs_clicked(self): self._prefs_dialog.show() def _cb_rules_filter_combo_changed(self, idx): if idx == self.RULES_TREE_APPS: self._set_rules_filter() elif idx == self.RULES_COMBO_PERMANENT: self._set_rules_filter(self.RULES_TREE_APPS, self.RULES_TREE_PERMANENT) elif idx == self.RULES_COMBO_TEMPORARY: self._set_rules_filter(self.RULES_TREE_APPS, self.RULES_TREE_TEMPORARY) elif idx == self.RULES_COMBO_FW: self._set_rules_filter(-1, self.RULES_TREE_FIREWALL) def _cb_rules_tree_item_expanded(self, item): self.rulesTreePanel.resizeColumnToContents(0) self.rulesTreePanel.resizeColumnToContents(1) def _cb_rules_tree_item_double_clicked(self, item, col): # TODO: open fw chain editor pass def _cb_rules_tree_item_clicked(self, item, col): """ Event fired when the user clicks on the left panel of the rules tab """ item_model = self.rulesTreePanel.indexFromItem(item, col) item_row = item_model.row() parent = item.parent() parent_row = -1 node_addr = "" fw_table = "" rulesHeader = self.rulesTable.horizontalHeader() self._cfg.setSettings(Config.STATS_RULES_COL_STATE, rulesHeader.saveState()) self._clear_rows_selection() # FIXME: find a clever way of handling these options # top level items if parent != None: parent_model = self.rulesTreePanel.indexFromItem(parent, 0) parent_row = parent_model.row() node_addr = parent_model.data() # 1st level items: nodes, rules types if parent.parent() != None: parent = parent.parent() parent_model = self.rulesTreePanel.indexFromItem(parent, 0) item_row = self.FILTER_TREE_FW_TABLE parent_row = self.RULES_TREE_FIREWALL fw_table = parent_model.data() # 2nd level items: chains if parent.parent() != None: parent = parent.parent() parent_model = self.rulesTreePanel.indexFromItem(parent.parent(), 0) item_row = self.FILTER_TREE_FW_CHAIN parent_row = self.RULES_TREE_FIREWALL if node_addr == None: return showFwTable = (parent_row == self.RULES_TREE_FIREWALL or (parent_row == -1 and item_row == self.RULES_TREE_FIREWALL)) self.fwTable.setVisible(showFwTable) self.rulesTable.setVisible(not showFwTable) self.rulesScrollBar.setVisible(self.rulesTable.isVisible()) self._set_rules_filter(parent_row, item_row, item.text(0), node_addr, fw_table) def _cb_rules_splitter_moved(self, pos, index): self.comboRulesFilter.setVisible(pos == 0) self._cfg.setSettings(Config.STATS_RULES_SPLITTER_POS, self.rulesSplitter.saveState()) def _cb_start_clicked(self): if self.daemon_connected == False: self.startButton.setChecked(False) self.startButton.setIcon(self.iconStart) return self.update_interception_status(self.startButton.isChecked()) self._status_changed_trigger.emit(self.startButton.isChecked()) if self.startButton.isChecked(): nid, noti = self._nodes.start_interception(_callback=self._notification_callback) else: nid, noti = self._nodes.stop_interception(_callback=self._notification_callback) self._notifications_sent[nid] = noti def _cb_node_start_clicked(self): addr = self.TABLES[self.TAB_NODES]['label'].text() if addr == "": return if self.nodeStartButton.isChecked(): self._update_nodes_interception_status() nid, noti = self._nodes.start_interception(_addr=addr, _callback=self._notification_callback) else: self._update_nodes_interception_status(disable=True) nid, noti = self._nodes.stop_interception(_addr=addr, _callback=self._notification_callback) self._notifications_sent[nid] = noti def _cb_node_prefs_clicked(self): addr = self.TABLES[self.TAB_NODES]['label'].text() if addr == "": return self._prefs_dialog.show_node_prefs(addr) def _cb_node_delete_clicked(self): ret = Message.yes_no( QC.translate("stats", " You are about to delete this node. "), QC.translate("stats", " Are you sure?"), QtWidgets.QMessageBox.Warning) if ret == QtWidgets.QMessageBox.Cancel: return addr = self.TABLES[self.TAB_NODES]['label'].text() if self._db.remove("DELETE FROM nodes WHERE addr = '{0}'".format(addr)) == False: Message.ok( QC.translate("stats", "<b>Error deleting node</b><br><br>", "{0}").format(addr), QtWidgets.QMessageBox.Warning) return self._nodes.delete(addr) self.TABLES[self.TAB_NODES]['cmd'].click() self.TABLES[self.TAB_NODES]['label'].setText("") self._refresh_active_table() def _cb_new_rule_clicked(self): self._rules_dialog.new_rule() def _cb_edit_rule_clicked(self): cur_idx = self.tabWidget.currentIndex() records = self._get_rule(self.TABLES[cur_idx]['label'].text(), self.nodeRuleLabel.text()) if records == None: return self._rules_dialog.edit_rule(records, self.nodeRuleLabel.text()) def _cb_del_rule_clicked(self): ret = Message.yes_no( QC.translate("stats", " You are about to delete this rule. "), QC.translate("stats", " Are you sure?"), QtWidgets.QMessageBox.Warning) if ret == QtWidgets.QMessageBox.Cancel: return self._del_rule(self.TABLES[self.tabWidget.currentIndex()]['label'].text(), self.nodeRuleLabel.text()) self.TABLES[self.TAB_RULES]['cmd'].click() self.nodeRuleLabel.setText("") self._refresh_active_table() def _cb_enable_rule_toggled(self, state): rule = ui_pb2.Rule(name=self.TABLES[self.tabWidget.currentIndex()]['label'].text()) rule.enabled = False rule.action = "" rule.duration = "" rule.operator.type = "" rule.operator.operand = "" rule.operator.data = "" notType = ui_pb2.DISABLE_RULE if state == True: notType = ui_pb2.ENABLE_RULE rule.enabled = state noti = ui_pb2.Notification(type=notType, rules=[rule]) self._notification_trigger.emit(noti) def _cb_prev_button_clicked(self): model = self._get_active_table().model() model.fetchMore() def _cb_next_button_clicked(self): model = self._get_active_table().model() model.fetchMore() def _cb_help_button_clicked(self): QuickHelp.show( QC.translate("stats", "<p><b>Quick help</b></p>" \ "<p>- Use CTRL+c to copy selected rows.</p>" \ "<p>- Use Home,End,PgUp,PgDown,PgUp,Up or Down keys to navigate rows.</p>" \ "<p>- Use right click on a row to stop refreshing the view.</p>" \ "<p>- Selecting more than one row also stops refreshing the view.</p>" "<p>- On the Events view, clicking on columns Node, Process or Rule<br>" \ "jumps to the view of the selected item.</p>" \ "<p>- On the rest of the views, double click on a row to get detailed<br>" \ " information.</p><br>" \ "<p>For more information visit the <a href=\"{0}\">wiki</a></p>" \ "<br>".format(Config.HELP_URL) ) ) # must be called after setModel() or setQuery() def _show_columns(self): cols = self._cfg.getSettings(Config.STATS_SHOW_COLUMNS) if cols == None: return for c in range(StatsDialog.GENERAL_COL_NUM): self.eventsTable.setColumnHidden(c, str(c) not in cols) def _update_status_label(self, running=False, text=FIREWALL_DISABLED): self.statusLabel.setText("%12s" % text) if running: self.statusLabel.setStyleSheet('color: green; margin: 5px') self.startButton.setIcon(self.iconPause) else: self.statusLabel.setStyleSheet('color: rgb(206, 92, 0); margin: 5px') self.startButton.setIcon(self.iconStart) self._add_rulesTree_nodes() self._add_rulesTree_fw_chains() def _get_rulesTree_item(self, index): try: return self.rulesTreePanel.topLevelItem(index) except Exception: return None def _add_rulesTree_nodes(self): if self._nodes.count() > 0: nodesItem = self.rulesTreePanel.topLevelItem(self.RULES_TREE_NODES) nodesItem.takeChildren() for n in self._nodes.get_nodes(): nodesItem.addChild(QtWidgets.QTreeWidgetItem([n])) def _find_tree_fw_items(self, item_data): """find fw items by data stored in UserRole role. """ fwItem = self.rulesTreePanel.topLevelItem(self.RULES_TREE_FIREWALL) it = QtWidgets.QTreeWidgetItemIterator(fwItem) items = [] while it.value(): x = it.value() if x.data(0, QtCore.Qt.UserRole) == item_data: items.append(x) it+=1 return items def _add_rulesTree_fw_chains(self): expanded = list() selected = None scrollValue = self.rulesTreePanel.verticalScrollBar().value() fwItem = self.rulesTreePanel.topLevelItem(self.RULES_TREE_FIREWALL) it = QtWidgets.QTreeWidgetItemIterator(fwItem) # save tree selected rows try: while it.value(): x = it.value() if x.isExpanded(): expanded.append(x) if x.isSelected(): selected = x it += 1 except Exception: pass self.rulesTreePanel.setAnimated(False) fwItem.takeChildren() self.rulesTreePanel.setItemWidget(fwItem, 1, self.fwTreeEdit) chains = self._fw.get_chains() for addr in chains: # add nodes nodeRoot = QtWidgets.QTreeWidgetItem(["{0}".format(addr)]) nodeRoot.setData(0, QtCore.Qt.UserRole, addr) fwItem.addChild(nodeRoot) for nodeChains in chains[addr]: # exclude legacy system rules if len(nodeChains) == 0: continue for cc in nodeChains: # add tables tableName = "{0}-{1}".format(cc.Table, cc.Family) nodeTable = QtWidgets.QTreeWidgetItem([tableName]) nodeTable.setData(0, QtCore.Qt.UserRole, "{0}-{1}".format(addr, tableName)) chainName = "{0}-{1}".format(cc.Name, cc.Hook) nodeChain = QtWidgets.QTreeWidgetItem([chainName, cc.Policy]) nodeChain.setData(0, QtCore.Qt.UserRole, "{0}-{1}".format(addr, chainName)) items = self._find_tree_fw_items("{0}-{1}".format(addr, tableName)) if len(items) == 0: # add table nodeTable.addChild(nodeChain) nodeRoot.addChild(nodeTable) else: # add chains node = items[0] node.addChild(nodeChain) # restore previous selected rows try: for item in expanded: items = self.rulesTreePanel.findItems(item.text(0), QtCore.Qt.MatchRecursive) for it in items: it.setExpanded(True) if selected != None and selected.text(0) == it.text(0): it.setSelected(True) except: pass self.rulesTreePanel.verticalScrollBar().setValue(scrollValue) self.rulesTreePanel.setAnimated(True) self.rulesTreePanel.resizeColumnToContents(0) self.rulesTreePanel.resizeColumnToContents(1) expanded = None def _clear_rows_selection(self): cur_idx = self.tabWidget.currentIndex() self.TABLES[cur_idx]['view'].clearSelection() def _are_rows_selected(self): cur_idx = self.tabWidget.currentIndex() view = self.TABLES[cur_idx]['view'] ret = False if view != None: ret = len(view.selectionModel().selectedRows(0)) > 0 return ret def _get_rule(self, rule_name, node_name): """ get rule records, given the name of the rule and the node """ cur_idx = self.tabWidget.currentIndex() records = self._db.get_rule(rule_name, node_name) if records.next() == False: print("[stats dialog] edit rule, no records: ", rule_name, node_name) if self.TABLES[cur_idx]['cmd'] != None: self.TABLES[cur_idx]['cmd'].click() return None return records def _get_filter_line_clause(self, idx, text): if text == "": return "" if idx == StatsDialog.TAB_RULES: return " WHERE rules.name LIKE '%{0}%' OR rules.operator_data LIKE '%{1}%' ".format(text, text) elif idx == StatsDialog.TAB_HOSTS or \ idx == StatsDialog.TAB_PROCS or \ idx == StatsDialog.TAB_ADDRS or \ idx == StatsDialog.TAB_PORTS or \ idx == StatsDialog.TAB_USERS: return " WHERE what LIKE '%{0}%' ".format(text) return "" def _get_limit(self): return " " + self.LIMITS[self.limitCombo.currentIndex()] def _get_order(self, field=None): cur_idx = self.tabWidget.currentIndex() order_field = self.TABLES[cur_idx]['last_order_by'] if field != None: order_field = field return " ORDER BY %s %s" % (order_field, self.SORT_ORDER[self.TABLES[cur_idx]['last_order_to']]) def _refresh_active_table(self): cur_idx = self.tabWidget.currentIndex() model = self._get_active_table().model() lastQuery = model.query().lastQuery() if "LIMIT" not in lastQuery: lastQuery += self._get_limit() self.setQuery(model, lastQuery) self.TABLES[cur_idx]['view'].refresh() def _get_active_table(self): if self.tabWidget.currentIndex() == self.TAB_RULES and self.fwTable.isVisible(): return self.TABLES[self.TAB_FIREWALL]['view'] return self.TABLES[self.tabWidget.currentIndex()]['view'] def _set_active_widgets(self, prev_idx, state, label_txt=""): cur_idx = self.tabWidget.currentIndex() self._clear_rows_selection() self.TABLES[cur_idx]['label'].setVisible(state) self.TABLES[cur_idx]['label'].setText(label_txt) self.TABLES[cur_idx]['cmd'].setVisible(state) if self.TABLES[cur_idx]['filterLine'] != None: self.TABLES[cur_idx]['filterLine'].setVisible(not state) if self.TABLES[cur_idx].get('cmdCleanStats') != None: if cur_idx == StatsDialog.TAB_RULES or cur_idx == StatsDialog.TAB_NODES: self.TABLES[cur_idx]['cmdCleanStats'].setVisible(state) if cur_idx == StatsDialog.TAB_NODES: self._update_nodes_interception_status(state) self.nodeDeleteButton.setVisible(state) self.nodeActionsButton.setVisible(state) header = self.TABLES[cur_idx]['view'].horizontalHeader() if state == True: # going to details state self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_COL_STATE, prev_idx), header.saveState()) else: # going to normal state self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx), header.saveState()) def _restore_last_selected_row(self): cur_idx = self.tabWidget.currentIndex() col = self.COL_TIME if cur_idx == self.TAB_RULES: col = self.TAB_RULES elif cur_idx == self.TAB_NODES: col = self.TAB_RULES #self.TABLES[cur_idx]['view'].selectItem(self.LAST_SELECTED_ITEM, col) self.LAST_SELECTED_ITEM = "" def _restore_scroll_value(self): if self.LAST_SCROLL_VALUE != None: cur_idx = self.tabWidget.currentIndex() self.TABLES[cur_idx]['view'].vScrollBar.setValue(self.LAST_SCROLL_VALUE) self.LAST_SCROLL_VALUE = None def _restore_details_view_columns(self, header, settings_key): header.blockSignals(True); # In order to resize the last column of a view, we firstly force a # resizeToContens call. # Secondly set resizeMode to Interactive (allow to move columns by # users + programmatically) header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) header.setSectionResizeMode(QtWidgets.QHeaderView.Interactive) col_state = self._cfg.getSettings(settings_key) if type(col_state) == QtCore.QByteArray: header.restoreState(col_state) header.blockSignals(False); def _restore_rules_tab_widgets(self, active): self.delRuleButton.setVisible(not active) self.editRuleButton.setVisible(not active) self.nodeRuleLabel.setText("") self.rulesTreePanel.setVisible(active) if not active: return self.rulesSplitter.refresh() self.comboRulesFilter.setVisible(self.rulesTreePanel.width() == 0) items = self.rulesTreePanel.selectedItems() if len(items) == 0: self._set_rules_filter() return rindex = item_m = self.rulesTreePanel.indexFromItem(items[0], 0) parent = item_m.parent() # find current root item of the tree panel while rindex.parent().isValid(): rindex = rindex.parent() rnum = rindex.row() if parent != None and rnum != self.RULES_TREE_FIREWALL: self._set_rules_filter(parent.row(), item_m.row(), item_m.data()) else: # when going back to the rules view, reset selection and select the # Apps view. index = self.rulesTreePanel.model().index(self.RULES_TREE_APPS, 0) self.rulesTreePanel.setCurrentIndex(index) self._set_rules_filter() def _set_rules_tab_active(self, row, cur_idx, name_idx, node_idx): self._restore_rules_tab_widgets(False) self.comboRulesFilter.setVisible(False) r_name = row.model().index(row.row(), name_idx).data() node = row.model().index(row.row(), node_idx).data() self.nodeRuleLabel.setText(node) self.fwTable.setVisible(False) self.rulesTable.setVisible(True) self.tabWidget.setCurrentIndex(cur_idx) return r_name, node def _set_events_query(self): if self.tabWidget.currentIndex() != self.TAB_MAIN: return model = self.TABLES[self.TAB_MAIN]['view'].model() qstr = self._db.get_query(self.TABLES[self.TAB_MAIN]['name'], self.TABLES[self.TAB_MAIN]['display_fields']) filter_text = self.filterLine.text() action = "" if self.comboAction.currentIndex() == 1: action = "action = \"{0}\"".format(Config.ACTION_ALLOW) elif self.comboAction.currentIndex() == 2: action = "action = \"{0}\"".format(Config.ACTION_DENY) elif self.comboAction.currentIndex() == 3: action = "action = \"{0}\"".format(Config.ACTION_REJECT) # FIXME: use prepared statements if filter_text == "": if action != "": qstr += " WHERE " + action else: if action != "": action += " AND " qstr += " WHERE " + action + " ("\ " process LIKE '%" + filter_text + "%'" \ " OR process_args LIKE '%" + filter_text + "%'" \ " OR src_port LIKE '%" + filter_text + "%'" \ " OR src_ip LIKE '%" + filter_text + "%'" \ " OR dst_ip LIKE '%" + filter_text + "%'" \ " OR dst_host LIKE '%" + filter_text + "%'" \ " OR dst_port LIKE '%" + filter_text + "%'" \ " OR rule LIKE '%" + filter_text + "%'" \ " OR node LIKE '%" + filter_text + "%'" \ " OR time LIKE '%" + filter_text + "%'" \ " OR pid LIKE '%" + filter_text + "%'" \ " OR uid LIKE '%" + filter_text + "%'" \ " OR protocol LIKE '%" + filter_text + "%')" \ qstr += self._get_order() + self._get_limit() self.setQuery(model, qstr) def _set_nodes_query(self, data): model = self._get_active_table().model() self.setQuery(model, "SELECT " \ "MAX(c.time) as {0}, " \ "c.action as {1}, " \ "count(c.process) as {2}, " \ "c.uid as {3}, " \ "c.protocol as {4}, " \ "c.src_port as {5}, " \ "c.src_ip as {6}, " \ "c.dst_ip as {7}, " \ "c.dst_host as {8}, " \ "c.dst_port as {9}, " \ "c.pid as {10}, " \ "c.process as {11}, " \ "c.process_args as {12}, " \ "c.process_cwd as CWD, " \ "c.rule as {13} " \ "FROM connections as c " \ "WHERE c.node = '{14}' GROUP BY {15}, c.process_args, c.uid, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol {16}".format( self.COL_STR_TIME, self.COL_STR_ACTION, self.COL_STR_HITS, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_IP, self.COL_STR_DST_HOST, self.COL_STR_DST_PORT, self.COL_STR_PID, self.COL_STR_PROCESS, self.COL_STR_PROC_CMDLINE, self.COL_STR_RULE, data, self.COL_STR_PROCESS, self._get_order() + self._get_limit())) def _get_nodes_filter_query(self, lastQuery, text): base_query = lastQuery.split("GROUP BY") if not self.IN_DETAIL_VIEW[self.TAB_NODES]: base_query = lastQuery.split("ORDER BY") qstr = base_query[0] if "AND" in qstr: # strip out ANDs if any os = qstr.split('AND') qstr = os[0] if text != "": if self.IN_DETAIL_VIEW[self.TAB_NODES]: qstr += "AND (c.time LIKE '%{0}%' OR " \ "c.action LIKE '%{0}%' OR " \ "c.uid LIKE '%{0}%' OR " \ "c.pid LIKE '%{0}%' OR " \ "c.src_port LIKE '%{0}%' OR " \ "c.dst_port LIKE '%{0}%' OR " \ "c.src_ip LIKE '%{0}%' OR " \ "c.dst_ip LIKE '%{0}%' OR " \ "c.dst_host LIKE '%{0}%' OR " \ "c.process LIKE '%{0}%' OR " \ "c.process_cwd LIKE '%{0}%' OR " \ "c.process_args LIKE '%{0}%')".format(text) else: if "WHERE" in qstr: w = qstr.split('WHERE') qstr = w[0] qstr += "WHERE (" \ "last_connection LIKE '%{0}%' OR " \ "addr LIKE '%{0}%' OR " \ "status LIKE '%{0}%' OR " \ "hostname LIKE '%{0}%' OR " \ "version LIKE '%{0}%'" \ ")".format(text) if self.IN_DETAIL_VIEW[self.TAB_NODES]: qstr += " GROUP BY" + base_query[1] else: qstr += " ORDER BY" + base_query[1] return qstr def _update_nodes_interception_status(self, show=True, disable=False): addr = self.TABLES[self.TAB_NODES]['label'].text() node_cfg = self._nodes.get_node(addr) if node_cfg == None: self.nodeStartButton.setVisible(False) self.nodePrefsButton.setVisible(False) self.nodeDeleteButton.setVisible(False) self.nodeActionsButton.setVisible(False) return self.nodeStartButton.setVisible(show) self.nodePrefsButton.setVisible(show) self.nodeActionsButton.setVisible(show) if not node_cfg['data'].isFirewallRunning or disable: self.nodeStartButton.setChecked(False) self.nodeStartButton.setDown(False) self.nodeStartButton.setIcon(self.iconStart) else: self.nodeStartButton.setIcon(self.iconPause) self.nodeStartButton.setChecked(True) self.nodeStartButton.setDown(True) def _set_rules_filter(self, parent_row=-1, item_row=0, what="", what1="", what2=""): section = self.FILTER_TREE_APPS if parent_row == -1: self.fwTable.setVisible(item_row == self.RULES_TREE_FIREWALL) self.rulesTable.setVisible(item_row != self.RULES_TREE_FIREWALL) if item_row == self.RULES_TREE_NODES: section=self.FILTER_TREE_NODES what="" elif item_row == self.RULES_TREE_FIREWALL: self.TABLES[self.TAB_FIREWALL]['view'].model().filterAll() return else: section=self.FILTER_TREE_APPS what="" elif parent_row == self.RULES_TREE_APPS: if item_row == self.RULES_TREE_PERMANENT: section=self.FILTER_TREE_APPS what=self.RULES_TYPE_PERMANENT elif item_row == self.RULES_TREE_TEMPORARY: section=self.FILTER_TREE_APPS what=self.RULES_TYPE_TEMPORARY elif parent_row == self.RULES_TREE_NODES: section=self.FILTER_TREE_NODES elif parent_row == self.RULES_TREE_FIREWALL: if item_row == self.FILTER_TREE_FW_NODE: self.TABLES[self.TAB_FIREWALL]['view'].filterByNode(what) elif item_row == self.FILTER_TREE_FW_TABLE: parm = what.split("-") if len(parm) < 2: return self.TABLES[self.TAB_FIREWALL]['view'].filterByTable(what1, parm[0], parm[1]) elif item_row == self.FILTER_TREE_FW_CHAIN: # + table parm = what.split("-") tbl = what1.split("-") self.TABLES[self.TAB_FIREWALL]['view'].filterByChain(what2, tbl[0], tbl[1], parm[0], parm[1]) return if section == self.FILTER_TREE_APPS: if what == self.RULES_TYPE_TEMPORARY: what = "WHERE r.duration != '%s'" % Config.DURATION_ALWAYS elif what == self.RULES_TYPE_PERMANENT: what = "WHERE r.duration = '%s'" % Config.DURATION_ALWAYS elif section == self.FILTER_TREE_NODES and what != "": what = "WHERE r.node = '%s'" % what filter_text = self.filterLine.text() if filter_text != "": if what == "": what = "WHERE" else: what = what + " AND" what = what + " r.name LIKE '%{0}%'".format(filter_text) model = self._get_active_table().model() self.setQuery(model, "SELECT {0} FROM rules as r {1} {2} {3}".format( self.TABLES[self.TAB_RULES]['display_fields'], what, self._get_order(), self._get_limit() )) def _set_rules_query(self, rule_name="", node=""): if node != "": node = "c.node = '%s'" % node if rule_name != "": rule_name = "c.rule = '%s'" % rule_name condition = "%s AND %s" % (rule_name, node) if rule_name != "" and node != "" else "" model = self._get_active_table().model() self.setQuery(model, "SELECT " \ "MAX(c.time) as {0}, " \ "c.node as {1}, " \ "count(c.process) as {2}, " \ "c.uid as {3}, " \ "c.protocol as {4}, " \ "c.src_port as {5}, " \ "c.src_ip as {6}, " \ "c.dst_ip as {7}, " \ "c.dst_host as {8}, " \ "c.dst_port as {9}, " \ "c.pid as {10}, " \ "c.process as {11}, " \ "c.process_args as {12}, " \ "c.process_cwd as CWD " \ "FROM connections as c " \ "WHERE {13} GROUP BY c.process, c.process_args, c.uid, c.dst_ip, c.dst_host, c.dst_port {14}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_IP, self.COL_STR_DST_HOST, self.COL_STR_DST_PORT, self.COL_STR_PID, self.COL_STR_PROCESS, self.COL_STR_PROC_CMDLINE, condition, self._get_order() + self._get_limit() )) def _set_hosts_query(self, data): model = self._get_active_table().model() self.setQuery(model, "SELECT " \ "MAX(c.time) as {0}, " \ "c.node as {1}, " \ "count(c.process) as {2}, " \ "c.action as {3}, " \ "c.uid as {4}, " \ "c.protocol as {5}, " \ "c.src_port as {6}, " \ "c.src_ip as {7}, " \ "c.dst_ip as {9}, " \ "c.dst_port as {8}, " \ "c.pid as {10}, " \ "c.process as {11}, " \ "c.process_args as {12}, " \ "c.process_cwd as CWD, " \ "c.rule as {13} " \ "FROM connections as c " \ "WHERE c.dst_host = '{14}' GROUP BY c.pid, {15}, c.process_args, c.src_ip, c.dst_ip, c.dst_port, c.protocol, c.action, c.node {16}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_ACTION, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_IP, self.COL_STR_DST_PORT, self.COL_STR_PID, self.COL_STR_PROCESS, self.COL_STR_PROC_CMDLINE, self.COL_STR_RULE, data, self.COL_STR_PROCESS, self._get_order("1") + self._get_limit())) def _set_process_query(self, data): model = self._get_active_table().model() self.setQuery(model, "SELECT " \ "MAX(c.time) as {0}, " \ "c.node as {1}, " \ "count(c.dst_ip) as {2}, " \ "c.action as {3}, " \ "c.uid as {4}, " \ "c.protocol as {5}, " \ "c.src_port as {6}, " \ "c.src_ip as {7}, " \ "c.dst_ip as {8}, " \ "c.dst_host as {9}, " \ "c.dst_port as {10}, " \ "c.pid as PID, " \ "c.process_args as {11}, " \ "c.process_cwd as CWD, " \ "c.rule as {12} " \ "FROM connections as c " \ "WHERE c.process = '{13}' " \ "GROUP BY c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.uid, c.action, c.node, c.pid, c.process_args {14}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_ACTION, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_IP, self.COL_STR_DST_HOST, self.COL_STR_DST_PORT, self.COL_STR_PROC_CMDLINE, self.COL_STR_RULE, data, self._get_order("1") + self._get_limit())) nrows = self._get_active_table().model().rowCount() self.cmdProcDetails.setVisible(nrows != 0) def _set_addrs_query(self, data): model = self._get_active_table().model() self.setQuery(model, "SELECT " \ "MAX(c.time) as {0}, " \ "c.node as {1}, " \ "count(c.dst_ip) as {2}, " \ "c.action as {3}, " \ "c.uid as {4}, " \ "c.protocol as {5}, " \ "c.src_port as {6}, " \ "c.src_ip as {7}, " \ "c.dst_host as {8}, " \ "c.dst_port as {9}, " \ "c.pid as {10}, " \ "c.process as {11}, " \ "c.process_args as {12}, " \ "c.process_cwd as CWD, " \ "c.rule as {13} " \ "FROM connections as c " \ "WHERE c.dst_ip = '{14}' GROUP BY c.pid, {15}, c.process_args, c.src_ip, c.dst_port, {16}, c.protocol, c.action, c.uid, c.node {17}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_ACTION, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_HOST, self.COL_STR_DST_PORT, self.COL_STR_PID, self.COL_STR_PROCESS, self.COL_STR_PROC_CMDLINE, self.COL_STR_RULE, data, self.COL_STR_PROCESS, self.COL_STR_DST_HOST, self._get_order("1") + self._get_limit())) def _set_ports_query(self, data): model = self._get_active_table().model() self.setQuery(model, "SELECT " \ "MAX(c.time) as {0}, " \ "c.node as {1}, " \ "count(c.dst_ip) as {2}, " \ "c.action as {3}, " \ "c.uid as {4}, " \ "c.protocol as {5}, " \ "c.src_port as {6}, " \ "c.src_ip as {7}, " \ "c.dst_ip as {8}, " \ "c.dst_host as {9}, " \ "c.pid as {10}, " \ "c.process as {11}, " \ "c.process_args as {12}, " \ "c.process_cwd as CWD, " \ "c.rule as {13} " \ "FROM connections as c " \ "WHERE c.dst_port = '{14}' GROUP BY c.pid, {15}, c.process_args, {16}, c.src_ip, c.dst_ip, c.protocol, c.action, c.uid, c.node {17}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_ACTION, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_IP, self.COL_STR_DST_HOST, self.COL_STR_PID, self.COL_STR_PROCESS, self.COL_STR_PROC_CMDLINE, self.COL_STR_RULE, data, self.COL_STR_PROCESS, self.COL_STR_DST_HOST, self._get_order("1") + self._get_limit())) def _set_users_query(self, data): uid = data.split(" ") if len(uid) == 2: uid = uid[1].strip("()") else: uid = uid[0] model = self._get_active_table().model() self.setQuery(model, "SELECT " \ "MAX(c.time) as {0}, " \ "c.node as {1}, " \ "count(c.dst_ip) as {2}, " \ "c.action as {3}, " \ "c.protocol as {4}, " \ "c.src_port as {5}, " \ "c.src_ip as {6}, " \ "c.dst_ip as {7}, " \ "c.dst_host as {8}, " \ "c.dst_port as {9}, " \ "c.pid as {10}, " \ "c.process as {11}, " \ "c.process_args as {12}, " \ "c.process_cwd as CWD, " \ "c.rule as {13} " \ "FROM connections as c " \ "WHERE c.uid = '{14}' GROUP BY c.pid, {15}, c.process_args, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol, c.action, c.node {16}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_ACTION, self.COL_STR_PROTOCOL, self.COL_STR_SRC_PORT, self.COL_STR_SRC_IP, self.COL_STR_DST_IP, self.COL_STR_DST_HOST, self.COL_STR_DST_PORT, self.COL_STR_PID, self.COL_STR_PROCESS, self.COL_STR_PROC_CMDLINE, self.COL_STR_RULE, uid, self.COL_STR_PROCESS, self._get_order("1") + self._get_limit())) # get the query filtering by text when a tab is in the detail view. def _get_indetail_filter_query(self, lastQuery, text): try: cur_idx = self.tabWidget.currentIndex() base_query = lastQuery.split("GROUP BY") qstr = base_query[0] where = qstr.split("WHERE")[1] # get SELECT ... WHERE (*) ands = where.split("AND (")[0] # get WHERE (*) AND (...) qstr = qstr.split("WHERE")[0] # get * WHERE ... qstr += "WHERE %s" % ands # if there's no text to filter, strip the filter "AND ()", and # return the original query. if text == "": return qstr += "AND (c.time LIKE '%{0}%' OR " \ "c.action LIKE '%{0}%' OR " \ "c.pid LIKE '%{0}%' OR " \ "c.protocol LIKE '%{0}%' OR " \ "c.src_port LIKE '%{0}%' OR " \ "c.src_ip LIKE '%{0}%' OR ".format(text) # exclude from query the field of the view we're filtering by if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_PORTS: qstr += "c.dst_port LIKE '%{0}%' OR ".format(text) if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_ADDRS: qstr += "c.dst_ip LIKE '%{0}%' OR ".format(text) if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_HOSTS: qstr += "c.dst_host LIKE '%{0}%' OR ".format(text) if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_PROCS: qstr += "c.process LIKE '%{0}%' OR ".format(text) qstr += "c.process_args LIKE '%{0}%')".format(text) finally: if len(base_query) > 1: qstr += " GROUP BY" + base_query[1] return qstr @QtCore.pyqtSlot() def _on_settings_saved(self): self._ui_refresh_interval = self._cfg.getInt(Config.STATS_REFRESH_INTERVAL, 0) self._show_columns() self.settings_saved.emit() def _on_menu_node_export_clicked(self, triggered): outdir = QtWidgets.QFileDialog.getExistingDirectory(self, os.path.expanduser("~"), QC.translate("stats", 'Select a directory to export rules'), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if outdir == "": return node = self.nodesLabel.text() if self._nodes.export_rules(node, outdir) == False: Message.ok("Rules export error", QC.translate("stats", "Error exporting rules" ), QtWidgets.QMessageBox.Warning) else: Message.ok("Rules export", QC.translate("stats", "Rules exported to {0}".format(outdir)), QtWidgets.QMessageBox.Information) def _on_menu_node_import_clicked(self, triggered): rulesdir = QtWidgets.QFileDialog.getExistingDirectory(self, os.path.expanduser("~"), QC.translate("stats", 'Select a directory with rules to import (JSON files)'), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if rulesdir == '': return node = self.nodesLabel.text() nid, notif, rules = self._nodes.import_rules(addr=node, rulesdir=rulesdir, callback=self._notification_callback) if nid != None: self._notifications_sent[nid] = notif # TODO: add rules per node and after receiving the notification for node in self._nodes.get_nodes(): self._nodes.add_rules(node, rules) Message.ok("Rules import", QC.translate("stats", "Rules imported fine"), QtWidgets.QMessageBox.Information) if self.tabWidget.currentIndex() == self.TAB_RULES: self._refresh_active_table() else: Message.ok("Rules import error", QC.translate("stats", "Error importing rules from {0}".format(rulesdir) ), QtWidgets.QMessageBox.Warning) def _on_menu_exit_clicked(self, triggered): self.close_trigger.emit() def _on_menu_export_clicked(self, triggered): outdir = QtWidgets.QFileDialog.getExistingDirectory(self, os.path.expanduser("~"), QC.translate("stats", 'Select a directory to export rules'), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if outdir == "": return errors = [] for node in self._nodes.get_nodes(): if self._nodes.export_rules(node, outdir) == False: errors.append(node) # apply_to_node()... if len(errors) > 0: errorlist = "" for e in errors: errorlist = errorlist + e + "<br>" Message.ok("Rules export error", QC.translate("stats", "Error exporting rules of the following nodes:<br><br>{0}" .format(errorlist) ), QtWidgets.QMessageBox.Warning) else: Message.ok("Rules export", QC.translate("stats", "Rules exported to {0}".format(outdir)), QtWidgets.QMessageBox.Information) def _on_menu_import_clicked(self, triggered): rulesdir = QtWidgets.QFileDialog.getExistingDirectory(self, os.path.expanduser("~"), QC.translate("stats", 'Select a directory with rules to import (JSON files)'), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if rulesdir == '': return nid, notif, rules = self._nodes.import_rules(rulesdir=rulesdir, callback=self._notification_callback) if nid != None: self._notifications_sent[nid] = notif # TODO: add rules per node and after receiving the notification for node in self._nodes.get_nodes(): self._nodes.add_rules(node, rules) Message.ok("Rules import", QC.translate("stats", "Rules imported fine"), QtWidgets.QMessageBox.Information) if self.tabWidget.currentIndex() == self.TAB_RULES: self._refresh_active_table() else: Message.ok("Rules import error", QC.translate("stats", "Error importing rules from {0}".format(rulesdir) ), QtWidgets.QMessageBox.Warning) def _on_menu_export_csv_clicked(self, triggered): tab_idx = self.tabWidget.currentIndex() filename = QtWidgets.QFileDialog.getSaveFileName(self, QC.translate("stats", 'Save as CSV'), self._file_names[tab_idx], 'All Files (*);;CSV Files (*.csv)')[0].strip() if filename == '': return with self._lock: table = self._tables[tab_idx] ncols = table.model().columnCount() nrows = table.model().rowCount() cols = [] for col in range(0, ncols): cols.append(table.model().headerData(col, QtCore.Qt.Horizontal)) with open(filename, 'w') as csvfile: w = csv.writer(csvfile, dialect='excel') w.writerow(cols) if tab_idx == self.TAB_MAIN: w.writerows(table.model().dumpRows()) else: for row in range(0, nrows): values = [] for col in range(0, ncols): values.append(table.model().index(row, col).data()) w.writerow(values) def _setup_table(self, widget, tableWidget, table_name, fields="*", group_by="", order_by="2", sort_direction=SORT_ORDER[1], limit="", resize_cols=(), model=None, delegate=None, verticalScrollBar=None, tracking_column=COL_TIME): tableWidget.setSortingEnabled(True) if model == None: model = self._db.get_new_qsql_model() if verticalScrollBar != None: tableWidget.setVerticalScrollBar(verticalScrollBar) tableWidget.verticalScrollBar().sliderPressed.connect(self._cb_scrollbar_pressed) tableWidget.verticalScrollBar().sliderReleased.connect(self._cb_scrollbar_released) tableWidget.setTrackingColumn(tracking_column) self.setQuery(model, "SELECT " + fields + " FROM " + table_name + group_by + " ORDER BY " + order_by + " " + sort_direction + limit) tableWidget.setModel(model) if delegate != None: action = self._actions.get(delegate) if action != None: tableWidget.setItemDelegate(ColorizedDelegate(tableWidget, actions=action)) header = tableWidget.horizontalHeader() if header != None: header.sortIndicatorChanged.connect(self._cb_table_header_clicked) for _, col in enumerate(resize_cols): header.setSectionResizeMode(col, QtWidgets.QHeaderView.ResizeToContents) cur_idx = self.tabWidget.currentIndex() self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx), header.saveState()) return tableWidget def update_interception_status(self, enabled): self.startButton.setDown(enabled) self.startButton.setChecked(enabled) if enabled: self._update_status_label(running=True, text=self.FIREWALL_RUNNING) else: self._update_status_label(running=False, text=self.FIREWALL_DISABLED) def _needs_refresh(self): diff = datetime.datetime.now() - self._last_update if diff.seconds < self._ui_refresh_interval: return False return True # launched from a thread def update(self, is_local=True, stats=None, need_query_update=True): # lock mandatory when there're multiple clients with self._lock: if stats is not None: self._stats = stats # do not update any tab if the window is not visible if self.isVisible() and self.isMinimized() == False and self._needs_refresh(): self._trigger.emit(is_local, need_query_update) self._last_update = datetime.datetime.now() def update_status(self): self.startButton.setDown(self.daemon_connected) self.startButton.setChecked(self.daemon_connected) self.startButton.setDisabled(not self.daemon_connected) if self.daemon_connected: self._update_status_label(running=True, text=self.FIREWALL_RUNNING) else: self._update_status_label(running=False, text=self.FIREWALL_STOPPED) self.statusLabel.setStyleSheet('color: red; margin: 5px') @QtCore.pyqtSlot(bool, bool) def _on_update_triggered(self, is_local, need_query_update=False): if self._stats is None: self.daemonVerLabel.setText("") self.uptimeLabel.setText("") self.rulesLabel.setText("") self.consLabel.setText("") self.droppedLabel.setText("") else: nodes = self._nodes.count() self.daemonVerLabel.setText(self._stats.daemon_version) if nodes <= 1: self.uptimeLabel.setText(str(datetime.timedelta(seconds=self._stats.uptime))) self.rulesLabel.setText("%s" % self._stats.rules) self.consLabel.setText("%s" % self._stats.connections) self.droppedLabel.setText("%s" % self._stats.dropped) else: self.uptimeLabel.setText("") self.rulesLabel.setText("") self.consLabel.setText("") self.droppedLabel.setText("") if need_query_update and not self._are_rows_selected(): self._refresh_active_table() # prevent a click on the window's x # from quitting the whole application def closeEvent(self, e): self._save_settings() e.accept() self.hide() def hideEvent(self, e): self._save_settings() # https://gis.stackexchange.com/questions/86398/how-to-disable-the-escape-key-for-a-dialog def keyPressEvent(self, event): if not event.key() == QtCore.Qt.Key_Escape: super(StatsDialog, self).keyPressEvent(event) def setQuery(self, model, q): if self._context_menu_active == True or self.scrollbar_active == True: return with self._lock: try: model.query().clear() model.setQuery(q, self._db_sqlite) if model.lastError().isValid(): print("setQuery() error: ", model.lastError().text()) if self.tabWidget.currentIndex() != self.TAB_MAIN: self.labelRowsCount.setText("{0}".format(model.totalRowCount)) else: self.labelRowsCount.setText("") except Exception as e: print(self._address, "setQuery() exception: ", e) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/firewall/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020263�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/firewall/__init__.py�������������������������������������������������0000664�0000000�0000000�00000020525�15003540030�0022400�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtCore import QObject, QCoreApplication as QC from google.protobuf import json_format import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() from opensnitch.nodes import Nodes from .enums import * from .rules import * from .chains import * from .utils import Utils from .exprs import * from .profiles import * class Firewall(QObject): __instance = None @staticmethod def instance(): if Firewall.__instance == None: Firewall.__instance = Firewall() return Firewall.__instance def __init__(self, parent=None): QObject.__init__(self) self._nodes = Nodes.instance() self.rules = Rules(self._nodes) self.chains = Chains(self._nodes) def switch_rules(self, key, old_pos, new_pos): pass def add_rule(self, addr, rule): return self.rules.add(addr, rule) def insert_rule(self, addr, rule, position=0): return self.rules.insert(addr, rule, position) def update_rule(self, addr, uuid, rule): return self.rules.update(addr, uuid, rule) def delete_rule(self, addr, uuid): return self.rules.delete(addr, uuid) def change_rule_field(self, addr, uuid, field, value): addr, chain = self.get_rule_by_uuid(uuid) if chain is None: return None, None if field == Rules.FIELD_ENABLED: chain.Rules[0].Enabled = value elif field == Rules.FIELD_TARGET: chain.Rules[0].Target = value return self.update_rule(addr, uuid, chain) def enable_rule(self, addr, uuid, enable): addr, chain = self.get_rule_by_uuid(uuid) if chain is None: return None, None chain.Rules[0].Enabled = enable return self.update_rule(addr, uuid, chain) def filter_rules(self, nail): """ """ chains = [] for addr in self._nodes.get_nodes(): node = self._nodes.get_node(addr) if not 'firewall' in node: return chains for n in node['firewall'].SystemRules: for c in n.Chains: for r in c.Rules: add_rule = False if nail == r.UUID: add_rule = True if nail in c.Family or \ nail in c.Hook or \ nail in r.Description or \ nail in r.Target or \ nail in r.TargetParameters: add_rule = True else: for e in r.Expressions: if add_rule: break expr_vals = "".join("{0} {1}".format(h.Key, h.Value) for h in e.Statement.Values) #print(nail in expr_vals, r.Description) if nail in e.Statement.Op or \ nail in e.Statement.Name or \ nail in e.Statement.Values or \ nail in expr_vals: add_rule = True if add_rule: chains.append(Rules.to_array(addr, c, r)) return chains def filter_by_table(self, addr, table, family): """get rules by table""" chains = [] node = self._nodes.get_node(addr) if not 'firewall' in node: return chains for n in node['firewall'].SystemRules: for c in n.Chains: for r in c.Rules: if c.Table == table and c.Family == family: chains.append(Rules.to_array(addr, c, r)) return chains def filter_by_chain(self, addr, table, family, chain, hook): """get rules by chain""" chains = [] node = self._nodes.get_node(addr) if not 'firewall' in node: return chains for n in node['firewall'].SystemRules: for c in n.Chains: for r in c.Rules: if c.Table == table and c.Family == family and c.Name == chain and c.Hook == hook: chains.append(Rules.to_array(addr, c, r)) return chains def swap_rules(self, view, addr, uuid, old_pos, new_pos): return self.rules.swap(view, addr, uuid, old_pos, new_pos) def get_rule_by_uuid(self, uuid): """get rule by uuid, in string format """ if uuid == "": return None, None for addr in self._nodes.get_nodes(): node = self._nodes.get_node(addr) if not 'fwrules' in node: continue r = node['fwrules'].get(uuid) if r != None: return addr, r return None, None def get_protorule_by_uuid(self, addr, uuid): """get protobuffer rule by uuid. """ return self.rules.get_by_uuid(addr, uuid) def get_node_rules(self, addr): return self.rules.get_by_node(addr) def get_chains(self): return self.chains.get() def get_rules(self): return self.rules.get() def rule_to_json(self, rule): return Rules.to_json(rule) def apply_profile(self, node_addr, json_profile): """ Apply a profile to the firewall configuration. Given a chain (table+family+type+hook), apply its policy, and any rules defined. """ try: holder = ui_pb2.FwChain() profile = json_format.Parse(json_profile, holder) fwcfg = self._nodes.get_node(node_addr)['firewall'] for sdx, n in enumerate(fwcfg.SystemRules): for cdx, c in enumerate(n.Chains): if c.Hook.lower() == profile.Hook and \ c.Type.lower() == profile.Type and \ c.Family.lower() == profile.Family and \ c.Table.lower() == profile.Table: fwcfg.SystemRules[sdx].Chains[cdx].Policy = profile.Policy for r in profile.Rules: temp_c = ui_pb2.FwChain() temp_c.CopyFrom(c) del temp_c.Rules[:] temp_c.Rules.extend([r]) if self.rules.is_duplicated(node_addr, temp_c): continue self.add_rule(node_addr, temp_c) self.rules.rulesUpdated.emit() return True, "" except Exception as e: return False, "{0}".format(e) return False, QC.translate("firewall", "profile not applied") def delete_profile(self, node_addr, json_profile): try: holder = ui_pb2.FwChain() profile = json_format.Parse(json_profile, holder) fwcfg = self._nodes.get_node(node_addr)['firewall'] for sdx, n in enumerate(fwcfg.SystemRules): for cdx, c in enumerate(n.Chains): if c.Hook.lower() == profile.Hook and \ c.Type.lower() == profile.Type and \ c.Family.lower() == profile.Family and \ c.Table.lower() == profile.Table: if profile.Policy == ProfileDropInput.value: profile.Policy = ProfileAcceptInput.value del_candidates = [] for rdx, r in enumerate(c.Rules): for pr in profile.Rules: if r.UUID == pr.UUID: # we cannot delete the rule here, otherwise # we'd modify the items of the loop. del_candidates.append(rdx) if len(del_candidates) > 0: for rdx in del_candidates: if rdx == len(c.Rules): # last rule rdx = rdx - 1 self.delete_rule(node_addr, c.Rules[rdx].UUID) except Exception as e: return False, "{0}".format(e) return False, QC.translate("firewall", "profile not deleted") ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/firewall/chains.py���������������������������������������������������0000664�0000000�0000000�00000017013�15003540030�0022104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() from .enums import * class Chains(): def __init__(self, nodes): self._nodes = nodes def get(self): chains = {} for node in self._nodes.get_nodes(): chains[node] = self.get_node_chains(node) return chains def get_node_chains(self, addr): node = self._nodes.get_node(addr) if node == None: return rules if not 'firewall' in node: return rules chains = [] for c in node['firewall'].SystemRules: # Chains node does not exist on <= v1.5.x try: chains.append(c.Chains) except Exception: pass return chains def get_node_chains(self, addr): node = self._nodes.get_node(addr) if node == None: return rules if not 'firewall' in node: return rules chains = [] for c in node['firewall'].SystemRules: # Chains node does not exist on <= v1.5.x try: chains.append(c.Chains) except Exception: pass return chains def get_policy(self, node_addr=None, hook=Hooks.INPUT.value, _type=ChainType.FILTER.value, family=Family.INET.value): fwcfg = self._nodes.get_node(node_addr)['firewall'] for sdx, n in enumerate(fwcfg.SystemRules): for cdx, c in enumerate(n.Chains): if c.Hook.lower() == hook and c.Type.lower() == _type and c.Family.lower() == family: return c.Policy return None def set_policy(self, node_addr, hook=Hooks.INPUT.value, _type=ChainType.FILTER.value, family=Family.INET.value, policy=Policy.DROP): fwcfg = self._nodes.get_node(node_addr)['firewall'] for sdx, n in enumerate(fwcfg.SystemRules): for cdx, c in enumerate(n.Chains): # XXX: support only "inet" family (ipv4/ipv6)? or allow to # specify ipv4 OR/AND ipv6? some systems have ipv6 disabled if c.Hook.lower() == hook and c.Type.lower() == _type and c.Family.lower() == family: fwcfg.SystemRules[sdx].Chains[cdx].Policy = policy if wantedHook == Fw.Hooks.INPUT.value and wantedPolicy == Fw.Policy.DROP.value: fwcfg.SystemRules[sdx].Chains[cdx].Rules.extend([rule.Rules[0]]) self._nodes.add_fw_config(node_addr, fwcfg) return True return False @staticmethod def new( name="", table=Table.FILTER.value, family=Family.INET.value, ctype="", hook=Hooks.INPUT.value ): chain = ui_pb2.FwChain() chain.Name = name chain.Table = table chain.Family = family chain.Type = ctype chain.Hook = hook return chain # man nft # Table 6. Standard priority names, family and hook compatibility matrix # Name │ Value │ Families │ Hooks # raw │ -300 │ ip, ip6, inet │ all # mangle │ -150 │ ip, ip6, inet │ all # dstnat │ -100 │ ip, ip6, inet │ prerouting # filter │ 0 │ ip, ip6, inet, arp, netdev │ all # security │ 50 │ ip, ip6, inet │ all # srcnat │ 100 │ ip, ip6, inet │ postrouting # class ChainFilter(Chains): """ ChainFilter returns a new chain of type filter. The name of the chain is the one listed with: nft list table inet filter. It corresponds with the hook name, but can be a random name. """ @staticmethod def input(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.INPUT.value chain.Table = Table.FILTER.value chain.Family = family chain.Type = ChainType.FILTER.value chain.Hook = Hooks.INPUT.value return chain @staticmethod def output(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.OUTPUT.value chain.Table = Table.FILTER.value chain.Family = family chain.Type = ChainType.FILTER.value chain.Hook = Hooks.OUTPUT.value return chain @staticmethod def forward(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.FORWARD.value chain.Table = Table.FILTER.value chain.Family = family chain.Type = ChainType.FILTER.value chain.Hook = Hooks.FORWARD.value return chain class ChainMangle(Chains): """ ChainMangle returns a new chain of type mangle. The name of the chain is the one listed with: nft list table inet mangle. It corresponds with the hook name, but can be a random name. """ @staticmethod def output(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.OUTPUT.value chain.Table = Table.MANGLE.value chain.Family = family chain.Type = ChainType.MANGLE.value chain.Hook = Hooks.OUTPUT.value return chain @staticmethod def input(family=Family.INET.value): chain = ui_pb2.FwChain(family=Family.INET.value) chain.Name = Hooks.INPUT.value chain.Table = Table.MANGLE.value chain.Family = family chain.Type = ChainType.MANGLE.value chain.Hook = Hooks.INPUT.value return chain @staticmethod def forward(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.FORWARD.value chain.Table = Table.MANGLE.value chain.Family = family chain.Type = ChainType.MANGLE.value chain.Hook = Hooks.FORWARD.value return chain @staticmethod def prerouting(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.PREROUTING.value chain.Table = Table.MANGLE.value chain.Family = family chain.Type = ChainType.MANGLE.value chain.Hook = Hooks.PREROUTING.value return chain @staticmethod def postrouting(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.POSTROUTING.value chain.Table = Table.MANGLE.value chain.Family = family chain.Type = ChainType.MANGLE.value chain.Hook = Hooks.POSTROUTING.value return chain class ChainDstNAT(Chains): """ ChainDstNAT returns a new chain of type dstnat. The name of the chain is the one listed with: nft list table inet nat. It corresponds with the hook name, but can be a random name. """ @staticmethod def prerouting(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.PREROUTING.value chain.Table = Table.NAT.value chain.Family = family chain.Type = ChainType.DNAT.value chain.Hook = Hooks.PREROUTING.value return chain @staticmethod def output(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.OUTPUT.value chain.Table = Table.NAT.value chain.Family = family chain.Type = ChainType.DNAT.value chain.Hook = Hooks.OUTPUT.value return chain @staticmethod def postrouting(family=Family.INET.value): chain = ui_pb2.FwChain() chain.Name = Hooks.POSTROUTING.value chain.Table = Table.NAT.value chain.Family = family chain.Type = ChainType.SNAT.value chain.Hook = Hooks.POSTROUTING.value return chain ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/firewall/enums.py����������������������������������������������������0000664�0000000�0000000�00000004637�15003540030�0021776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from opensnitch.utils import Enums from opensnitch.config import Config class Verdicts(Enums): EMPTY = "" ACCEPT = Config.ACTION_ACCEPT DROP = Config.ACTION_DROP REJECT = Config.ACTION_REJECT RETURN = Config.ACTION_RETURN QUEUE = Config.ACTION_QUEUE DNAT = Config.ACTION_DNAT SNAT = Config.ACTION_SNAT REDIRECT = Config.ACTION_REDIRECT TPROXY = Config.ACTION_TPROXY #MASQUERADE = Config.ACTION_MASQUERADE #LOG = Config.ACTION_LOG STOP = Config.ACTION_STOP class Policy(Enums): ACCEPT = "accept" DROP = "drop" class Table(Enums): FILTER = "filter" MANGLE = "mangle" NAT = "nat" class Hooks(Enums): INPUT ="input" OUTPUT ="output" FORWARD = "forward" PREROUTING = "prerouting" POSTROUTING = "postrouting" class PortProtocols(Enums): TCPUDP = "tcp,udp" TCP = "tcp" UDP = "udp" UDPLITE = "udplite" SCTP = "sctp" DCCP = "dccp" class Protocols(Enums): TCP = "tcp" UDP = "udp" UDPLITE = "udplite" SCTP = "sctp" DCCP = "dccp" ICMP = "icmp" ICMPv6 = "icmpv6" AH = "ah" ETHERNET = "ethernet" GREP = "gre" IP = "ip" IPIP = "ipip" L2TP = "l2tp" COMP = "comp" IGMP = "igmp" ESP = "esp" RAW = "raw" ENCAP = "encap" class Family(Enums): INET = "inet" IPv4 = "ip" IPv6 = "ip6" class ChainType(Enums): FILTER = "filter" MANGLE = "mangle" ROUTE = "route" SNAT = "natsource" DNAT = "natdest" class Operator(Enums): EQUAL = "==" NOT_EQUAL = "!=" GT_THAN = ">=" GT = ">" LT_THAN = "<=" LT = "<" class TimeUnits(Enums): SECOND = "second" MINUTE = "minute" HOUR = "hour" DAY = "day" class RateUnits(Enums): BYTES = "bytes" KBYTES = "kbytes" MBYTES = "mbytes" GBYTES = "gbytes" class Statements(Enums): """Enum of known (allowed) statements: [tcp,udp,ip] ... """ # we may need in the future: # ANY = tcp,udp,udplite,sctp,dccp TCPUDP = "tcp,udp" TCP = "tcp" UDP = "udp" UDPLITE = "udplite" SCTP = "sctp" DCCP = "dccp" ICMP = "icmp" ICMPv6 = "icmpv6" SPORT = "sport" DPORT = "dport" DADDR = "daddr" SADDR = "saddr" IP = "ip" IP6 = "ip6" IIFNAME = "iifname" OIFNAME = "oifname" CT = "ct" META = "meta" COUNTER = "counter" NAME = "name" LOG = "log" QUOTA = "quota" LIMIT = "limit" �������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/firewall/exprs.py����������������������������������������������������0000664�0000000�0000000�00000006113�15003540030�0021777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() from .enums import * class Expr(): """ Expr returns a new nftables expression that defines a match or an action: tcp dport 22, udp sport 53 log prefix "xxx" Attributes: op (string): operator (==, !=, ...). what (string): name of the statement (tcp, udp, ip, ...) value (tuple): array of values (dport -> 22, etc). """ @staticmethod def new(op, what, values): expr = ui_pb2.Expressions() expr.Statement.Op = op expr.Statement.Name = what for val in values: exprValues = ui_pb2.StatementValues() exprValues.Key = val[0] exprValues.Value = val[1] expr.Statement.Values.extend([exprValues]) return expr class ExprCt(Enums): STATE = "state" NEW = "new" ESTABLISHED = "established" RELATED = "related" INVALID = "invalid" SET = "set" MARK = "mark" class ExprMeta(Enums): SET = "set" MARK = "mark" L4PROTO = "l4proto" SKUID = "skuid" SKGID = "skgid" PROTOCOL = "protocol" PRIORITY = "priority" class ExprIface(Enums): IIFNAME = "iifname" OIFNAME = "oifname" class ExprICMP(Enums): ECHO_REQUEST = "echo-request" ECHO_REPLY = "echo-reply" SOURCE_QUENCH = "source-quench" DEST_UNREACHABLE = "destination-unreachable" ROUTER_ADVERTISEMENT = "router-advertisement" ROUTER_SOLICITATION = "router-solicitation" REDIRECT = "redirect" TIME_EXCEEDED = "time-exceeded" INFO_REQUEST = "info-request" INFO_REPLY = "info-reply" PARAMETER_PROBLEM = "parameter-problem" TIMESTAMP_REQUEST = "timestamp-request" TIMESTAMP_REPLY = "timestamp-reply" ADDRESS_MASK_REQUEST = "address-mask-request" ADDRESS_MASK_REPLY = "address-mask-reply" # IPv6 PACKET_TOO_BIG = "packet-too-big" NEIGHBOUR_SOLICITATION = "neighbour-solicitation" NEIGHBOUR_ADVERTISEMENT = "neighbour-advertisement" class ExprICMPRejectCodes(Enums): NO_ROUTE = "no-route" PROT_UNREACHABLE = "prot-unreachable" PORT_UNREACHABLE = "port-unreachable" NET_UNREACHABLE = "net-unreachable" ADDR_UNREACHABLE = "addr-unreachable" HOST_UNREACHABLE = "host-unreachable" NET_PROHIBITED = "net-prohibited" HOST_PROHIBITED = "host-prohibited" ADMIN_PROHIBITED = "admin-prohibited" REJECT_ROUTE = "reject-route" REJECT_POLICY_FAIL = "policy-fail" class ExprLog(Enums): LOG = "log" LEVEL = "level" PREFIX = "prefix" class ExprLogLevels(Enums): EMERG = "emerg" ALERT = "alert" CRIT = "crit" ERR = "err" WARN = "warn" NOTICE = "notice" INFO = "info" DEBUG = "debug" AUDIT = "audit" class ExprCounter(Enums): COUNTER = "counter" PACKETS = "packets" BYTES = "bytes" NAME = "name" class ExprLimit(Enums): OVER = "over" LIMIT = "limit" UNITS = "units" RATE_UNITS = "rate-units" TIME_UNITS = "time-units" class ExprQuota(Enums): QUOTA = "quota" OVER = "over" UNTIL = "until" USED = "used" UNIT = "unit" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/firewall/profiles.py�������������������������������������������������0000664�0000000�0000000�00000007745�15003540030�0022475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ import glob import json import os.path class Profiles(): @staticmethod def load_predefined_profiles(): profiles = glob.glob("/etc/opensnitchd/system-fw.d/profiles/*.profile") p = [] for pr_path in profiles: with open(pr_path) as f: p.append({os.path.basename(pr_path): json.load(f)}) return p class ProfileAcceptOutput(): value = { "Name": "accept-mangle-output", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "output", "Policy": "accept", "Rules": [ ] } class ProfileDropOutput(): value = { "Name": "drop-mangle-output", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "output", "Policy": "drop", "Rules": [ ] } class ProfileAcceptForward(): value = { "Name": "accept-mangle-forward", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "forward", "Policy": "accept", "Rules": [ ] } class ProfileDropForward(): value = { "Name": "drop-mangle-forward", "Table": "mangle", "Family": "inet", "Priority": "", "Type": "mangle", "Hook": "forward", "Policy": "drop", "Rules": [ ] } class ProfileAcceptInput(): value = { "Name": "accept-filter-input", "Table": "filter", "Family": "inet", "Priority": "", "Type": "filter", "Hook": "input", "Policy": "accept", "Rules": [ ] } class ProfileDropInput(): """ Set input filter table policy to DROP and add the needed rules to allow outbound connections. """ # TODO: delete dropInput profile's rules value = { "Name": "drop-filter-input", "Table": "filter", "Family": "inet", "Priority": "", "Type": "filter", "Hook": "input", "Policy": "drop", "Rules": [ { "Table": "", "Chain": "", "UUID": "profile-drop-inbound-2d7e6fe4-c21d-11ec-99a6-3c970e298b0c", "Enabled": True, "Position": "0", "Description": "[profile-drop-inbound] allow localhost connections", "Parameters": "", "Expressions": [ { "Statement": { "Op": "", "Name": "iifname", "Values": [ { "Key": "lo", "Value": "" } ] } } ], "Target": "accept", "TargetParameters": "" }, { "Enabled": True, "Description": "[profile-drop-inbound] allow established,related connections", "UUID": "profile-drop-inbound-e1fc1a1c-c21c-11ec-9a2a-3c970e298b0c", "Expressions": [ { "Statement": { "Op": "", "Name": "ct", "Values": [ { "Key": "state", "Value": "related" }, { "Key": "state", "Value": "established" } ] } } ], "Target": "accept", "TargetParameters": "" } ] } ���������������������������opensnitch-1.6.9/ui/opensnitch/firewall/rules.py����������������������������������������������������0000664�0000000�0000000�00000026712�15003540030�0021777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtCore import QObject, pyqtSignal from PyQt5.QtCore import QCoreApplication as QC from google.protobuf.json_format import MessageToJson import uuid import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() from .enums import Operator from .exprs import ExprLog class Rules(QObject): rulesUpdated = pyqtSignal() # Fields defined in the protobuf, to be used as constants on other parts. FIELD_UUID = "UUID" FIELD_ENABLED = "Enabled" FIELD_TARGET = "Target" def __init__(self, nodes): QObject.__init__(self) self._nodes = nodes self.rulesUpdated.connect(self._cb_rules_updated) def _cb_rules_updated(self): pass def add(self, addr, rule): """Add a new rule to the corresponding table on the given node """ node = self._nodes.get_node(addr) if node == None or not 'firewall' in node: return False, QC.translate("firewall", "rule not found by its ID.") if self.is_duplicated(addr, rule): return False, QC.translate("firewall", "duplicated.") for sdx, n in enumerate(node['firewall'].SystemRules): for cdx, c in enumerate(n.Chains): if c.Name == rule.Name and \ c.Hook == rule.Hook and \ c.Table == rule.Table and \ c.Family == rule.Family and \ c.Type == rule.Type: node['firewall'].SystemRules[sdx].Chains[cdx].Rules.extend([rule.Rules[0]]) node['fwrules'][rule.Rules[0].UUID] = rule self._nodes.add_fw_config(addr, node['firewall']) self._nodes.add_fw_rules(addr, node['fwrules']) self.rulesUpdated.emit() return True return False, QC.translate("firewall", "firewall table/chain not properly configured.") def insert(self, addr, rule, position=0): """Insert a new rule to the corresponding table on the given node """ node = self._nodes.get_node(addr) if node == None or not 'firewall' in node: return False, QC.translate("firewall", "this node doesn't have a firewall configuration, review it.") if self.is_duplicated(addr, rule): return False, QC.translate("firewall", "duplicated") for sdx, n in enumerate(node['firewall'].SystemRules): for cdx, c in enumerate(n.Chains): if c.Name == rule.Name and \ c.Hook == rule.Hook and \ c.Table == rule.Table and \ c.Family == rule.Family and \ c.Type == rule.Type: if hasattr(node['firewall'].SystemRules[sdx].Chains[cdx].Rules, "insert"): node['firewall'].SystemRules[sdx].Chains[cdx].Rules.insert(int(position), rule.Rules[0]) else: node['firewall'].SystemRules[sdx].Chains[cdx].Rules.extend([rule.Rules[0]]) node['fwrules'][rule.Rules[0].UUID] = rule self._nodes.add_fw_config(addr, node['firewall']) self._nodes.add_fw_rules(addr, node['fwrules']) self.rulesUpdated.emit() return True, "" return False, QC.translate("firewall", "firewall table/chain not properly configured.") def update(self, addr, uuid, rule): node = self._nodes.get_node(addr) if node == None or not 'firewall' in node: return False, QC.translate("firewall", "this node doesn't have a firewall configuration, review it.") for sdx, n in enumerate(node['firewall'].SystemRules): for cdx, c in enumerate(n.Chains): for rdx, r in enumerate(c.Rules): if r.UUID == uuid: c.Rules[rdx].CopyFrom(rule.Rules[0]) node['firewall'].SystemRules[sdx].Chains[cdx].Rules[rdx].CopyFrom(rule.Rules[0]) self._nodes.add_fw_config(addr, node['firewall']) node['fwrules'][uuid] = rule self._nodes.add_fw_rules(addr, node['fwrules']) self.rulesUpdated.emit() return True, "" return False, QC.translate("firewall", "rule not found by its ID.") def get(self): rules = [] for node in self._nodes.get_nodes(): node_rules = self.get_by_node(node) rules += node_rules return rules def delete(self, addr, uuid): node = self._nodes.get_node(addr) if node == None or not 'firewall' in node: return False, None for sdx, n in enumerate(node['firewall'].SystemRules): for cdx, c in enumerate(n.Chains): for idx, r in enumerate(c.Rules): if r.UUID == uuid: del node['firewall'].SystemRules[sdx].Chains[cdx].Rules[idx] self._nodes.add_fw_config(addr, node['firewall']) if uuid in node['fwrules']: del node['fwrules'][uuid] self._nodes.add_fw_rules(addr, node['fwrules']) else: # raise Error("rules doesn't have UUID field") return False, None self.rulesUpdated.emit() return True, node['firewall'] return False, None def get_by_node(self, addr): rules = [] node = self._nodes.get_node(addr) if node == None: return rules if not 'firewall' in node: return rules for u in node['firewall'].SystemRules: for c in u.Chains: for r in c.Rules: rules.append(Rules.to_array(addr, c, r)) return rules def get_by_uuid(self, addr, uuid): rules = [] node = self._nodes.get_node(addr) if node == None: return rules if not 'firewall' in node: return rules for u in node['firewall'].SystemRules: for c in u.Chains: for r in c.Rules: if r.UUID == uuid: return r return None def swap(self, view, addr, uuid, old_pos, new_pos): """ swap changes the order of 2 rows. The list of rules is ordered from top to bottom: 0,1,2,3... so a click on the down button sums +1, a click on the up button rest -1 """ node = self._nodes.get_node(addr) if node == None: return if not 'firewall' in node: return for sdx, c in enumerate(node['firewall'].SystemRules): for cdx, u in enumerate(c.Chains): nrules = len(u.Rules) for rdx, r in enumerate(u.Rules): # is the last rule if new_pos > nrules and new_pos < nrules: break if u.Rules[rdx].UUID == uuid: old_rule = u.Rules[old_pos] new_rule = ui_pb2.FwRule() new_rule.CopyFrom(u.Rules[new_pos]) node['firewall'].SystemRules[sdx].Chains[cdx].Rules[new_pos].CopyFrom(old_rule) node['firewall'].SystemRules[sdx].Chains[cdx].Rules[old_pos].CopyFrom(new_rule) self._nodes.add_fw_config(addr, node['firewall']) #self._nodes.add_fw_rules(addr, node['fwrules']) self.rulesUpdated.emit() return True return False def is_duplicated(self, addr, orig_rule): # we need to duplicate the rule, otherwise we'd modify the UUID of the # orig rule. temp_c = ui_pb2.FwChain() temp_c.CopyFrom(orig_rule) # the UUID will be different, so zero it out. # but keep a copy of the original one. orig_uuid = temp_c.Rules[0].UUID temp_c.Rules[0].UUID = "" node = self._nodes.get_node(addr) if node == None: return False if not 'firewall' in node: return False for n in node['firewall'].SystemRules: for c in n.Chains: if c.Name == temp_c.Name and \ c.Hook == temp_c.Hook and \ c.Table == temp_c.Table and \ c.Family == temp_c.Family and \ c.Type == temp_c.Type: for rdx, r in enumerate(c.Rules): uuid = c.Rules[rdx].UUID c.Rules[rdx].UUID = "" is_equal = (c.Rules[rdx].SerializeToString() == temp_c.Rules[0].SerializeToString() or orig_uuid == uuid) c.Rules[rdx].UUID = uuid if is_equal: return True return False @staticmethod def new( enabled=True, _uuid="", description="", expressions=None, target="", target_parms="" ): rule = ui_pb2.FwRule() if _uuid == "": rule.UUID = str(uuid.uuid1()) else: rule.UUID = _uuid rule.Enabled = enabled rule.Description = description if expressions != None: rule.Expressions.extend([expressions]) rule.Target = target rule.TargetParameters = target_parms return rule @staticmethod def new_flat(c, r): """Create a new "flat" rule from a hierarchical one. Transform from: { xx: { yy: { to: {xx:, yy} """ chain = ui_pb2.FwChain() chain.CopyFrom(c) del chain.Rules[:] chain.Rules.extend([r]) return chain @staticmethod def to_dict(sysRules): """Transform json/protobuf struct to flat structure. This is the default format used to find rules in the table view. """ rules={} for s in sysRules: for c in s.Chains: if len(c.Rules) == 0: continue for r in c.Rules: rules[r.UUID] = Rules.new_flat(c, r) return rules @staticmethod def to_json(rule): try: return MessageToJson(rule) except: return None @staticmethod def to_array(addr, chain, rule): cols = [] cols.append(rule.UUID) cols.append(addr) cols.append(chain.Name) cols.append(chain.Table) cols.append(chain.Family) cols.append(chain.Hook) cols.append(str(rule.Enabled)) cols.append(rule.Description) exprs = "" for e in rule.Expressions: exprs += "{0} {1}".format( e.Statement.Name, "".join( [ "{0} {1}{2} ".format( h.Key, e.Statement.Op + " " if e.Statement.Op != Operator.EQUAL.value else "", "\"{0}\"".format(h.Value) if h.Key == ExprLog.PREFIX.value else h.Value ) for h in e.Statement.Values ] ) ) cols.append(exprs) cols.append(rule.Target) cols.append(rule.TargetParameters) return cols ������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/firewall/utils.py����������������������������������������������������0000664�0000000�0000000�00000001432�15003540030�0021775�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from google.protobuf import __version__ as protobuf_version from .enums import * class Utils(): @staticmethod def isExprPort(value): """Return true if the value is valid for a port based rule: nft add rule ... tcp dport 22 accept """ return value == Statements.TCP.value or \ value == Statements.UDP.value or \ value == Statements.UDPLITE.value or \ value == Statements.SCTP.value or \ value == Statements.DCCP.value @staticmethod def isProtobufSupported(): """ The protobuf operations append() and insert() were introduced on 3.8.0 version. """ vparts = protobuf_version.split(".") return int(vparts[0]) >= 3 and int(vparts[1]) >= 8 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/nodes.py�������������������������������������������������������������0000664�0000000�0000000�00000034114�15003540030�0020143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot from queue import Queue from datetime import datetime import time import json from opensnitch.database import Database from opensnitch.config import Config from opensnitch.utils import NetworkInterfaces from opensnitch.rules import Rules import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() class Nodes(QObject): __instance = None nodesUpdated = pyqtSignal(int) # total LOG_TAG = "[Nodes]: " ONLINE = "\u2713 online" OFFLINE = "\u2613 offline" WARNING = "\u26a0" @staticmethod def instance(): if Nodes.__instance == None: Nodes.__instance = Nodes() return Nodes.__instance def __init__(self): QObject.__init__(self) self._db = Database.instance() self._rules = Rules() self._nodes = {} self._notifications_sent = {} self._interfaces = NetworkInterfaces() def count(self): return len(self._nodes) def add(self, _peer, client_config=None): try: proto, addr = self.get_addr(_peer) peer = proto+":"+addr if peer not in self._nodes: self._nodes[peer] = { 'notifications': Queue(), 'online': True, 'last_seen': datetime.now() } else: self._nodes[peer]['last_seen'] = datetime.now() self._nodes[peer]['online'] = True self.add_data(peer, client_config) self.update(peer) self.nodesUpdated.emit(self.count()) return self._nodes[peer], peer except Exception as e: print(self.LOG_TAG, "exception adding/updating node: ", e, "addr:", addr, "config:", client_config) return None, None def add_data(self, addr, client_config): if client_config != None: self._nodes[addr]['data'] = self.get_client_config(client_config) self.add_fw_config(addr, client_config.systemFirewall) self._rules.add_rules(addr, client_config.rules) def add_fw_config(self, addr, fwconfig): self._nodes[addr]['firewall'] = fwconfig def add_fw_rules(self, addr, fwconfig): self._nodes[addr]['fwrules'] = fwconfig def add_rule(self, time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created): # don't add rule if the user has selected to exclude temporary # rules if duration in Config.RULES_DURATION_FILTER: return self._rules.add(time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created) def add_rules(self, addr, rules): try: self._rules.add_rules(addr, rules) except Exception as e: print(self.LOG_TAG + " exception adding node to db: ", e) def delete_rule(self, rule_name, addr, callback): deleted_rule = self._rules.delete(rule_name, addr, callback) if deleted_rule == None: print(self.LOG_TAG, "error deleting rule", rule_name) return None, None noti = ui_pb2.Notification(type=ui_pb2.DELETE_RULE, rules=[deleted_rule]) if addr != None: nid = self.send_notification(addr, noti, callback) else: nid = self.send_notifications(noti, callback) return nid, noti def delete_rule_by_field(self, field, values): return self._rules.delete_by_field(field, values) def rule_to_json(self, addr, rule_name): return self._rules.rule_to_json(addr, rule_name) def export_rule(self, addr, rule_name, outdir): return self._rules.export_rule(addr, rule_name, outdir) def export_rules(self, addr, outdir): return self._rules.export_rules(addr, outdir) def import_rules(self, addr=None, rulesdir="", callback=None): rules_list = self._rules.import_rules(rulesdir) if rules_list == None: return None, None, None notif = ui_pb2.Notification( id=int(str(time.time()).replace(".", "")), type=ui_pb2.CHANGE_RULE, data="", rules=rules_list) if addr != None: nid = self.send_notification(addr, notif, callback) else: nid = self.send_notifications(notif, callback) return nid, notif, rules_list def update_rule_time(self, time, rule_name, addr): self._rules.update_time(time, rule_name, addr) def delete_all(self): self.send_notifications(None) self._nodes = {} self.nodesUpdated.emit(self.count()) def delete(self, peer): try: proto, addr = self.get_addr(peer) addr = "%s:%s" % (proto, addr) # Force the node to get one new item from queue, # in order to loop and exit. self._nodes[addr]['notifications'].put(None) except: addr = peer if addr in self._nodes: del self._nodes[addr] self.nodesUpdated.emit(self.count()) def get(self): return self._nodes def get_node(self, addr): try: return self._nodes[addr] except Exception as e: return None def get_nodes(self): return self._nodes def get_node_config(self, addr): try: if addr not in self._nodes: return None return self._nodes[addr]['data'].config except Exception as e: print(self.LOG_TAG + " exception get_node_config(): ", e) return None def get_client_config(self, client_config): try: node_config = json.loads(client_config.config) if 'LogLevel' not in node_config: node_config['LogLevel'] = 1 client_config.config = json.dumps(node_config) except Exception as e: print(self.LOG_TAG, "exception parsing client config", e) return client_config def get_addr(self, peer): try: peer = peer.split(":") # WA for backward compatibility if peer[0] == "unix" and peer[1] == "": peer[1] = "/local" return peer[0], peer[1] except: print(self.LOG_TAG, "get_addr() error getting addr:", peer) return peer def is_local(self, addr): if addr.startswith("unix"): return True if addr.startswith("ipv4") or addr.startswith("ipv6"): ifaces = self._interfaces.list() for name in ifaces: if ifaces[name] in addr: return True return False def get_notifications(self): notlist = [] try: for c in self._nodes: if self._nodes[c]['online'] == False: continue if self._nodes[c]['notifications'].empty(): continue notif = self._nodes[c]['notifications'].get(False) if notif != None: self._nodes[c]['notifications'].task_done() notlist.append(notif) except Exception as e: print(self.LOG_TAG + " exception get_notifications(): ", e) return notlist def save_node_config(self, addr, config): try: self._nodes[addr]['data'].config = config except Exception as e: print(self.LOG_TAG + " exception saving node config: ", e, addr, config) def save_nodes_config(self, config): try: for c in self._nodes: self._nodes[c]['data'].config = config except Exception as e: print(self.LOG_TAG + " exception saving nodes config: ", e, config) def change_node_config(self, addr, config, _callback): _cfg = json.dumps(config, indent=" ") notif = ui_pb2.Notification( id=int(str(time.time()).replace(".", "")), type=ui_pb2.CHANGE_CONFIG, data=_cfg, rules=[]) self.save_node_config(addr, _cfg) return self.send_notification(addr, notif, _callback), notif def start_interception(self, _addr=None, _callback=None): return self.firewall(not_type=ui_pb2.ENABLE_INTERCEPTION, addr=_addr, callback=_callback) def stop_interception(self, _addr=None, _callback=None): return self.firewall(not_type=ui_pb2.DISABLE_INTERCEPTION, addr=_addr, callback=_callback) def firewall(self, not_type=ui_pb2.ENABLE_INTERCEPTION, addr=None, callback=None): noti = ui_pb2.Notification(clientName="", serverName="", type=not_type, data="", rules=[]) if addr == None: nid = self.send_notifications(noti, callback) else: nid = self.send_notification(addr, noti, callback) return nid, noti def send_notification(self, addr, notification, callback_signal=None): try: notification.id = int(str(time.time()).replace(".", "")) if addr not in self._nodes: # FIXME: the reply is sent before we return the notification id if callback_signal != None: callback_signal.emit( ui_pb2.NotificationReply( id=notification.id, code=ui_pb2.ERROR, data="node not connected: {0}".format(addr) ) ) return notification.id self._notifications_sent[notification.id] = { 'callback': callback_signal, 'type': notification.type } self._nodes[addr]['notifications'].put(notification) except Exception as e: print(self.LOG_TAG + " exception sending notification: ", e, addr, notification) if callback_signal != None: callback_signal.emit( ui_pb2.NotificationReply( id=notification.id, code=ui_pb2.ERROR, data="Notification not sent ({0}):<br>{1}".format(addr, e) ) ) return notification.id def send_notifications(self, notification, callback_signal=None): """ Enqueues a notification to the clients queue. It'll be retrieved and delivered by get_notifications """ try: notification.id = int(str(time.time()).replace(".", "")) for c in self._nodes: self._nodes[c]['notifications'].put(notification) self._notifications_sent[notification.id] = { 'callback': callback_signal, 'type': notification.type } except Exception as e: print(self.LOG_TAG + " exception sending notifications: ", e, notification) return notification.id def reply_notification(self, addr, reply): try: if reply == None: print(self.LOG_TAG, " reply notification None") return if reply.id not in self._notifications_sent: print(self.LOG_TAG, " reply notification not in the list:", reply.id) return if self._notifications_sent[reply.id] == None: print(self.LOG_TAG, " reply notification body empty:", reply.id) return if self._notifications_sent[reply.id]['callback'] != None: self._notifications_sent[reply.id]['callback'].emit(reply) # delete only one-time notifications # we need the ID of streaming notifications from the server # (monitor_process for example) to keep track of the data sent to us. if self._notifications_sent[reply.id]['type'] != ui_pb2.MONITOR_PROCESS: del self._notifications_sent[reply.id] except Exception as e: print(self.LOG_TAG, "notification exception:", e) def stop_notifications(self): """Send a dummy notification to force Notifications class to exit. """ exit_noti = ui_pb2.Notification(clientName="", serverName="", type=0, data="", rules=[]) self.send_notifications(exit_noti) def update(self, peer, status=ONLINE): try: proto, addr = self.get_addr(peer) self._db.update("nodes", "hostname=?,version=?,last_connection=?,status=?", ( self._nodes[proto+":"+addr]['data'].name, self._nodes[proto+":"+addr]['data'].version, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), status, "{0}:{1}".format(proto, addr)), "addr=?" ) except Exception as e: print(self.LOG_TAG + " exception updating DB: ", e, peer) def update_all(self, status=OFFLINE): try: for peer in self._nodes: self._db.update("nodes", "hostname=?,version=?,last_connection=?,status=?", ( self._nodes[peer]['data'].name, self._nodes[peer]['data'].version, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), status, peer), "addr=?" ) except Exception as e: print(self.LOG_TAG + " exception updating nodes: ", e) def reset_status(self): try: self._db.update("nodes", "status=?", (self.OFFLINE,)) except Exception as e: print(self.LOG_TAG + " exception resetting nodes status: ", e) def reload_fw(self, addr, fw_config, callback): notif = ui_pb2.Notification( id=int(str(time.time()).replace(".", "")), type=ui_pb2.RELOAD_FW_RULES, sysFirewall=fw_config ) nid = self.send_notification(addr, notif, callback) return nid, notif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/notifications.py�����������������������������������������������������0000664�0000000�0000000�00000012410�15003540030�0021677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtCore import QCoreApplication as QC import os from opensnitch.utils import Utils from opensnitch.config import Config class DesktopNotifications(): """DesktopNotifications display informative pop-ups using the system D-Bus. The notifications are handled and configured by the system. The notification daemon also decides where to show the notifications, as well as how to group them. The body of a notification supports markup (if the implementation supports it): https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#markup Basically: <a>, <u>, <b>, <i> and <img>. New lines can be added with the regular \n. It also support actions (buttons). https://notify2.readthedocs.io/en/latest/ """ _cfg = Config.init() # list of hints: # https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#hints HINT_DESKTOP_ENTRY = "desktop-entry" CATEGORY_NETWORK = "network" EXPIRES_DEFAULT = 0 NEVER_EXPIRES = -1 URGENCY_LOW = 0 URGENCY_NORMAL = 1 URGENCY_CRITICAL = 2 # must be a string ACTION_ID_OPEN = "action-open" ACTION_ID_ALLOW = "action-allow" ACTION_ID_DENY = "action-deny" def __init__(self): self.ACTION_OPEN = QC.translate("popups", "Open") self.ACTION_ALLOW = QC.translate("popups", "Allow") self.ACTION_DENY = QC.translate("popups", "Deny") self.IS_LIBNOTIFY_AVAILABLE = True self.DOES_SUPPORT_ACTIONS = True try: import notify2 self.ntf2 = notify2 mloop = 'glib' # First try to initialise the D-Bus connection with the given # mainloop. # If it fails, we'll try to initialise it without it. try: self.ntf2.init("opensnitch", mainloop=mloop) except Exception: self.DOES_SUPPORT_ACTIONS = False self.ntf2.init("opensnitch") # usually because dbus mainloop is not initiated, specially # with 'qt' # FIXME: figure out how to init it, or how to connect to an # existing session. print("DesktopNotifications(): system doesn't support actions. Available capabilities:") print(self.ntf2.get_server_caps()) # Example: ['actions', 'action-icons', 'body', 'body-markup', 'icon-static', 'persistence', 'sound'] if ('actions' not in self.ntf2.get_server_caps()): self.DOES_SUPPORT_ACTIONS = False except Exception as e: print("DesktopNotifications not available (install python3-notify2):", e) self.IS_LIBNOTIFY_AVAILABLE = False def is_available(self): return self.IS_LIBNOTIFY_AVAILABLE def are_enabled(self): return self._cfg.getBool(Config.NOTIFICATIONS_ENABLED, True) def support_actions(self): """Returns true if the notifications daemon support actions(buttons). This depends on 2 factors: - If the notification server actually supports it (get_server_caps()). - If there's a dbus instance running. """ return self.DOES_SUPPORT_ACTIONS def show(self, title, body, icon="dialog-information", urgency=URGENCY_NORMAL, callback=None): try: ntf = self.ntf2.Notification(title, body, icon) ntf.set_urgency(urgency) ntf.set_category(self.CATEGORY_NETWORK) # used to display our app icon and name. # Note: setting this Hint causes some DEs to call opensnitch_ui.desktop file, # that as of today, kills and relaunches the current opensnitch-ui process. #ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui") if self.DOES_SUPPORT_ACTIONS and callback != None: ntf.add_action(self.ACTION_ID_OPEN, self.ACTION_OPEN, callback) ntf.show() except Exception as e: print("[notifications] show() exception:", e) raise Exception("[notifications] show() exception:", e) # TODO: # - construct a rule with the default configured parameters. # - create a common dialogs/prompt.py:_send_rule(), maybe in utils.py def ask(self, connection, timeout, callback): c = connection title = QC.translate("popups", "New outgoing connection") body = c.process_path + "\n" body = body + QC.translate("popups", "is connecting to <b>%s</b> on %s port %d") % ( \ c.dst_host or c.dst_ip, c.protocol.upper(), c.dst_port ) ntf = self.ntf2.Notification(title, body, "dialog-warning") timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15) ntf.set_timeout(timeout * 1000) ntf.timeout = timeout * 1000 if self.DOES_SUPPORT_ACTIONS: ntf.set_urgency(self.ntf2.URGENCY_CRITICAL) ntf.add_action(self.ACTION_ID_ALLOW, self.ACTION_ALLOW, callback, connection) ntf.add_action(self.ACTION_ID_DENY, self.ACTION_DENY, callback, connection) #ntf.add_action("open-gui", QC.translate("popups", "View"), callback, connection) ntf.set_category(self.CATEGORY_NETWORK) ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui") ntf.show() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/proto/���������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017621�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/proto/__init__.py����������������������������������������������������0000664�0000000�0000000�00000004307�15003540030�0021736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2018 Simone Margaritelli # 2019-2025 Gustavo Iñiguez Goia # # This file is part of OpenSnitch. # # OpenSnitch 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. # # OpenSnitch 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 for more details. # # You should have received a copy of the GNU General Public License # along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. from packaging.version import Version import importlib from opensnitch.utils import Versions # Protobuffers compiled with protobuf < 3.20.0 are incompatible with # protobuf >= 4.0.0 # https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#gui-does-not-show-up # # In order to solve this issue, we provide several protobuffers: # proto.ui_pb2* for protobuf >= 4.0.0 # proto.pre3200.ui_pb2* for protobuf >= 3.6.0 and < 3.20.0 # # To avoid import errors, each protobuffer must be placed in its own directory, # and the name of the protobuffer files must be named with the syntax # <prefix>_pb2.py/<prefix>_pb2_grpc.py: # ui_pb2.py and ui_pb2_grpc.py default_pb = "opensnitch.proto.ui_pb2" default_grpc = "opensnitch.proto.ui_pb2_grpc" old_pb = "opensnitch.proto.pre3200.ui_pb2" old_grpc = "opensnitch.proto.pre3200.ui_pb2_grpc" def import_(): """load the protobuffer needed based on the grpc and protobuffer version installed in the system. """ try: gui_version, grpc_version, proto_version = Versions.get() proto_ver = default_pb grpc_ver = default_grpc if Version(proto_version) < Version("3.20.0"): proto_ver = old_pb grpc_ver = old_grpc return importlib.import_module(proto_ver), importlib.import_module(grpc_ver) except Exception as e: print("error importing protobuffer: ", repr(e)) return importlib.import_module(default_pb, default_grpc) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/proto/pre3200/�������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020714�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/proto/pre3200/ui_pb2.py����������������������������������������������0000664�0000000�0000000�00000262667�15003540030�0022471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: ui.proto import sys _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='ui.proto', package='protocol', syntax='proto3', serialized_options=_b('Z3github.com/evilsocket/opensnitch/daemon/ui/protocol'), serialized_pb=_b('\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\x89\x02\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x10\n\x08io_reads\x18\t \x01(\x04\x12\x11\n\tio_writes\x18\n \x01(\x04\x12\x11\n\tnet_reads\x18\x0b \x01(\x04\x12\x12\n\nnet_writes\x18\x0c \x01(\x04\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xc4\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xa5\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x13\n\x0fMONITOR_PROCESS\x10\r\x12\x18\n\x14STOP_MONITOR_PROCESS\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3') ) _ACTION = _descriptor.EnumDescriptor( name='Action', full_name='protocol.Action', filename=None, file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( name='NONE', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='ENABLE_INTERCEPTION', index=1, number=1, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='DISABLE_INTERCEPTION', index=2, number=2, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='ENABLE_FIREWALL', index=3, number=3, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='DISABLE_FIREWALL', index=4, number=4, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='RELOAD_FW_RULES', index=5, number=5, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='CHANGE_CONFIG', index=6, number=6, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='ENABLE_RULE', index=7, number=7, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='DISABLE_RULE', index=8, number=8, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='DELETE_RULE', index=9, number=9, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='CHANGE_RULE', index=10, number=10, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='LOG_LEVEL', index=11, number=11, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='STOP', index=12, number=12, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='MONITOR_PROCESS', index=13, number=13, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='STOP_MONITOR_PROCESS', index=14, number=14, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=3795, serialized_end=4088, ) _sym_db.RegisterEnumDescriptor(_ACTION) Action = enum_type_wrapper.EnumTypeWrapper(_ACTION) _NOTIFICATIONREPLYCODE = _descriptor.EnumDescriptor( name='NotificationReplyCode', full_name='protocol.NotificationReplyCode', filename=None, file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( name='OK', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='ERROR', index=1, number=1, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=4090, serialized_end=4132, ) _sym_db.RegisterEnumDescriptor(_NOTIFICATIONREPLYCODE) NotificationReplyCode = enum_type_wrapper.EnumTypeWrapper(_NOTIFICATIONREPLYCODE) NONE = 0 ENABLE_INTERCEPTION = 1 DISABLE_INTERCEPTION = 2 ENABLE_FIREWALL = 3 DISABLE_FIREWALL = 4 RELOAD_FW_RULES = 5 CHANGE_CONFIG = 6 ENABLE_RULE = 7 DISABLE_RULE = 8 DELETE_RULE = 9 CHANGE_RULE = 10 LOG_LEVEL = 11 STOP = 12 MONITOR_PROCESS = 13 STOP_MONITOR_PROCESS = 14 OK = 0 ERROR = 1 _ALERT_PRIORITY = _descriptor.EnumDescriptor( name='Priority', full_name='protocol.Alert.Priority', filename=None, file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( name='LOW', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='MEDIUM', index=1, number=1, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='HIGH', index=2, number=2, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=357, serialized_end=398, ) _sym_db.RegisterEnumDescriptor(_ALERT_PRIORITY) _ALERT_TYPE = _descriptor.EnumDescriptor( name='Type', full_name='protocol.Alert.Type', filename=None, file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( name='ERROR', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='WARNING', index=1, number=1, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='INFO', index=2, number=2, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=400, serialized_end=440, ) _sym_db.RegisterEnumDescriptor(_ALERT_TYPE) _ALERT_ACTION = _descriptor.EnumDescriptor( name='Action', full_name='protocol.Alert.Action', filename=None, file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( name='NONE', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='SHOW_ALERT', index=1, number=1, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='SAVE_TO_DB', index=2, number=2, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=442, serialized_end=492, ) _sym_db.RegisterEnumDescriptor(_ALERT_ACTION) _ALERT_WHAT = _descriptor.EnumDescriptor( name='What', full_name='protocol.Alert.What', filename=None, file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( name='GENERIC', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='PROC_MONITOR', index=1, number=1, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='FIREWALL', index=2, number=2, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='CONNECTION', index=3, number=3, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='RULE', index=4, number=4, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='NETLINK', index=5, number=5, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( name='KERNEL_EVENT', index=6, number=6, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=494, serialized_end=602, ) _sym_db.RegisterEnumDescriptor(_ALERT_WHAT) _ALERT = _descriptor.Descriptor( name='Alert', full_name='protocol.Alert', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='protocol.Alert.id', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='type', full_name='protocol.Alert.type', index=1, number=2, type=14, cpp_type=8, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='action', full_name='protocol.Alert.action', index=2, number=3, type=14, cpp_type=8, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='priority', full_name='protocol.Alert.priority', index=3, number=4, type=14, cpp_type=8, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='what', full_name='protocol.Alert.what', index=4, number=5, type=14, cpp_type=8, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='text', full_name='protocol.Alert.text', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='proc', full_name='protocol.Alert.proc', index=6, number=8, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='conn', full_name='protocol.Alert.conn', index=7, number=9, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='rule', full_name='protocol.Alert.rule', index=8, number=10, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='fwrule', full_name='protocol.Alert.fwrule', index=9, number=11, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ _ALERT_PRIORITY, _ALERT_TYPE, _ALERT_ACTION, _ALERT_WHAT, ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ _descriptor.OneofDescriptor( name='data', full_name='protocol.Alert.data', index=0, containing_type=None, fields=[]), ], serialized_start=23, serialized_end=610, ) _MSGRESPONSE = _descriptor.Descriptor( name='MsgResponse', full_name='protocol.MsgResponse', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='protocol.MsgResponse.id', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=612, serialized_end=637, ) _EVENT = _descriptor.Descriptor( name='Event', full_name='protocol.Event', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='time', full_name='protocol.Event.time', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='connection', full_name='protocol.Event.connection', index=1, number=2, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='rule', full_name='protocol.Event.rule', index=2, number=3, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='unixnano', full_name='protocol.Event.unixnano', index=3, number=4, type=3, cpp_type=2, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=639, serialized_end=750, ) _STATISTICS_BYPROTOENTRY = _descriptor.Descriptor( name='ByProtoEntry', full_name='protocol.Statistics.ByProtoEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Statistics.ByProtoEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Statistics.ByProtoEntry.value', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1315, serialized_end=1361, ) _STATISTICS_BYADDRESSENTRY = _descriptor.Descriptor( name='ByAddressEntry', full_name='protocol.Statistics.ByAddressEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Statistics.ByAddressEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Statistics.ByAddressEntry.value', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1363, serialized_end=1411, ) _STATISTICS_BYHOSTENTRY = _descriptor.Descriptor( name='ByHostEntry', full_name='protocol.Statistics.ByHostEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Statistics.ByHostEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Statistics.ByHostEntry.value', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1413, serialized_end=1458, ) _STATISTICS_BYPORTENTRY = _descriptor.Descriptor( name='ByPortEntry', full_name='protocol.Statistics.ByPortEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Statistics.ByPortEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Statistics.ByPortEntry.value', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1460, serialized_end=1505, ) _STATISTICS_BYUIDENTRY = _descriptor.Descriptor( name='ByUidEntry', full_name='protocol.Statistics.ByUidEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Statistics.ByUidEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Statistics.ByUidEntry.value', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1507, serialized_end=1551, ) _STATISTICS_BYEXECUTABLEENTRY = _descriptor.Descriptor( name='ByExecutableEntry', full_name='protocol.Statistics.ByExecutableEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Statistics.ByExecutableEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Statistics.ByExecutableEntry.value', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1553, serialized_end=1604, ) _STATISTICS = _descriptor.Descriptor( name='Statistics', full_name='protocol.Statistics', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='daemon_version', full_name='protocol.Statistics.daemon_version', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='rules', full_name='protocol.Statistics.rules', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='uptime', full_name='protocol.Statistics.uptime', index=2, number=3, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='dns_responses', full_name='protocol.Statistics.dns_responses', index=3, number=4, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='connections', full_name='protocol.Statistics.connections', index=4, number=5, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='ignored', full_name='protocol.Statistics.ignored', index=5, number=6, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='accepted', full_name='protocol.Statistics.accepted', index=6, number=7, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='dropped', full_name='protocol.Statistics.dropped', index=7, number=8, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='rule_hits', full_name='protocol.Statistics.rule_hits', index=8, number=9, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='rule_misses', full_name='protocol.Statistics.rule_misses', index=9, number=10, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='by_proto', full_name='protocol.Statistics.by_proto', index=10, number=11, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='by_address', full_name='protocol.Statistics.by_address', index=11, number=12, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='by_host', full_name='protocol.Statistics.by_host', index=12, number=13, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='by_port', full_name='protocol.Statistics.by_port', index=13, number=14, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='by_uid', full_name='protocol.Statistics.by_uid', index=14, number=15, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='by_executable', full_name='protocol.Statistics.by_executable', index=15, number=16, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='events', full_name='protocol.Statistics.events', index=16, number=17, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[_STATISTICS_BYPROTOENTRY, _STATISTICS_BYADDRESSENTRY, _STATISTICS_BYHOSTENTRY, _STATISTICS_BYPORTENTRY, _STATISTICS_BYUIDENTRY, _STATISTICS_BYEXECUTABLEENTRY, ], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=753, serialized_end=1604, ) _PINGREQUEST = _descriptor.Descriptor( name='PingRequest', full_name='protocol.PingRequest', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='protocol.PingRequest.id', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='stats', full_name='protocol.PingRequest.stats', index=1, number=2, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1606, serialized_end=1668, ) _PINGREPLY = _descriptor.Descriptor( name='PingReply', full_name='protocol.PingReply', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='protocol.PingReply.id', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1670, serialized_end=1693, ) _PROCESS_ENVENTRY = _descriptor.Descriptor( name='EnvEntry', full_name='protocol.Process.EnvEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Process.EnvEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Process.EnvEntry.value', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1919, serialized_end=1961, ) _PROCESS = _descriptor.Descriptor( name='Process', full_name='protocol.Process', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='pid', full_name='protocol.Process.pid', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='ppid', full_name='protocol.Process.ppid', index=1, number=2, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='uid', full_name='protocol.Process.uid', index=2, number=3, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='comm', full_name='protocol.Process.comm', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='path', full_name='protocol.Process.path', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='args', full_name='protocol.Process.args', index=5, number=6, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='env', full_name='protocol.Process.env', index=6, number=7, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='cwd', full_name='protocol.Process.cwd', index=7, number=8, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='io_reads', full_name='protocol.Process.io_reads', index=8, number=9, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='io_writes', full_name='protocol.Process.io_writes', index=9, number=10, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='net_reads', full_name='protocol.Process.net_reads', index=10, number=11, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='net_writes', full_name='protocol.Process.net_writes', index=11, number=12, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[_PROCESS_ENVENTRY, ], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1696, serialized_end=1961, ) _CONNECTION_PROCESSENVENTRY = _descriptor.Descriptor( name='ProcessEnvEntry', full_name='protocol.Connection.ProcessEnvEntry', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='key', full_name='protocol.Connection.ProcessEnvEntry.key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='value', full_name='protocol.Connection.ProcessEnvEntry.value', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=_b('8\001'), is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2243, serialized_end=2292, ) _CONNECTION = _descriptor.Descriptor( name='Connection', full_name='protocol.Connection', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='protocol', full_name='protocol.Connection.protocol', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='src_ip', full_name='protocol.Connection.src_ip', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='src_port', full_name='protocol.Connection.src_port', index=2, number=3, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='dst_ip', full_name='protocol.Connection.dst_ip', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='dst_host', full_name='protocol.Connection.dst_host', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='dst_port', full_name='protocol.Connection.dst_port', index=5, number=6, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='user_id', full_name='protocol.Connection.user_id', index=6, number=7, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='process_id', full_name='protocol.Connection.process_id', index=7, number=8, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='process_path', full_name='protocol.Connection.process_path', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='process_cwd', full_name='protocol.Connection.process_cwd', index=9, number=10, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='process_args', full_name='protocol.Connection.process_args', index=10, number=11, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='process_env', full_name='protocol.Connection.process_env', index=11, number=12, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[_CONNECTION_PROCESSENVENTRY, ], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=1964, serialized_end=2292, ) _OPERATOR = _descriptor.Descriptor( name='Operator', full_name='protocol.Operator', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='type', full_name='protocol.Operator.type', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='operand', full_name='protocol.Operator.operand', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='data', full_name='protocol.Operator.data', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='sensitive', full_name='protocol.Operator.sensitive', index=3, number=4, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='list', full_name='protocol.Operator.list', index=4, number=5, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2294, serialized_end=2402, ) _RULE = _descriptor.Descriptor( name='Rule', full_name='protocol.Rule', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='created', full_name='protocol.Rule.created', index=0, number=1, type=3, cpp_type=2, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='name', full_name='protocol.Rule.name', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='description', full_name='protocol.Rule.description', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='enabled', full_name='protocol.Rule.enabled', index=3, number=4, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='precedence', full_name='protocol.Rule.precedence', index=4, number=5, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='nolog', full_name='protocol.Rule.nolog', index=5, number=6, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='action', full_name='protocol.Rule.action', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='duration', full_name='protocol.Rule.duration', index=7, number=8, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='operator', full_name='protocol.Rule.operator', index=8, number=9, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2405, serialized_end=2587, ) _STATEMENTVALUES = _descriptor.Descriptor( name='StatementValues', full_name='protocol.StatementValues', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='Key', full_name='protocol.StatementValues.Key', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Value', full_name='protocol.StatementValues.Value', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2589, serialized_end=2634, ) _STATEMENT = _descriptor.Descriptor( name='Statement', full_name='protocol.Statement', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='Op', full_name='protocol.Statement.Op', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Name', full_name='protocol.Statement.Name', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Values', full_name='protocol.Statement.Values', index=2, number=3, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2636, serialized_end=2716, ) _EXPRESSIONS = _descriptor.Descriptor( name='Expressions', full_name='protocol.Expressions', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='Statement', full_name='protocol.Expressions.Statement', index=0, number=1, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2718, serialized_end=2771, ) _FWRULE = _descriptor.Descriptor( name='FwRule', full_name='protocol.FwRule', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='Table', full_name='protocol.FwRule.Table', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Chain', full_name='protocol.FwRule.Chain', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='UUID', full_name='protocol.FwRule.UUID', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Enabled', full_name='protocol.FwRule.Enabled', index=3, number=4, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Position', full_name='protocol.FwRule.Position', index=4, number=5, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Description', full_name='protocol.FwRule.Description', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Parameters', full_name='protocol.FwRule.Parameters', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Expressions', full_name='protocol.FwRule.Expressions', index=7, number=8, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Target', full_name='protocol.FwRule.Target', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='TargetParameters', full_name='protocol.FwRule.TargetParameters', index=9, number=10, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2774, serialized_end=2988, ) _FWCHAIN = _descriptor.Descriptor( name='FwChain', full_name='protocol.FwChain', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='Name', full_name='protocol.FwChain.Name', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Table', full_name='protocol.FwChain.Table', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Family', full_name='protocol.FwChain.Family', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Priority', full_name='protocol.FwChain.Priority', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Type', full_name='protocol.FwChain.Type', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Hook', full_name='protocol.FwChain.Hook', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Policy', full_name='protocol.FwChain.Policy', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Rules', full_name='protocol.FwChain.Rules', index=7, number=8, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=2991, serialized_end=3140, ) _FWCHAINS = _descriptor.Descriptor( name='FwChains', full_name='protocol.FwChains', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='Rule', full_name='protocol.FwChains.Rule', index=0, number=1, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Chains', full_name='protocol.FwChains.Chains', index=1, number=2, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=3142, serialized_end=3219, ) _SYSFIREWALL = _descriptor.Descriptor( name='SysFirewall', full_name='protocol.SysFirewall', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='Enabled', full_name='protocol.SysFirewall.Enabled', index=0, number=1, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='Version', full_name='protocol.SysFirewall.Version', index=1, number=2, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='SystemRules', full_name='protocol.SysFirewall.SystemRules', index=2, number=3, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=3221, serialized_end=3309, ) _CLIENTCONFIG = _descriptor.Descriptor( name='ClientConfig', full_name='protocol.ClientConfig', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='protocol.ClientConfig.id', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='name', full_name='protocol.ClientConfig.name', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='version', full_name='protocol.ClientConfig.version', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='isFirewallRunning', full_name='protocol.ClientConfig.isFirewallRunning', index=3, number=4, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='config', full_name='protocol.ClientConfig.config', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='logLevel', full_name='protocol.ClientConfig.logLevel', index=5, number=6, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='rules', full_name='protocol.ClientConfig.rules', index=6, number=7, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='systemFirewall', full_name='protocol.ClientConfig.systemFirewall', index=7, number=8, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=3312, serialized_end=3508, ) _NOTIFICATION = _descriptor.Descriptor( name='Notification', full_name='protocol.Notification', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='protocol.Notification.id', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='clientName', full_name='protocol.Notification.clientName', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='serverName', full_name='protocol.Notification.serverName', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='type', full_name='protocol.Notification.type', index=3, number=4, type=14, cpp_type=8, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='data', full_name='protocol.Notification.data', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='rules', full_name='protocol.Notification.rules', index=5, number=6, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='sysFirewall', full_name='protocol.Notification.sysFirewall', index=6, number=7, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=3511, serialized_end=3698, ) _NOTIFICATIONREPLY = _descriptor.Descriptor( name='NotificationReply', full_name='protocol.NotificationReply', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='protocol.NotificationReply.id', index=0, number=1, type=4, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='code', full_name='protocol.NotificationReply.code', index=1, number=2, type=14, cpp_type=8, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='data', full_name='protocol.NotificationReply.data', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=3700, serialized_end=3792, ) _ALERT.fields_by_name['type'].enum_type = _ALERT_TYPE _ALERT.fields_by_name['action'].enum_type = _ALERT_ACTION _ALERT.fields_by_name['priority'].enum_type = _ALERT_PRIORITY _ALERT.fields_by_name['what'].enum_type = _ALERT_WHAT _ALERT.fields_by_name['proc'].message_type = _PROCESS _ALERT.fields_by_name['conn'].message_type = _CONNECTION _ALERT.fields_by_name['rule'].message_type = _RULE _ALERT.fields_by_name['fwrule'].message_type = _FWRULE _ALERT_PRIORITY.containing_type = _ALERT _ALERT_TYPE.containing_type = _ALERT _ALERT_ACTION.containing_type = _ALERT _ALERT_WHAT.containing_type = _ALERT _ALERT.oneofs_by_name['data'].fields.append( _ALERT.fields_by_name['text']) _ALERT.fields_by_name['text'].containing_oneof = _ALERT.oneofs_by_name['data'] _ALERT.oneofs_by_name['data'].fields.append( _ALERT.fields_by_name['proc']) _ALERT.fields_by_name['proc'].containing_oneof = _ALERT.oneofs_by_name['data'] _ALERT.oneofs_by_name['data'].fields.append( _ALERT.fields_by_name['conn']) _ALERT.fields_by_name['conn'].containing_oneof = _ALERT.oneofs_by_name['data'] _ALERT.oneofs_by_name['data'].fields.append( _ALERT.fields_by_name['rule']) _ALERT.fields_by_name['rule'].containing_oneof = _ALERT.oneofs_by_name['data'] _ALERT.oneofs_by_name['data'].fields.append( _ALERT.fields_by_name['fwrule']) _ALERT.fields_by_name['fwrule'].containing_oneof = _ALERT.oneofs_by_name['data'] _EVENT.fields_by_name['connection'].message_type = _CONNECTION _EVENT.fields_by_name['rule'].message_type = _RULE _STATISTICS_BYPROTOENTRY.containing_type = _STATISTICS _STATISTICS_BYADDRESSENTRY.containing_type = _STATISTICS _STATISTICS_BYHOSTENTRY.containing_type = _STATISTICS _STATISTICS_BYPORTENTRY.containing_type = _STATISTICS _STATISTICS_BYUIDENTRY.containing_type = _STATISTICS _STATISTICS_BYEXECUTABLEENTRY.containing_type = _STATISTICS _STATISTICS.fields_by_name['by_proto'].message_type = _STATISTICS_BYPROTOENTRY _STATISTICS.fields_by_name['by_address'].message_type = _STATISTICS_BYADDRESSENTRY _STATISTICS.fields_by_name['by_host'].message_type = _STATISTICS_BYHOSTENTRY _STATISTICS.fields_by_name['by_port'].message_type = _STATISTICS_BYPORTENTRY _STATISTICS.fields_by_name['by_uid'].message_type = _STATISTICS_BYUIDENTRY _STATISTICS.fields_by_name['by_executable'].message_type = _STATISTICS_BYEXECUTABLEENTRY _STATISTICS.fields_by_name['events'].message_type = _EVENT _PINGREQUEST.fields_by_name['stats'].message_type = _STATISTICS _PROCESS_ENVENTRY.containing_type = _PROCESS _PROCESS.fields_by_name['env'].message_type = _PROCESS_ENVENTRY _CONNECTION_PROCESSENVENTRY.containing_type = _CONNECTION _CONNECTION.fields_by_name['process_env'].message_type = _CONNECTION_PROCESSENVENTRY _OPERATOR.fields_by_name['list'].message_type = _OPERATOR _RULE.fields_by_name['operator'].message_type = _OPERATOR _STATEMENT.fields_by_name['Values'].message_type = _STATEMENTVALUES _EXPRESSIONS.fields_by_name['Statement'].message_type = _STATEMENT _FWRULE.fields_by_name['Expressions'].message_type = _EXPRESSIONS _FWCHAIN.fields_by_name['Rules'].message_type = _FWRULE _FWCHAINS.fields_by_name['Rule'].message_type = _FWRULE _FWCHAINS.fields_by_name['Chains'].message_type = _FWCHAIN _SYSFIREWALL.fields_by_name['SystemRules'].message_type = _FWCHAINS _CLIENTCONFIG.fields_by_name['rules'].message_type = _RULE _CLIENTCONFIG.fields_by_name['systemFirewall'].message_type = _SYSFIREWALL _NOTIFICATION.fields_by_name['type'].enum_type = _ACTION _NOTIFICATION.fields_by_name['rules'].message_type = _RULE _NOTIFICATION.fields_by_name['sysFirewall'].message_type = _SYSFIREWALL _NOTIFICATIONREPLY.fields_by_name['code'].enum_type = _NOTIFICATIONREPLYCODE DESCRIPTOR.message_types_by_name['Alert'] = _ALERT DESCRIPTOR.message_types_by_name['MsgResponse'] = _MSGRESPONSE DESCRIPTOR.message_types_by_name['Event'] = _EVENT DESCRIPTOR.message_types_by_name['Statistics'] = _STATISTICS DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST DESCRIPTOR.message_types_by_name['PingReply'] = _PINGREPLY DESCRIPTOR.message_types_by_name['Process'] = _PROCESS DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['Operator'] = _OPERATOR DESCRIPTOR.message_types_by_name['Rule'] = _RULE DESCRIPTOR.message_types_by_name['StatementValues'] = _STATEMENTVALUES DESCRIPTOR.message_types_by_name['Statement'] = _STATEMENT DESCRIPTOR.message_types_by_name['Expressions'] = _EXPRESSIONS DESCRIPTOR.message_types_by_name['FwRule'] = _FWRULE DESCRIPTOR.message_types_by_name['FwChain'] = _FWCHAIN DESCRIPTOR.message_types_by_name['FwChains'] = _FWCHAINS DESCRIPTOR.message_types_by_name['SysFirewall'] = _SYSFIREWALL DESCRIPTOR.message_types_by_name['ClientConfig'] = _CLIENTCONFIG DESCRIPTOR.message_types_by_name['Notification'] = _NOTIFICATION DESCRIPTOR.message_types_by_name['NotificationReply'] = _NOTIFICATIONREPLY DESCRIPTOR.enum_types_by_name['Action'] = _ACTION DESCRIPTOR.enum_types_by_name['NotificationReplyCode'] = _NOTIFICATIONREPLYCODE _sym_db.RegisterFileDescriptor(DESCRIPTOR) Alert = _reflection.GeneratedProtocolMessageType('Alert', (_message.Message,), { 'DESCRIPTOR' : _ALERT, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Alert) }) _sym_db.RegisterMessage(Alert) MsgResponse = _reflection.GeneratedProtocolMessageType('MsgResponse', (_message.Message,), { 'DESCRIPTOR' : _MSGRESPONSE, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.MsgResponse) }) _sym_db.RegisterMessage(MsgResponse) Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), { 'DESCRIPTOR' : _EVENT, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Event) }) _sym_db.RegisterMessage(Event) Statistics = _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), { 'ByProtoEntry' : _reflection.GeneratedProtocolMessageType('ByProtoEntry', (_message.Message,), { 'DESCRIPTOR' : _STATISTICS_BYPROTOENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statistics.ByProtoEntry) }) , 'ByAddressEntry' : _reflection.GeneratedProtocolMessageType('ByAddressEntry', (_message.Message,), { 'DESCRIPTOR' : _STATISTICS_BYADDRESSENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statistics.ByAddressEntry) }) , 'ByHostEntry' : _reflection.GeneratedProtocolMessageType('ByHostEntry', (_message.Message,), { 'DESCRIPTOR' : _STATISTICS_BYHOSTENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statistics.ByHostEntry) }) , 'ByPortEntry' : _reflection.GeneratedProtocolMessageType('ByPortEntry', (_message.Message,), { 'DESCRIPTOR' : _STATISTICS_BYPORTENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statistics.ByPortEntry) }) , 'ByUidEntry' : _reflection.GeneratedProtocolMessageType('ByUidEntry', (_message.Message,), { 'DESCRIPTOR' : _STATISTICS_BYUIDENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statistics.ByUidEntry) }) , 'ByExecutableEntry' : _reflection.GeneratedProtocolMessageType('ByExecutableEntry', (_message.Message,), { 'DESCRIPTOR' : _STATISTICS_BYEXECUTABLEENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statistics.ByExecutableEntry) }) , 'DESCRIPTOR' : _STATISTICS, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statistics) }) _sym_db.RegisterMessage(Statistics) _sym_db.RegisterMessage(Statistics.ByProtoEntry) _sym_db.RegisterMessage(Statistics.ByAddressEntry) _sym_db.RegisterMessage(Statistics.ByHostEntry) _sym_db.RegisterMessage(Statistics.ByPortEntry) _sym_db.RegisterMessage(Statistics.ByUidEntry) _sym_db.RegisterMessage(Statistics.ByExecutableEntry) PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), { 'DESCRIPTOR' : _PINGREQUEST, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.PingRequest) }) _sym_db.RegisterMessage(PingRequest) PingReply = _reflection.GeneratedProtocolMessageType('PingReply', (_message.Message,), { 'DESCRIPTOR' : _PINGREPLY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.PingReply) }) _sym_db.RegisterMessage(PingReply) Process = _reflection.GeneratedProtocolMessageType('Process', (_message.Message,), { 'EnvEntry' : _reflection.GeneratedProtocolMessageType('EnvEntry', (_message.Message,), { 'DESCRIPTOR' : _PROCESS_ENVENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Process.EnvEntry) }) , 'DESCRIPTOR' : _PROCESS, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Process) }) _sym_db.RegisterMessage(Process) _sym_db.RegisterMessage(Process.EnvEntry) Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), { 'ProcessEnvEntry' : _reflection.GeneratedProtocolMessageType('ProcessEnvEntry', (_message.Message,), { 'DESCRIPTOR' : _CONNECTION_PROCESSENVENTRY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Connection.ProcessEnvEntry) }) , 'DESCRIPTOR' : _CONNECTION, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Connection) }) _sym_db.RegisterMessage(Connection) _sym_db.RegisterMessage(Connection.ProcessEnvEntry) Operator = _reflection.GeneratedProtocolMessageType('Operator', (_message.Message,), { 'DESCRIPTOR' : _OPERATOR, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Operator) }) _sym_db.RegisterMessage(Operator) Rule = _reflection.GeneratedProtocolMessageType('Rule', (_message.Message,), { 'DESCRIPTOR' : _RULE, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Rule) }) _sym_db.RegisterMessage(Rule) StatementValues = _reflection.GeneratedProtocolMessageType('StatementValues', (_message.Message,), { 'DESCRIPTOR' : _STATEMENTVALUES, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.StatementValues) }) _sym_db.RegisterMessage(StatementValues) Statement = _reflection.GeneratedProtocolMessageType('Statement', (_message.Message,), { 'DESCRIPTOR' : _STATEMENT, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Statement) }) _sym_db.RegisterMessage(Statement) Expressions = _reflection.GeneratedProtocolMessageType('Expressions', (_message.Message,), { 'DESCRIPTOR' : _EXPRESSIONS, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Expressions) }) _sym_db.RegisterMessage(Expressions) FwRule = _reflection.GeneratedProtocolMessageType('FwRule', (_message.Message,), { 'DESCRIPTOR' : _FWRULE, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.FwRule) }) _sym_db.RegisterMessage(FwRule) FwChain = _reflection.GeneratedProtocolMessageType('FwChain', (_message.Message,), { 'DESCRIPTOR' : _FWCHAIN, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.FwChain) }) _sym_db.RegisterMessage(FwChain) FwChains = _reflection.GeneratedProtocolMessageType('FwChains', (_message.Message,), { 'DESCRIPTOR' : _FWCHAINS, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.FwChains) }) _sym_db.RegisterMessage(FwChains) SysFirewall = _reflection.GeneratedProtocolMessageType('SysFirewall', (_message.Message,), { 'DESCRIPTOR' : _SYSFIREWALL, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.SysFirewall) }) _sym_db.RegisterMessage(SysFirewall) ClientConfig = _reflection.GeneratedProtocolMessageType('ClientConfig', (_message.Message,), { 'DESCRIPTOR' : _CLIENTCONFIG, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.ClientConfig) }) _sym_db.RegisterMessage(ClientConfig) Notification = _reflection.GeneratedProtocolMessageType('Notification', (_message.Message,), { 'DESCRIPTOR' : _NOTIFICATION, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.Notification) }) _sym_db.RegisterMessage(Notification) NotificationReply = _reflection.GeneratedProtocolMessageType('NotificationReply', (_message.Message,), { 'DESCRIPTOR' : _NOTIFICATIONREPLY, '__module__' : 'ui_pb2' # @@protoc_insertion_point(class_scope:protocol.NotificationReply) }) _sym_db.RegisterMessage(NotificationReply) DESCRIPTOR._options = None _STATISTICS_BYPROTOENTRY._options = None _STATISTICS_BYADDRESSENTRY._options = None _STATISTICS_BYHOSTENTRY._options = None _STATISTICS_BYPORTENTRY._options = None _STATISTICS_BYUIDENTRY._options = None _STATISTICS_BYEXECUTABLEENTRY._options = None _PROCESS_ENVENTRY._options = None _CONNECTION_PROCESSENVENTRY._options = None _UI = _descriptor.ServiceDescriptor( name='UI', full_name='protocol.UI', file=DESCRIPTOR, index=0, serialized_options=None, serialized_start=4135, serialized_end=4438, methods=[ _descriptor.MethodDescriptor( name='Ping', full_name='protocol.UI.Ping', index=0, containing_service=None, input_type=_PINGREQUEST, output_type=_PINGREPLY, serialized_options=None, ), _descriptor.MethodDescriptor( name='AskRule', full_name='protocol.UI.AskRule', index=1, containing_service=None, input_type=_CONNECTION, output_type=_RULE, serialized_options=None, ), _descriptor.MethodDescriptor( name='Subscribe', full_name='protocol.UI.Subscribe', index=2, containing_service=None, input_type=_CLIENTCONFIG, output_type=_CLIENTCONFIG, serialized_options=None, ), _descriptor.MethodDescriptor( name='Notifications', full_name='protocol.UI.Notifications', index=3, containing_service=None, input_type=_NOTIFICATIONREPLY, output_type=_NOTIFICATION, serialized_options=None, ), _descriptor.MethodDescriptor( name='PostAlert', full_name='protocol.UI.PostAlert', index=4, containing_service=None, input_type=_ALERT, output_type=_MSGRESPONSE, serialized_options=None, ), ]) _sym_db.RegisterServiceDescriptor(_UI) DESCRIPTOR.services_by_name['UI'] = _UI # @@protoc_insertion_point(module_scope) �������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/proto/pre3200/ui_pb2_grpc.py�����������������������������������������0000664�0000000�0000000�00000010137�15003540030�0023463�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! import grpc from . import ui_pb2 as ui__pb2 class UIStub(object): # missing associated documentation comment in .proto file pass def __init__(self, channel): """Constructor. Args: channel: A grpc.Channel. """ self.Ping = channel.unary_unary( '/protocol.UI/Ping', request_serializer=ui__pb2.PingRequest.SerializeToString, response_deserializer=ui__pb2.PingReply.FromString, ) self.AskRule = channel.unary_unary( '/protocol.UI/AskRule', request_serializer=ui__pb2.Connection.SerializeToString, response_deserializer=ui__pb2.Rule.FromString, ) self.Subscribe = channel.unary_unary( '/protocol.UI/Subscribe', request_serializer=ui__pb2.ClientConfig.SerializeToString, response_deserializer=ui__pb2.ClientConfig.FromString, ) self.Notifications = channel.stream_stream( '/protocol.UI/Notifications', request_serializer=ui__pb2.NotificationReply.SerializeToString, response_deserializer=ui__pb2.Notification.FromString, ) self.PostAlert = channel.unary_unary( '/protocol.UI/PostAlert', request_serializer=ui__pb2.Alert.SerializeToString, response_deserializer=ui__pb2.MsgResponse.FromString, ) class UIServicer(object): # missing associated documentation comment in .proto file pass def Ping(self, request, context): # missing associated documentation comment in .proto file pass context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def AskRule(self, request, context): # missing associated documentation comment in .proto file pass context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def Subscribe(self, request, context): # missing associated documentation comment in .proto file pass context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def Notifications(self, request_iterator, context): # missing associated documentation comment in .proto file pass context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def PostAlert(self, request, context): # missing associated documentation comment in .proto file pass context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def add_UIServicer_to_server(servicer, server): rpc_method_handlers = { 'Ping': grpc.unary_unary_rpc_method_handler( servicer.Ping, request_deserializer=ui__pb2.PingRequest.FromString, response_serializer=ui__pb2.PingReply.SerializeToString, ), 'AskRule': grpc.unary_unary_rpc_method_handler( servicer.AskRule, request_deserializer=ui__pb2.Connection.FromString, response_serializer=ui__pb2.Rule.SerializeToString, ), 'Subscribe': grpc.unary_unary_rpc_method_handler( servicer.Subscribe, request_deserializer=ui__pb2.ClientConfig.FromString, response_serializer=ui__pb2.ClientConfig.SerializeToString, ), 'Notifications': grpc.stream_stream_rpc_method_handler( servicer.Notifications, request_deserializer=ui__pb2.NotificationReply.FromString, response_serializer=ui__pb2.Notification.SerializeToString, ), 'PostAlert': grpc.unary_unary_rpc_method_handler( servicer.PostAlert, request_deserializer=ui__pb2.Alert.FromString, response_serializer=ui__pb2.MsgResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'protocol.UI', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/proto/ui_pb2.py������������������������������������������������������0000664�0000000�0000000�00000031335�15003540030�0021360�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: ui.proto """Generated protocol buffer code.""" from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\x89\x02\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x10\n\x08io_reads\x18\t \x01(\x04\x12\x11\n\tio_writes\x18\n \x01(\x04\x12\x11\n\tnet_reads\x18\x0b \x01(\x04\x12\x12\n\nnet_writes\x18\x0c \x01(\x04\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xc4\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xa5\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x13\n\x0fMONITOR_PROCESS\x10\r\x12\x18\n\x14STOP_MONITOR_PROCESS\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ui_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'Z3github.com/evilsocket/opensnitch/daemon/ui/protocol' _STATISTICS_BYPROTOENTRY._options = None _STATISTICS_BYPROTOENTRY._serialized_options = b'8\001' _STATISTICS_BYADDRESSENTRY._options = None _STATISTICS_BYADDRESSENTRY._serialized_options = b'8\001' _STATISTICS_BYHOSTENTRY._options = None _STATISTICS_BYHOSTENTRY._serialized_options = b'8\001' _STATISTICS_BYPORTENTRY._options = None _STATISTICS_BYPORTENTRY._serialized_options = b'8\001' _STATISTICS_BYUIDENTRY._options = None _STATISTICS_BYUIDENTRY._serialized_options = b'8\001' _STATISTICS_BYEXECUTABLEENTRY._options = None _STATISTICS_BYEXECUTABLEENTRY._serialized_options = b'8\001' _PROCESS_ENVENTRY._options = None _PROCESS_ENVENTRY._serialized_options = b'8\001' _CONNECTION_PROCESSENVENTRY._options = None _CONNECTION_PROCESSENVENTRY._serialized_options = b'8\001' _ACTION._serialized_start=3795 _ACTION._serialized_end=4088 _NOTIFICATIONREPLYCODE._serialized_start=4090 _NOTIFICATIONREPLYCODE._serialized_end=4132 _ALERT._serialized_start=23 _ALERT._serialized_end=610 _ALERT_PRIORITY._serialized_start=357 _ALERT_PRIORITY._serialized_end=398 _ALERT_TYPE._serialized_start=400 _ALERT_TYPE._serialized_end=440 _ALERT_ACTION._serialized_start=442 _ALERT_ACTION._serialized_end=492 _ALERT_WHAT._serialized_start=494 _ALERT_WHAT._serialized_end=602 _MSGRESPONSE._serialized_start=612 _MSGRESPONSE._serialized_end=637 _EVENT._serialized_start=639 _EVENT._serialized_end=750 _STATISTICS._serialized_start=753 _STATISTICS._serialized_end=1604 _STATISTICS_BYPROTOENTRY._serialized_start=1315 _STATISTICS_BYPROTOENTRY._serialized_end=1361 _STATISTICS_BYADDRESSENTRY._serialized_start=1363 _STATISTICS_BYADDRESSENTRY._serialized_end=1411 _STATISTICS_BYHOSTENTRY._serialized_start=1413 _STATISTICS_BYHOSTENTRY._serialized_end=1458 _STATISTICS_BYPORTENTRY._serialized_start=1460 _STATISTICS_BYPORTENTRY._serialized_end=1505 _STATISTICS_BYUIDENTRY._serialized_start=1507 _STATISTICS_BYUIDENTRY._serialized_end=1551 _STATISTICS_BYEXECUTABLEENTRY._serialized_start=1553 _STATISTICS_BYEXECUTABLEENTRY._serialized_end=1604 _PINGREQUEST._serialized_start=1606 _PINGREQUEST._serialized_end=1668 _PINGREPLY._serialized_start=1670 _PINGREPLY._serialized_end=1693 _PROCESS._serialized_start=1696 _PROCESS._serialized_end=1961 _PROCESS_ENVENTRY._serialized_start=1919 _PROCESS_ENVENTRY._serialized_end=1961 _CONNECTION._serialized_start=1964 _CONNECTION._serialized_end=2292 _CONNECTION_PROCESSENVENTRY._serialized_start=2243 _CONNECTION_PROCESSENVENTRY._serialized_end=2292 _OPERATOR._serialized_start=2294 _OPERATOR._serialized_end=2402 _RULE._serialized_start=2405 _RULE._serialized_end=2587 _STATEMENTVALUES._serialized_start=2589 _STATEMENTVALUES._serialized_end=2634 _STATEMENT._serialized_start=2636 _STATEMENT._serialized_end=2716 _EXPRESSIONS._serialized_start=2718 _EXPRESSIONS._serialized_end=2771 _FWRULE._serialized_start=2774 _FWRULE._serialized_end=2988 _FWCHAIN._serialized_start=2991 _FWCHAIN._serialized_end=3140 _FWCHAINS._serialized_start=3142 _FWCHAINS._serialized_end=3219 _SYSFIREWALL._serialized_start=3221 _SYSFIREWALL._serialized_end=3309 _CLIENTCONFIG._serialized_start=3312 _CLIENTCONFIG._serialized_end=3508 _NOTIFICATION._serialized_start=3511 _NOTIFICATION._serialized_end=3698 _NOTIFICATIONREPLY._serialized_start=3700 _NOTIFICATIONREPLY._serialized_end=3792 _UI._serialized_start=4135 _UI._serialized_end=4438 # @@protoc_insertion_point(module_scope) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/proto/ui_pb2_grpc.py�������������������������������������������������0000664�0000000�0000000�00000017506�15003540030�0022377�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc from . import ui_pb2 as ui__pb2 class UIStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): """Constructor. Args: channel: A grpc.Channel. """ self.Ping = channel.unary_unary( '/protocol.UI/Ping', request_serializer=ui__pb2.PingRequest.SerializeToString, response_deserializer=ui__pb2.PingReply.FromString, ) self.AskRule = channel.unary_unary( '/protocol.UI/AskRule', request_serializer=ui__pb2.Connection.SerializeToString, response_deserializer=ui__pb2.Rule.FromString, ) self.Subscribe = channel.unary_unary( '/protocol.UI/Subscribe', request_serializer=ui__pb2.ClientConfig.SerializeToString, response_deserializer=ui__pb2.ClientConfig.FromString, ) self.Notifications = channel.stream_stream( '/protocol.UI/Notifications', request_serializer=ui__pb2.NotificationReply.SerializeToString, response_deserializer=ui__pb2.Notification.FromString, ) self.PostAlert = channel.unary_unary( '/protocol.UI/PostAlert', request_serializer=ui__pb2.Alert.SerializeToString, response_deserializer=ui__pb2.MsgResponse.FromString, ) class UIServicer(object): """Missing associated documentation comment in .proto file.""" def Ping(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def AskRule(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def Subscribe(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def Notifications(self, request_iterator, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def PostAlert(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def add_UIServicer_to_server(servicer, server): rpc_method_handlers = { 'Ping': grpc.unary_unary_rpc_method_handler( servicer.Ping, request_deserializer=ui__pb2.PingRequest.FromString, response_serializer=ui__pb2.PingReply.SerializeToString, ), 'AskRule': grpc.unary_unary_rpc_method_handler( servicer.AskRule, request_deserializer=ui__pb2.Connection.FromString, response_serializer=ui__pb2.Rule.SerializeToString, ), 'Subscribe': grpc.unary_unary_rpc_method_handler( servicer.Subscribe, request_deserializer=ui__pb2.ClientConfig.FromString, response_serializer=ui__pb2.ClientConfig.SerializeToString, ), 'Notifications': grpc.stream_stream_rpc_method_handler( servicer.Notifications, request_deserializer=ui__pb2.NotificationReply.FromString, response_serializer=ui__pb2.Notification.SerializeToString, ), 'PostAlert': grpc.unary_unary_rpc_method_handler( servicer.PostAlert, request_deserializer=ui__pb2.Alert.FromString, response_serializer=ui__pb2.MsgResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'protocol.UI', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. class UI(object): """Missing associated documentation comment in .proto file.""" @staticmethod def Ping(request, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/protocol.UI/Ping', ui__pb2.PingRequest.SerializeToString, ui__pb2.PingReply.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def AskRule(request, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/protocol.UI/AskRule', ui__pb2.Connection.SerializeToString, ui__pb2.Rule.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def Subscribe(request, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/protocol.UI/Subscribe', ui__pb2.ClientConfig.SerializeToString, ui__pb2.ClientConfig.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def Notifications(request_iterator, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.stream_stream(request_iterator, target, '/protocol.UI/Notifications', ui__pb2.NotificationReply.SerializeToString, ui__pb2.Notification.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def PostAlert(request, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/protocol.UI/PostAlert', ui__pb2.Alert.SerializeToString, ui__pb2.MsgResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017247�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/__init__.py������������������������������������������������������0000664�0000000�0000000�00000000000�15003540030�0021346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/firewall.ui������������������������������������������������������0000664�0000000�0000000�00000041012�15003540030�0021411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Dialog</class> <widget class="QDialog" name="Dialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>440</width> <height>463</height> </rect> </property> <property name="windowTitle"> <string>Firewall</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QTabWidget" name="tabWidget"> <property name="styleSheet"> <string notr="true">QTabBar { alignment: center; }</string> </property> <property name="tabPosition"> <enum>QTabWidget::South</enum> </property> <property name="currentIndex"> <number>0</number> </property> <property name="documentMode"> <bool>true</bool> </property> <widget class="QWidget" name="tabMain"> <attribute name="title"> <string/> </attribute> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <property name="leftMargin"> <number>10</number> </property> <property name="topMargin"> <number>5</number> </property> <property name="rightMargin"> <number>10</number> </property> <property name="bottomMargin"> <number>10</number> </property> <item> <widget class="QLabel" name="label_2"> <property name="text"> <string><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> </widget> </item> <item> <spacer name="horizontalSpacer_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QSlider" name="sliderFwEnable"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="maximumSize"> <size> <width>48</width> <height>16777215</height> </size> </property> <property name="styleSheet"> <string notr="true">QSlider::groove:horizontal { background-color: transparent; border: 0px solid #424242; height: 20px; border-radius: 4px; } QSlider::handle:horizontal { background-color: rgb(154, 153, 150); border: 1px solid rgb(119, 118, 123); width: 20px; height: 20px; line-height: 20px; /* do not delete this */ /*margin-left: -5px;*/ margin-top: -2px; margin-bottom: -2px; border-radius: 5px; } QSlider::handle:horizontal:hover { background-color: rgb(79, 91, 98); border-radius: 5px; } QSlider::add-page:horizontal { border-radius: 4px; background: rgb(237, 51, 59); border: 1px solid rgb(192, 28, 40); } QSlider::sub-page:horizontal { border-radius: 4px; background: rgb(139, 195, 74); border: 1px solid rgb(67, 190, 24); }</string> </property> <property name="maximum"> <number>1</number> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="invertedAppearance"> <bool>false</bool> </property> <property name="tickPosition"> <enum>QSlider::TicksBothSides</enum> </property> </widget> </item> </layout> </item> <item> <widget class="QLabel" name="lblStatusIcon"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="textFormat"> <enum>Qt::AutoText</enum> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="openExternalLinks"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> </property> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> <spacer name="horizontalSpacer_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="Line" name="line"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>96</width> <height>0</height> </size> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item> <spacer name="horizontalSpacer_5"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> </layout> </item> <item> <widget class="QLabel" name="lblFwStatus"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">color: rgb(237, 51, 59);</string> </property> <property name="text"> <string/> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QGroupBox" name="policiesBox"> <property name="title"> <string/> </property> <property name="flat"> <bool>true</bool> </property> <layout class="QGridLayout" name="gridLayout"> <property name="leftMargin"> <number>15</number> </property> <property name="topMargin"> <number>5</number> </property> <property name="rightMargin"> <number>15</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="4" column="0" colspan="2"> <widget class="Line" name="line_2"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="lblProfile"> <property name="text"> <string>Profile</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QComboBox" name="comboInput"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>Allow</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>Deny</string> </property> <property name="icon"> <iconset theme="process-stop"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </item> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_4"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Outbound</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Inbound</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QComboBox" name="comboOutput"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>Allow</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>Deny</string> </property> <property name="icon"> <iconset theme="process-stop"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </item> </widget> </item> <item row="0" column="1"> <widget class="QComboBox" name="comboProfile"/> </item> <item row="5" column="0" colspan="2"> <layout class="QGridLayout" name="gridLayout_2"> <property name="topMargin"> <number>5</number> </property> <property name="bottomMargin"> <number>5</number> </property> <item row="0" column="0"> <widget class="QPushButton" name="cmdAllowINService"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Allow inbound connections to a port</string> </property> <property name="text"> <string>Allow service (IN)</string> </property> <property name="icon"> <iconset theme="go-down"> <normaloff>.</normaloff>.</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item row="0" column="1"> <widget class="QPushButton" name="cmdAllowOUTService"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Exclude outbound connections to a port from being intercepted</string> </property> <property name="layoutDirection"> <enum>Qt::RightToLeft</enum> </property> <property name="styleSheet"> <string notr="true">QPushButton { text-align: right; }</string> </property> <property name="text"> <string>Allow service (OUT)</string> </property> <property name="icon"> <iconset theme="go-up"> <normaloff>.</normaloff>.</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item row="1" column="0" colspan="2"> <widget class="QPushButton" name="cmdNewRule"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>New rule</string> </property> <property name="icon"> <iconset theme="document-new"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> </layout> </widget> </item> </layout> </widget> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_5"> <item> <widget class="QLabel" name="statusLabel"> <property name="text"> <string/> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QPushButton" name="cmdHelp"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="help-browser"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="cmdClose"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Close</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> </layout> </item> </layout> </widget> <tabstops> <tabstop>sliderFwEnable</tabstop> <tabstop>comboInput</tabstop> <tabstop>comboOutput</tabstop> <tabstop>cmdClose</tabstop> <tabstop>tabWidget</tabstop> </tabstops> <resources/> <connections/> </ui> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/firewall_rule.ui�������������������������������������������������0000664�0000000�0000000�00000035172�15003540030�0022452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Dialog</class> <widget class="QDialog" name="Dialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>484</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>Firewall rule</string> </property> <property name="windowIcon"> <iconset> <normaloff>../../../../../../.designer/backup/icon-white.svg</normaloff>../../../../../../.designer/backup/icon-white.svg</iconset> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QLabel" name="label_7"> <property name="text"> <string>Node</string> </property> </widget> </item> <item> <widget class="QComboBox" name="comboNodes"/> </item> </layout> </item> <item> <widget class="QCheckBox" name="checkEnable"> <property name="text"> <string>Enable</string> </property> </widget> </item> <item> <layout class="QVBoxLayout" name="verticalLayout_3"> <property name="sizeConstraint"> <enum>QLayout::SetMaximumSize</enum> </property> <item> <widget class="QLabel" name="label"> <property name="text"> <string>Description</string> </property> <property name="alignment"> <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> </property> </widget> </item> <item> <widget class="QLineEdit" name="lineDescription"> <property name="clearButtonEnabled"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item> <widget class="QTabWidget" name="tabWidget"> <property name="styleSheet"> <string notr="true">QTabBar { alignment: center; }</string> </property> <property name="tabPosition"> <enum>QTabWidget::South</enum> </property> <property name="currentIndex"> <number>0</number> </property> <property name="elideMode"> <enum>Qt::ElideRight</enum> </property> <property name="documentMode"> <bool>true</bool> </property> <property name="tabBarAutoHide"> <bool>true</bool> </property> <widget class="QWidget" name="tabSimple"> <attribute name="title"> <string>Simple</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QToolBox" name="toolBoxSimple"> <property name="currentIndex"> <number>0</number> </property> <property name="tabSpacing"> <number>0</number> </property> <widget class="QWidget" name="page1"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>448</width> <height>200</height> </rect> </property> <attribute name="label"> <string/> </attribute> </widget> </widget> </item> <item> <widget class="QLabel" name="lblExcludeTip"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> </widget> </item> </layout> </widget> </widget> </item> <item> <widget class="QWidget" name="hboxAdvanced" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_5"> <property name="topMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <widget class="QPushButton" name="cmdAddStatement"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Add new condition</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-add"> <normaloff>.</normaloff>.</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdDelStatement"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Remove selected condition</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-remove"> <normaloff>.</normaloff>.</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </widget> </item> <item> <widget class="QFrame" name="frameDirection"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Plain</enum> </property> <property name="lineWidth"> <number>0</number> </property> <layout class="QVBoxLayout" name="verticalLayout_6"> <property name="leftMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <item> <widget class="QLabel" name="label_3"> <property name="text"> <string>Direction</string> </property> <property name="alignment"> <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> </property> </widget> </item> <item> <widget class="QComboBox" name="comboDirection"> <item> <property name="text"> <string>IN</string> </property> <property name="icon"> <iconset theme="go-down"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>OUT</string> </property> <property name="icon"> <iconset theme="go-up"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>FORWARD</string> </property> </item> <item> <property name="text"> <string>PREROUTING</string> </property> </item> <item> <property name="text"> <string>POSTROUTING</string> </property> </item> </widget> </item> <item> <widget class="QLabel" name="label_4"> <property name="text"> <string>Action</string> </property> <property name="alignment"> <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> </property> </widget> </item> <item> <widget class="QComboBox" name="comboVerdict"> <item> <property name="text"> <string>ACCEPT</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>DROP</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>REJECT</string> </property> <property name="icon"> <iconset theme="edit-delete"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>RETURN</string> </property> <property name="icon"> <iconset theme="edit-undo"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>QUEUE</string> </property> <property name="icon"> <iconset theme="go-next"/> </property> </item> <item> <property name="text"> <string>DNAT</string> </property> </item> <item> <property name="text"> <string>SNAT</string> </property> </item> <item> <property name="text"> <string>REDIRECT</string> </property> <property name="icon"> <iconset theme="edit-redo"/> </property> </item> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QComboBox" name="comboVerdictParms"/> </item> <item> <widget class="QLineEdit" name="lineVerdictParms"> <property name="toolTip"> <string>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. Some examples: QUEUE -> num 0 (or 1, 2, ...) REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: to :22 to 192.168.1.254:8080 to 192.168.1.254 to 1024-2048 (masquerade)</string> </property> </widget> </item> </layout> </item> </layout> </widget> </item> <item> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item> <widget class="QLabel" name="statusLabel"> <property name="text"> <string/> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPushButton" name="helpButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="help-browser"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="cmdClose"> <property name="text"> <string>Close</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdReset"> <property name="text"> <string>Clear</string> </property> <property name="icon"> <iconset theme="reload"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdDelete"> <property name="text"> <string>Delete</string> </property> <property name="icon"> <iconset theme="edit-delete"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdSave"> <property name="text"> <string>Save</string> </property> <property name="icon"> <iconset theme="document-save"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdAdd"> <property name="text"> <string>Add</string> </property> <property name="icon"> <iconset theme="list-add"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> </layout> </item> </layout> </widget> <resources/> <connections/> </ui> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon-alert.png���������������������������������������������������0000664�0000000�0000000�00000046216�15003540030�0022023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ôxÔú��� pHYs��Ã��ÃÇo¨d���tEXtSoftware�www.inkscape.org›î<�� �IDATxœìÝwxåÚÇñï$©Ò›;¢ž×sÄŠ ÕBQì"*bïØD°+ö®tP¥c V”^T¤[è„–Üï HÙÝ{v÷÷¹..“¯ž{ï33Ï€ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆHdÞ""%efU€ªÀŽ?WËÿ¹Üv_^‰ÿþÙ—d�ÙÀF 3ÿç ÀÚü¿^,þ~þ ‚`utÿD¢O€ˆ„ž™•�Üîçm]HqÒò†¿€ÅÀ,`&0˜ŸG¤È4�ˆHh˜Yià0àßù?ê“÷F_dz«ˆÖ“7L#o(˜ üÁ×*‘h�7fV 8h”ÿó@iרèØü|•ÿcB™¾I’ì4�ˆH̘Y% 1pp:yKøÉ(—¼Ó_cqAlôM’d£@D¢ÊÌ�ç�-†Äþ|}<XGÞ 0£‹ %4�ˆHÄ™YC -pÉû)¿¸¶�_7 ‚àwçIP�D$"̬>и¼ ÷¤är€O€ÞÀð 69÷HÑ� "Åff@ àòÎéKôdC€þALðŽ‘ø§@DŠÌÌŽ®.�Ê;ç$£)À[@Ÿ ²¼c$>i�‘B1³ 9pú´Yäx2‚ÅÞ1_4�ˆÈ.™ÙÀUÀmÀ>Î9R°ÍÀÛÀSALóŽ‘ø @D dfå+Î@-ç)¼¯Ç€QÚ’XvE€ˆüüOü·�ws¤ø~‚`¬wˆ„“�þ>Ç ðPÛ9G"ç;àá Fz‡H¸h�Irf·aOOà牞/€{‚ øÖ;DÂA€H3³ãÈ;_|¢w‹ÄÌ(àæ æy‡ˆ/ �"IÈÌöžÎönÉûßÿá ÖxLj �"IÄÌÒ€›²Î9âo9ywyôÑÉG€H’Èß½ïUòžÈ'²½ ÀuAL÷‘ØIñ‘è2³²fö(0½ùKÁ“ÌìÙüý$ h@$™Ùyäë­ëÝ"qcÐ!‚qÞ!]ZI@fVÙ̆�ï£7)š}OÍì3Óu" L+�" ÆÌ}Ñ¿”ÜàÊ Æ{‡Häi@$A˜Y)3{ø½ùKdìKÞjÀ£fVÊ;F"K+�" À̪ƒÆÞ-’°&A0×;D"C+�"qÎÌN¦ 7‰®†äÝ)ÐÎ;D"C€H3³[€qèq½€ÁföœN Ä?‰Cf– <ÜàÝ"IkÐ6‚%Þ!R<�D⌙•#ï| ïIz�­‚ øÁ;DŠN§�D∙í|Þü%ê�_˜ÙEÞ!Rt�Dâ„™ý‹¼7ÿ#¼[D¶“ 0³nÞ!R4: Ìì` z‚ßn­_¿ž¬¬,²²Ö±ió&�rs¬¬¬]~_¹²eIMK lÙ²”/WžråÊ’žžõæòp}[½Cd÷4�ˆ„\þ•þO©Þ-ÞÌŒ 2wî<.\È‚… Y°`!ýµ„ÌÌL²Ö­#'''¢ÇLKK£\¹rT©R™Z5kR³f jתM­Z5©[·.õëB¥Š#zÌ87¸ ‚uÞ!²k�DBÊÌà)àVï/[·neúôü4i2ü‘I“§°zõjﬨY³‡|0‡|0ÿ>òÿhØð(Ê—Oê‡ê}4 ‚ Ó;DvN€Håßæ÷:ÐÞ»%Ö–-[Æç_|Éç_|É„¯¿aÆ ÞIE–ššÊa å¸cå¸ãŽå˜£’––æk“³‚ Xî"Ó� 2fV´òn‰•¹óæ1zôX>?žY³fcfÞIU©bEÎ8ý4š6mÂñÇ›LÃÀ,àô þò‘Ò� "ù_œîÝmK—.eÔè1Œ5š™3gyçÄLÅ hÒä,.8¿-G~˜wN,üœÁïÞ!ò¿4�ˆ„Dþ›ÿ(à用ٺu+Ÿ|úƒß~‡ï¾ÿÜÜ\ï$W‡ZŸvçŸÏÙ-›S®\9ïœhú8)‚¥Þ!ò_�DB ÿÍ4p²wK4üõ×Þò.CÞ}eËuJxGœ{NK®lõöÝ×;'Z¦“·°Ò;Dòh�q–¿µïhà$ï–H›1s&¯¼ú:Ÿ|úYÄoÏKD)))4>õ:\Ùž†GýÇ;'~$5Þ!¢@Ä•™¥“wßô©Þ-‘ôÃyéÕט0ákõ¯#çê«:pæ§“’’P›¶N�΂`£wH²Ó� C3HKKáÒKÏ£zõ|«"ç÷?þà§Ÿ&±diÑOõ~½GY¾ÙCîèÀàúN×Ò¬i“DÞ#o³ ä¾Ä™�‘2¸xÈ»#Œž­Rç«Tõέý÷ß›o¼¦MÎ"â ¸Ý;"™%Ì8)"’ÈæÎÇM·ÜF›óÛ1qâÞ9‘p›™Ýä‘Ì4�ˆˆÄ‘©Ó¦sá%—ѱÓõ,X¸Ð;§¤ž6³æÞÉJ€H,uÔ¾Þ ’>7ž&ÍZÒ£ç#q¹]r¾T`°™ê’Œ4�ˆÄˆ™Lóæ{wHâØºu+}úõ§Y‹s7n¼wNq•Þ3³ Þ!É&i6¤–Ä—K]…ü;{>k ~¬îf`ý¿¶ÈV9A¬-AÛžÀHÒÒôpy‰¸ßÿøƒk:]Ï9-[Ðåþ{©T©’wRQÕú™ÙyA$Öƒ BL€Ä3«”ÿãàüŸ÷ªñß7ýÒQn€¼AaÕv?V+¹ÀœüÓƒ زÝ÷•>�ŒfŸÈð‘£øæÛïx°ÛœyFÜ=Nâà^ §wH²Ð� ¡dfuÈÛ÷¤üõ}‹þV¨™ÿcg²ÍìG``ä=Õoï´‰°|Å ®»á&Ú´>®]î'##Ã;©(º™ÙALðI�$̬pЄ¼‡áìçT2À‰ù?D\¼7ô&MšÂ³½ž¢~ýC¼s + dfÿÁ*ï˜D§‹�Å™jf÷˜Ù—Àr`p%ñýæ/óæÏ§õùí8ømï”¢Ø xÓÌb·£0Ó� 1ef‡›Ùƒf6˜ <LÞ'e­F‰DÁæÍ›éÚ­;÷Ü{?›6mòÎ)¬së¼#�‰:3«mfwå¿éO�t߯H ½;ô}.ºä2–,YâRX›™.œ" �fVÚÌÚ™ÙX`ðzÓq5uÚtZµmÇìÙ?{§ÆÀ[f¦÷©(ÑX‰(3«efÉ»-n0yõtß½ˆ8X¶l\t ã?ÿÂ;¥0·xG$* �fv¼™½GÞ§ýGºÎI"²6làÚënཡx§F3;Ø;"i�b3³ÀÌZšÙWÀ×@kt1ŸH\ÈÉÉážûî§_ÿÞ)»“¼®»"O€™™¥˜Ù…À `yËt"gÌŒî=zòêëox§ìΉÀåÞ‰F€Zþ'þÖä]É?]Ô'’žxòi^xéeïŒÝyÂ̪xG$ �R(fv&ððÐÀ9GD"ì™gŸçÍ·z{gìJUò®/’Ñ� »df Ìl ðp¤wˆDÏ£?ö];˜ÙñÞ‰B€È̪˜ÙËÀ ©wˆDŸ™ñ`÷Œ=Æ;egà]�ääŸçoü \‹®êI*¹¹¹Üu÷½Lüñ'ï”i\ä‘4�Èß̬ððyçÛD$ mÞ¼™N×ÝÀÜyó¼Svæa3‹«ç‡‘�ÁÌJ™Yòž_¯GØŠ™kÖpuÇNd®YãR½Û¼#â€$gfÿ¾º¥sD$D-ZÌ-·ÝANNŽwJA:›Yuïˆx¦ I™Yª™ÝLDW÷‹ÈNL˜ð5½žyÎ;£ 廽#♀$dfu€O@)ç ¹W_ƒ>þÄ;£ ךYmïˆx¥ ɘÙÙÀTàç‰fƽ÷uáÏ?ÿòNÙQp¯wD¼Ò�$ò—ü†ÚNSDŠdÍÚµÜvÇa¼àj3Û×;"i�HfV øèLÞF""EöãO“xáÅÐ=3 4º X4�$83kHÞþ½[D$þ½ôÊ«LŸ1Ã;cG—›Y ïˆx£ ™Y+às`/çI999Üq×ÝlÚ´É;e{éÀõÞñF@‚2³›w=¼[D$±Ì;—_yÍ;cG×›YYïˆx¢ Á˜Yš™½<ƒþ÷‘(yåµ×ùuÎïŒíU.÷Žˆ'zƒH fV \íÝ""‰mëÖ­<ÐõAÌÌ;e{·›YªwD¼Ð� ̬0hãÝ""ÉáÇŸ&1|ÄHïŒííœã/4�$�3« | œáÝ""ÉåÑÇŸ ++Ë;c{·{Ä =ë=Îåßãÿ)p„wKXdee1oþ–-[Fæš5¬É\“÷óšLV¯Î$++‹Ò¥K“žžþ÷÷”)S†ôôtÊ–-KêÕ¨\¹2ÕªUcï½êR»vmRSµª(R+VòêkopÇí·z§ls¼™Á4ï°Ó�Çòßü?÷nñÍ”©Óøí·ßømî<æÍ›ÇÜyóY¶lYDSªT)öÞ{/ö«Wú‡Bƒ‡ÒàÐúÔ¬Y3¢Ç‰W}úõçâ‹.¤V­Ðüžh„f" + �qÊ̪doþëׯçÇŸ&1qâ|?q"Ó§Ï`ëÖ­Q?î–-[˜;wsçÎã“O?ûû׫T©Ì¿<’£nHãþCýCÑJ$¥7ÒëÙçxüч½S¶¹ØÌ:A°Ù;$Ì4�Ä!3+Œ! Þüç/XÀرñÙ¸q̘9+Tû¯\¹ŠO>ýìï¡ \¹rܱœ|Ò‰œròIÔ¨¡É$y >‚W^ÁÁäP h õ 3 �q&ÿV¿aÀÑÞ-Ñ2oþ|>üðcÆ~ø!³þÅ;§ÐÖ­[ÇÇŸ|ÊÇŸ| @ýCæÌ3Ï YÓ&ì¿ß~Îu"Ñ•››Ëó/¼Ä Ï=ã²M{4�ì’ Gòïo}8Ï»%ÒÖ¯_Ïû ã!ïñó/ñó¦_Xõ9˜fM›ré‚y”{ì1ïœPz¶J5ž¯RÕ;CJ %%…‘ÃßË*ÀV`Ÿ þô +­�Ä—^$Ø›ÿÂ…‹òî{¼ýÎÖ¬]ë5³þ…Ù?ÿ¦Õ+¹Å;F$JrssyáÅ—yþÙ^Þ)÷þvð¨wHXi�ˆfv#p£wG$˜_~5¾ýúóÕ„¯Ã¶“XT%Ó¿«$§>þ„¹óæ…å´×%h�Ø)m̬9yŸþãÞ„¯¿¡ÍùíèpuG¾üj‚ÞELnn.}úöóÎØ¦™èV�BÎÌê“·¿\ß_6iòd.¾ô ®¸ò*¦N›î#"Q4lø233½3¶ÑÖÀ;¡ Ä̬"ðPÞ»¥¸~þå®î؉óÛ]Ì÷?üà#"1½‘ÁoñÎØFÀNh�)3Kú{·Gvv6<öçœ×†ñŸá#"1Öà ¶lÙâpœ™U÷Ž# �áuyYÄñŸA“f-yó­Þ¡Ú¸GDbgÙ²eŒÿ¹wä>máF�BÈÌNºzwÕò+¸³ó=\ݱü©[oE’Ý{Cß÷NØæ\ï€0Ò�2fVHœ]ô7zÌXÎjÚœ† ÷N‘øò« ,]ºÔ;àt3ÛÃ;"l4�„ˆ™À[À^Þ-…µqãFîëÒ•›o½µkCõLpq–““Ãû„âCApœwDØh�—+‰£+VçÌùVm.à!ïz§ˆHH…hUðDÑ�fVxÒ»£°† A«¶ðëœ9Þ)"bóæÏç—_õÎ�8É; l4�„@þÒÿ›@%ï–ÝÉÉÉ¡[÷Üq×Ýdgg{çˆHøè£O¼�ŽÍšªäÓ�×�gyGìΆ èØéz ä""qdìGy'@Þu�GyG„‰�gfVƒ8xXŲeËhwñ¥|þÅ—Þ)"gæÌù¹óæyg€Nü �þž$äKÿ¿Î™C› .dÖ¬ÙÞ)"§>ÇŽ ºp;�™Ù©ÀÅÞ»2eÊT.¸ðþüó/ï‰c_}ýwèVÀÿ¡À‰™¥/�wËÎL›>ƒöW]CV–îï‘’ùñÇŸØ´i“wF%3‹›}V¢M€ŸkC½#vfö쟹²ÃÕzó‘ˆØ¸q#üÉ;à0Ð�àÀÌ*â½þg͚ͥ—·'sÍïI ß|ó­wÀáÞa¡ÀÇý@UüüË/\Ö¾ƒÞüE$â~š4Ù;´ð7 �1–þé,_±‚«;v"33Ó;EDÐÌY³Ø²e‹w†V�òi�ˆ½ûÐíFµiÓ&®½îþúk‰wŠˆ$¨7òó/¿xgÔÏ¿;éi�ˆ!3«´÷îØ‘™qgç{˜:ušwŠˆ$¸ü9S8Ð;" 4�ÄÖ@)ïˆ=÷ü‹Œû¡w†ˆ$iÓgx'�ìç�bÄÌö.ñîØÑè1cyᥗ½3D$IÌ™ó›w@ï€0Ð�;·¡:ï´xñbîëÒ3óN‘$1wÞ¼0ü™S×; 4�Ä€™U&dçþsrr¸íÎά[·Î;ED’Ȇ øãÏ?½34�  Vn�ÊyGlï…_fòä)Þ"’„Bp@��¢ÎÌJ¼;¶÷Ó¤I¼ôÊ«Þ"’¤.Zä � �±Ð ¨é±MVV·ßÑ™œœïIR!ØoD��b!TŸþŸêõ,¿ÿñ‡w†ˆ$±¿þr¼xy3+ïáM@™ÙaÀIÞÛÌžý3ƒß~Ç;CD’Ü_KÜW��*zxÓ�]¼¶13|¨‡–þEÄ]V��ÊzxÓ�%ù{M·óîØføˆ‘üøÓ$ï V®\å��4�DQ3Brñßúõëyüɧ½3DD�زe ÙÙ½3Buk¶ �Ñs™wÀ6/¾ô Ë–-óÎù[VÖZï �Þ‰(ÿêÒæÞ�™™™ 4Ø;CDä¬Yã>�è€w@‚j¤{G�¼Ù»6lðÎùkµàN@t´ö�X»6‹õé_DÂgË–-Þ {xxÓ�af¶ÐÄ» oÿþdeeygˆˆüCn®ûs½¼i�ˆ¼3Á¹¥ 6зß�ï ‘åälõNpð¦ òBñéÿwß#33Ó;CD¤@99îÀ“~W4 �‘w–w�À»ïõNÙ)3÷S�ZðH$fvPÏ»cê´éü:gŽw†ˆÈN¥§»ß(¥À; Áœá�0tèûÞ ""»”^¦Œw‚Nx$˜“½6mÚÄè1c½3DDv)=#Ã;A+�Þ æï€>þ„5kÝ7ØÙ¥ôt÷� �Þ‰ÂÌ"ÿyØpï‘b)Uª”w‚ÄP¹rîñ­÷ð¦ ry¬Y»–ï¾ûÞ;C¤X.»ôbš7kê!1’’Bå=÷ôÎÐ�à@Žñ?þs¶nMúU-‰SåÊ•ãÙ^O1¨_<ð�=÷¬DjjªwÆ:ï�o�"çßÞŸ~6Î;A¤ÄŽ>º!#‡½O—ûî Ã2±DAÕ*U½@+��"ÁÌJ‡y6äää0áëo<D"&--Ë/»„ÇŽâ¼sÏ!ï$‰ ªU«x'€� �rÎÿ<e*ëÖ%ýŠ–$˜êÕ«óÄc0 _oH 5kº_/ :  BŽðøæÛo½D¢æ˜£fä°÷¹ëÎÛÙc¤ŠkÜÛ{¯½¼ ÈöŽð¦ 2öøVWÿK‚KKKãš«:ðñØQ4kŠgnI1í½·û�ûÓˆ¼i�ˆŒC<¾uëV¦OŸá™ 35kÖä¹gžf`ÿ>:-§öÙg_þ �‘âº0köÏlܸÑ3A$æŽ9úhF|0T§âPV�’þ@Ð�Pbf– ¸~ ™2eªçáEÜ”*UêïÓM›„âIܲ5jÔ RÅŠÞ¹ùv'5 �%W(í0cæLÏ˸«Y³&Ï?ÛK§â@ƒ‡z'�ìL0³PÄxÑ�Pr{{üòë¯Þ "¡°ín.÷ÝKÙ²e½s¤� ê×÷NØæX`Š™=jf®â¼h�(9דY999Ì;Ï3A$T¶m"ôɇ£9ïÜs¼sd‡š� Ð˜hfƚ€’s�ÿþ».�)À¶M„úö~“ýêÕóΑ|  åªûÀ·fÖ5gפ  äjy|ñâÅž‡ ½Ž?ŽÑ#‡qÇí·’‘‘á“Ôj×®EíÚ®dîJi ðc²¬h�(9×M­/þÃóð"q¡T©R\{ÍÕ|<v4MÎ:Ó;'i5<ê(ï„Â8øÞÌî3³„~L蹩ìyðÅ¿k@¤°jÕªÉ Ï=CŸ·ÞÐiG7Œ‹� è|bfµ½c¢E@ɹ>×rÉ’¥ž‡‰KN8ž1£†Óå¾{µ‰P }tCï„¢j L5³–Þ!Ñ  äöô<øêÌLÏËĭmw Œ5œÓŸê“ðjÖ¬A½}÷õÎ(ŽªÀp3{(ÑN $Ô¿Œ“Jž_¹r¥çáEâ^Ý:uxõåyí•—¨[·®wNÂ:íÔ¸²à~`¤™¹oc)�J®¼çÁ׬Yãyx‘„ÑøÔS;j×uêHéÒI¹/LTrÊÉÞ ‘Ð øÚÌö÷‰ �%`fi€ë Äìlí )éÜvËÍŒ9ŒŽ?Î;'add¤sܱÇxgDJà3;Ý;¤¤4�”Lï�m$yõöÝ—¾½ßäÙ^OQ£F wüqÇ‘žžîI•±fv•wHIh�(÷`Ó¦MÞ " «y³¦|<ví¯¸œÔÔ¤x\±%è“Ó€×ͬ»™Þ1Å¡ d\�3#77×3A$á•-[–ûîéÌØÑ#8þ¸c½sâNFF:gž÷«å»ÒèJóˆs®�A@jj*999ž;õ¯Á[o¼æ*ežxzôðÎbد^=úö~“÷?ÆcO<ŪU«¼“âÂi'Ã^ —µÍ¬uk½c K@ɸŸó�–šFÅ îÿ‰Â¥Lï) hÝê<N?ý4žzªoyW«p»Ñ²Esï„X9øÊÌÎ ‚`‰wLaè@ɸ¿»¥¥i†‰µŠ*ÐýÁ®¼7äm?ì0ïœÐJMM¥Ñ Ç{gÄÒÀ—f¶wHah�(÷ \Ù²Þ "IëˆÃcè»oó`×.Zí*@NNï¹Õ«W{§ÄÒä y‡ìŽ€’q®eùò®—!ˆ$½””.¾èB>þp ­Î;— ˆË £fÔè1œÙ´¯¼ö:k×fyçÄÊÞÀfv¸wÈ®h�(3+mf§™Y/3û xл©\9­�ˆ„A•*•yüч4 x wN¨¬^½š'Ÿêʼn§4¦çÃòÛos½“b¡&ð¹™í²3�vÃÌRóßôß�–�Ÿ·�¡Ø ²re×§‹ÈõF Ê=ïL†«ß‹dýúõôîÛ&Í[Òúüv¼ýβ²zU 2ð±™ýÇ;¤ �vÂÌŽ1³gßÉ{Óï€ó“ÿ ¢]ÊDÂ'--W¶ç“G'ê&8%6uê4î ÇŸx wß{S¦LõNŠ–ŠÀ‡fv¨wÈŽ4�l'‰ÿ3›|ÜDÞ2NhU¯^Í;ADv¢F<ÿl/ú¼ù:ûì³·wN(eggóÞÐhsÁ…4oy.ýúdÍÚ¸¹•¾°ªŸ˜Ù~Þ!ÛÓ��˜Yu3ë,�úGù^­Zî×!ŠÈn4jt£G ãºNuëî.üòë¯tïÑ“F'JÏGcéÒ¥ÞI‘TøÔÌêx‡l“Ô€™Õ1³—€…@wBpUQí»O\Ün*’ôÒÓóž48rØûõŸ{ç„Zvv6½ûôå”ÓÎä¾.]Y¸p‘wR¤Ô#o% K·I9�äâïütâö1UõöÝ×;ADŠàÀ`ðÀþôèÞM{ìÆ–-[xgÈ»œÙ´9·Ýq‹/öNŠ„úä]PÑ;$©�3«lf�óÈ»’?nßø·©ZµŠö‰3AÐî‚óùhìèdÚ*·Ørrr1rg6mÁ#=‘×üáý�¡¤�̬”™ÝJÞÿÝ@BÝ<¯{ŽEâSÕªUèõÔô~ó5êÖ­ëz[¶láÍ·zsÚMè× [·nõN*‰“€—=~�0³Ó€)ÀÓäÝŽ‘p4¨ï "%pb£FŒñí.8_;tàH¼�� �IDAT Bff&Ý{ô¤Y‹s˜8ñG¸2ÿé‹„�Ìlo3{—¼{øCwÿe$Ö w‚–6†Ù¹$ÿoS¶lYztïÆ¯½B•*Úà«0æÍŸÏE—^Î]dݺuÞ9Åõ„™5õ8p �f–ff÷�³6Þ=±pØa�âFíÚÞá¥[ZX¾|YYqûfsfÆ ·ß¡Ió–Œÿ¹wNq¤ƒÍ,æK¹ 5�ä?xá;àa i>N°ÿþºš8^h�ع$�V­ZÅu7ÜÄÝ÷ÞÇæÍ›½sâÎ’%K¹æÚë¸ýÎÎñ¸P‘¼‹«Äò  1�ä_ä×øåžËÑ”’’Ât_q|Ð�°sI<� 9Š&ÍÏæãO>õN‰{ÃGŒäœVm˜1s¦wJQ�¼kf¥buÀ¸�Ìì0àò6òq½¥Â“€8¡`çj†z×í¨X¶l®¿‘Û︋U«Vyç$Œ… Ѻm;ž{áErss½sŠâTbø”Ù¸�Ì,0³ÛŸˆ£­{£å¸cõNÂØc¨TÉ»"|Ê”Iªÿ.999ôéןÓÏjÆ'Ÿ~æ“rrrxîù¹öúÉ\³Æ;§(:çß½uq9�äŸ'<IêßÞa Õ•ÃñB«�ÿT«$Éío¿Î™Ã]Bž°aÃ7nÜxZµ9Ÿ¹sçy§V 0ÐÌ¢þ¨×¸�Ììh`"л%LRRR8áøã½3¤04�üSœÿߺu+¯¾þç¶j›È¾ ¥E‹ÓªíŒÿü ô6³¨NÅq3�ä/ùß |MÞd'Ÿt¢w‚†€Jðàëo¾¥ió³yâɧu…¿“õë×síu70`à ï”Âj ÜÍÄÅs)ͬ<0}êߥSO9…Ò¥K똰Ó�ðO :�,[¶Œ‡}œQ£Çx§y×tëÞƒ¹óæÓå¾{HI ýgàÇÍì‹ ¦EãÅCÿoofõÈ»Ê_oþ»Q¡By­ă}³+‘»`ÛE~g6m¡7ÿê?` wÞu7999Þ)»“NÞ&AÑxñP�fv<ð-p˜wK¼hѼ™w‚ìŽV�þ)†¢3gÒö‚ éÑó‘xÜ&i 9ŠN×ßȦM›¼SvçP K4^8´€™]|FÞÅRHO=•=’|OõÐÓ�ðO 0�¬Y»–=¡uÛvL›>Ã;G aÜøÏ¹òªŽ¬_¿Þ;ewî4³ÿ‹ô‹†n�È¿ØïQ` yËRéœ~Zcï Ù �ÿǧ�ÌŒ÷†~Àg5£O¿þñ°¬,Ûùþ‡¸¢ÃÕaÒ€—Ì,¢ïÙ¡�Ì,xèìÝÏt äj×Nš{Þ -NW�æÍŸÏåí;p÷½÷i'¿86yòÚw¸†ììlï”]9¸&’/šÀÌÒ€·€«½[âÝI'6¢RíªwJ—†*1}æG¸¥¤@µjÞE’½‘ç^x‘æ-Ïå›o¿óΑ˜4y2®¿1ìwQ=ffu"õb¡�Ì,#33sp™wK"HKK£áQI÷L¤ø¢Ó�ÿU½:¤ÅÅÉ�|øÑǜѤ)Ï=ÿ"[¶lñΑšðõ7Üî»*�½"õbîëfV¼m}Oön ‹eË—³pá"þøãþúk K–,aÕêÕdee‘µn9[ÿûÎôô2¤§§S¾|yöØc²³³Y»v-ß}ÿ[·nuü·€ÔÔTÊ•-ëÚVÏÏù…ã×dzg„ÑG¤IÞ»µhÑbºuˆ/¿šà"QÖ¶MkîÑ ¼§êZA0º¤/â:v›ÙÀ( )o^ÏÉÉá×_ç0eê4f͞Ŝ9¿1gÎo¬Y»Ö;-"rrræß%Òþð“_�¸yóf^}ý ^yõõx¸eL"àÝ÷†²Wݺ\ש£wÊÎ<efAP¢Oyn€™•Þ#‰Þü³³7òÓ¤I|÷Ý÷Lüñ'fÍžEvöFï,q°,5~–¼£.Ä�N˜ð5ݺ÷`ÁÂ…Þ)c½ž}Žýö«G“³ÎôN)ÈÁ@ò.š/6—?…ò¯ö@Þ^Ç mÎœßølÜ8¾øjS§N û&#ËâèœwÔ…p�Xºt)=yŒ1c?ôN'fÆï¦nÝ:Ö wNAºšÙ€ Š}ÿbÌÿʺѫ@ÛX;ÌŒÉS¦0vìG|6~<‹-öN’Ò�°ÈÉɡ߀<óìóa¿/\b ;{#;ÝÀï½CõêÕ½svT ¸èQÜðøSè)ò–.Êü 1bÃGŽÔ›¾ì–€í„d`òä)<ÐíAfÿü‹wŠ„ÈÒ¥KétÃM¼=°?¥J•òÎÙÑföJ+ŠóÍ1ýSÈÌî%obI999|úÙ8 Ä·ß}ï#qd©€ÿr�233yüɧy÷½¡˜™k‹„ÓÔ©Óxâ©^Ü{÷]Þ);ª�ÜÜRœoŽÙ=fv.0”ì=PëÖ­càà·8h0þù—wŽÄ¡T3fÍù™Tï0˜7êÕ‹ùasssy÷½¡<ñT/23uK¦ìZ¼úÒ 4n|ªwÊŽ6õƒ ˜_ÔoŒÉ�`f‡“÷Hßr±8^´¬_¿ž¾ýðVï>d®Yã#qîÛys¨æ¼WC(¬_1~€ÕŒ™3éÚ­;S§Méq%¾UªX‘ÃÞ§vípœ¶ÚΛA\UÔoŠú�`fU€Øø²yófz÷íÇëo¼¥O 1ÃΧÁ¦$¿ ´bEˆáï©Ì5kèÕëY¿3„ÜÜܘWÇ¿<’Áû‘šªõ»M@½ Š´$Õåx3+ !ŽßüÇÿœ&Í[òÄ“OëÍ_"J׳óÿfưá#8«is~[oþRl“&OæÕ×ÞðÎØQàÆ¢~S´ÏÇ? „î„IaÌ;K.kÏ5×^§«ú%*t'�1�fÍšÍù^ÌwÝÍÊ•zbŸ”Ü /½Ìœ9¿ygìèZ3+_”oˆÚ�`fíNÑzýhÉÉÉá•W_ãìóZóÝ÷º²_¢G�Q�Ö¬]K·î=8¯ÍùLž<%jÇ‘ä³yófî¾ïþ°=4hOŠx‹}T�3;x.¯M?ÿò ­Ú^À“O?£=¿%ê–¥…îžâØ‹Â&@fÆÐ÷?àÌ&Í0pPØþ–1uê4z÷éç±£[̬П,">�äŸ÷H]ñofôéןVm.`æÌYÞ9’$´@ÄW�fÏþ™v]Jç{îÓr¿DÝ3Ï=¶çDìœ_Ø/ŽÆ Àƒ@Ã(¼nT¬^½šŽ®§GÏG´O¿Ä”.$bÀÚµYtïÑ“s[·å§8x´°$†7òP‡½3vtGa¿0¢€™tŽäkFÓO“&Ñüìó7þsïIBz" %>`f|0l8g6mF¿þµÜ/1÷Å—_ñù_zglïH3;ª0_±ÀÌ*}"ùšÑ4|ÄH.»¢Ë–-óN‘$µ2-œ f›q†S V�æÍŸÏW^ÅïaÅŠ•Œ)š‡z>Ì–-[¼3¶wEa¾(’oÖ/wþ!ÔrrrèÞ£'·ßÙYú‰«\`y¸6‰½b �ëÖ­£çÃÒ´ùÙ|ýÍ·Qˆ)š… 1`à ïŒí]hfev÷Eùøaf§ŸFâµ¢iÆ \wãÍL˜ðµwŠ�C-à_³½3|”)ÙÙPÈU3cÄÈQ<öø“,[¾<Êq"ES¡By>ýh,•+WöNÙæ‚ †ìê J¼`f¥Éûôj™kÖpù•WéÍ_B%©ï¨U«ÐoþóæÏ§}‡«¹ýÎÎzó—PZ»6‹×ß|Ë;c{Wìî "q ànà¼NÔüþÇœÁEÚ DB'©€B\�˜Ís/¼Hó–ç2áëob%R|Óí§g™Ù^»ú‚ �fv�pOI^#Úæ/XÀùí.fÞü"?)Q$ê’z�ØÍùÿ‘£FsÚ™MyîùÃv•H²³³yýÍ7½3¶I.ÞÝ”Ä @z _#j,\ÈÅ—^¡+ý%´’úVÀ �¿ý6—K.kÏ­·ß©ß»wz;L«�—îê{�0³¶ÀYÅýþhÓ›¿Äƒ¤Þ h‡S�6làÑÇŸ Å9çé9·B¶ phþÖü*Ö�¿ÝoÏb'EÙ_-á’Ë®`éÒ¥Þ)"»”ÔÏØn`ô˜±œÑ¤9o¼Ù›­[·:F‰”Ü Áï°vm–wÆ6gïìwàJ`§S…§ÌÌL®èpK–èÍ_Â/©W�jÕbÁÂ…´ïp 7ßz»vI6l`èûï{glÓrgÿ È€™¥÷—('J²³7Ò±Ó Ì;Ï;E¤P2SSÙ”¤»¾ýåW4kq_M˜à"qý"77×; ‘™U-ègà nÉz"/''‡›o½M‘¸bÀò$]xî½÷õ�.IX‹-æ‹/¿òÎ�HšôŠ4�˜YyàÎHEÚ“O÷ÒC}$.%ã­€¹ÀÊdßY^¿þ½¶)ð4@QW�î�ª—¼%²†Åëo„j&‘BKÆ W¥êAH’ø&|ý5 .ô΀¼MJïø‹…�òŸöwkD“"`úŒÜwÿÞ"Ŷ4 ÷HÖÓ’\ÌŒ#Fyg�T�íø‹EY¸ (±œÈÊÊâÆ›ocãÆÞ)"Å–Œo†ÉxÚC’Óˆ‘¡� ¸€™¥7F<§„èÖßÿÝ;C¤D’ñV@ �’,,\È´é3¼3�NØñ »ÐØ;²-%3hð;Œ5Ú;C¤Ä’ñÍpEžöä52«�ÇšÙÿ\y[Øà–(ÄÛ¼ùóyøÑǼ3D"¢~ãÆÞ 1—ŒC$¯Q£Ç““ãQ8|û_Øí�`f'�ÇD«¨¨rss¹÷¾.:ï/q¯Þ¾ûÒûÍ׸ûÙ^Þ)1—Œ×=HòZ¾b“§Lõ΀Nf TWþ÷í?€Òf?¿22Ò¹éÆë=r'6j•*AÙ²ÞY1µ\§�$É|ñå—Þ P”ÀÌjçF5§/^ÌÓ½žõÎ)¶æÍšòéGc¹é†ë)]z»ÛrwòhÜD¥S�’lB²+`‘V�.&oÁPx¨ç#dgg{gˆÙþûïGÿ¾½y¶×SÔ¨QãŸ_P»v죭H Í+"11{öÏ,[¾Ü;cïüöÀî€K£Sh&|­­~%îl[î9ì}Ž;v—Ò$Ñ�•’BvPÜ‘ŠÄ'3c„¯½3�Ýö;ý]hfGGÄ$g7¶lÙƒõôÎ)’.÷$‰�-ÿK²úò«P<ùòï`W¿/‹AH¡ôí7€ù xgˆÊþûïG×.÷süqÇþ›’è€�$IMž<Å;v7�äïü×.f9»°aÃ^{ãMï ‘ÝÊÈHçê«:pí5WïþÿŽ’i@w�H’úãÏ?Y¹rUªTǫ̈¿í/vv  )P36-»Ö»O?V­Zå!²KŸÊ‡cFn¹¿ I4�h�If3fÎôNØí5�Æ(d—Ö®Íâ­Þ}¼3Dvjÿý÷£_Ÿ·xíå©S’7q �"Iaú ÷çT3³jPÀ)�3+EÞ €»~°fíZï ‘Øc=¸áúN\yÅå¤Eâ -‰�d6=ª,/èwb# RŒcþaóæÍ 8Ø;Cäš5m½wßEÍš<KV®T¨�I0ðj@’Ù‚… ½�¾,èwbËX—dä¨Ñ,_±Â;Cäoûî³t¹“NüÇcµ#£ví¤�t $³ßÿƒÜÜ\RR\÷¨ _Ð"Æ!êÓ¯¿w‚ðßÍ|ÆŒ½7HšÓ�z°$³M›6±ÜGÀš°Ã5�fv0p KÎv&MžÌìÙ?{gˆÐ´ÉYÜwOçÈ.÷ïL �›ƒ€5©ÚX’ۢſ¼%xìÔ‚®�„bùÿƒaý$Éí»Ï>¼õÆk<ÿl¯Ø¼ùCR �ËÓÒ0ïg‹/öNøç �ÐÜ!älÞ¼™1c>ôÎ$•‘‘ñ÷Õý¥J•ŠíÁ“`7@=Xþúk‰wB-Øn�0³ àx·œ|Ÿ¯[ÿÄEãÆ§ÒµË}%»Ÿ¿$’`@�ŠÀêÕ«½j˜Y°ýïÆã€blaYc?üÈ;A’LÔ¯î/¬$�t  ¬ÎÌôN( TÞþwãI^%Ûäää0áëP<.Q’@FF×_w-Ú_ûåþ‚Ô¯>ú÷ßN›>>úÄ1(ò&edx'ˆ¸Ëô��jn?�œì–‘ï§Ÿ&±vm–w†$ÆOåûï¥n:Þ)ÿU¥ tîü÷ßÎyÿ^8É1HD¢!$@ù4�3+ íÃø/¾ðNW³f î¿÷šœu¦wÊnEd‹a œ�Hßö'Ì€=<K�>ÿâKïIP¥J•âê«®¤SÇŽdd¤{çŠ�‘Ä´~ýzïØn�hàšüùç_Ì™ó›w†$ £nH÷®pÀû{§Iª6ÌIH[¶lñN�(³m�pßýoüçŸ{'H‚©Vµ*ﺃsÎnIÞ9E¦�‘Ä’ =4À7ß~ç "55•‹/jÇ­7ßDùòå½sŠM€HbÚ¼9\ÀÁ®ÀÔiÓ¼$4hp(Ý»uå_GîRbi: ’rrrÈÉÉñ>ÍW&ÍÌR�ד£+V¬dÉ’¥ž ç*V¨Àí·ÝJ» Úz?f3bÒJi@$QmÞ¼…Œ × = ¨�”ñ¬˜>c†çá%ŽAÀyçžCç;ï J•ÊÞ9uì1ÇðÛ/³¼3ÄÉ;CÞå¾.]½3$q¥l�\MŸ®@Š®Þ¾ûÒ­kN8þ8ï‘")]Ú}÷ÑìP �Ó¦O÷N8’‘‘ÎÕWu SÇk±…¯ˆH¤¦¦zŸÿ‡üÀý2iÂrbŸˆH …àÓ?„a�X¾b+W®òL8P·n]ºv¹SOqd…ˆH‰”.åþà]Ã)€ z^B.--K.ºÛn½™=öpß­ZD¤ÄJ…c`CPÖ³`Ñ¢Ež‡—;þ¸céÖµ ûÕ«ç""1!ù0³1 p½añâß=/!T½Z5î¹û.Z¶hî""q{î¹§wä¯�¸îš²tÙ2ÏÃKˆ¤¤¤pñErÛ-ñ½…¯ˆÈ®ìY©’wä�®+�Ë—/÷<¼„ÄAHχäÈ#ÿÏ;ED$ªB²°Ô}`ÅŠ•ž‡giiith7ßt¥K‡âÊX‘¨ªä¿°!‚5i€ësR3׬ñ<¼8jxÔèñЃì¿ß~Þ)""1‚S�¸ŸX¿~½çáÅAùòå¹å¦¹ô’‹æÁ=""…U³f ï„? o�p]ÈÎÎö<¼ÄØ™gœN·î§zõêÞ)"".öª[×;áï�·`¹¹¹lܸÑëðC+Tàî眖-¼SDD\íµWx�·�3ó:´Äпþu/=ÿ,5j¸/{‰ˆ¸*S¦LV@ÿ„¼Oÿ®§�$ñ•)S†´´Pl})"âªn:a¸öéï ׫ 4{$ƒ~˜H³–góñ'Ÿz§ˆˆ¸Úgß}¼�‚ó�’’†g"K ¬\¹Šën¸‰Û︋U«ôôGIN‡z¨wÀ,È�\Oćä¡#ÃGŽâŒ³šñö;Ct ˆˆ$Ã?Ì;ᯠVó �h�HFkÖ®åþºqñeW0Á9ì°Þ ³¶ýE ãB¹r®O#G?ü0‘³ÏmÍ[½û“ãúC‘¨«U«&ÕªVõÎøŸÀu j÷ÿâ(;;›‡}œ6ç_ÈìÙ?{爈DÍ¿Ž8Â;`æ¶¿p�ªW¯æyx ‰é3fpnë¶<ñäÓdgks(I<'6:Á;Bµà¿"!‘““믿A“f-ølÜx:éĽ L×�Ô­SÇóðBüù';]OÇN×óÇŸz爈”ØAH­Z5½3–A°rÛߤ�®ãÛ{ï½</!öÙ¸ñ4iÖ‚W^{-[¶x爈Û©§œì�ðýö“d9…��dײ³7òäS½hÚül&LøÚ;GD¤Xš5mâ�0aû¿IÖ:…�°÷^{Qª”ö‰—][°p!í¯º†ÛïìÌŠ+wÿ ""!±Ï>{Ó A(v�üfû¿q_(Uª°¿g‚Ä 3cøˆ‘œÑ¤}û ÐÞ"Z4oæ�° øiû_p_�8´~}ï‰#YYY<ÔóaÚœ!ÓgÌðÎÙ¥,ÿO ‚`Óö¿à¾��¤x¦Ï˜Aë¶íèúàC¬]ëþc‘hÐàP>è ï Øaù ¼€Í@é˜çäkxÔ¼-q.77—ƒóÑÇsOç»8»e‹„yÌôwßÏ%—µ÷Α8¿Mkï„mþquJ9À\‡˜¿rÈÁTªTÉ3AâÜŠ+¹ýÎÎ\téåÌ™ó›wNDlݲÕ;ADJ ==-š{g@ÞS¿ÝñSòþ5¶-;D¤¤ptã<$ALœø#gŸ×š'Ÿê÷[ oÕEŽ"qí¬3N§b… Þ�3ƒ X¾ã/†b��8ö˜c¼$AlÙ²…W^{=î·Ö]"ñ­Ýç{'l3¦ _Ü6�̉aHŽ=öhïI0Û¶¾¦ÓõüþÇÞ9E¦ÝEâ×AHÃð¬l8�¤åÿ<« Kp�UªTfåÊUÞ)’`ÆÏ·ß~Ëõ®¥Ã•í}7ž3†/Ô—ºh1=–þå ðø¬\yÆ—-ç!í¯¸Ì;a›L ¸�þ;�ülÒcU´£ 8îØc5ºÀAE¤D²³7òäÓÏðÁð<ص‹ß)§I“àµ× õ¥{çÿHKÓJi�„°çž{Ò2ÿŒ ‚ ÀåÄ€ 6?Ä4©�MÎ:Ó;AÜܹó¸ôò+µ¥°ˆDÍmÛžîöyzGïîì¤l÷×_Æ d—N9ù$Ê–-ë! nÛ–Âg6mÆ€ƒt±ˆDLéÒ¥¹ìÒ‹½3¶Y|¼³ª ==ÓOkì!IbíÚ,ºuïÁymÎgòä)Þ9"’�Z·:—êÕ«{glóÁŽÛÿnoûà[Àý²ã<4A’ȬY³9ÿ‹¹³ó=:- "Å–ššÊUW^é±½7võÿ�‚ XG®8±Ñ TªXÑ;C’Œ™ñÁ°áœÕ´¹N ˆH±´hÖ”}ö Í¥»³‚ øÇö¿ÛKÙáïÇF1¦PÒÒÒ8KŠ“5k×ê´€ˆYJJ ×v¼Æ;c{¯ïî B7��´l¡Ó�âkÛi{U«´7…ˆìZËÍ9ðÀ¼3¶ÙôÛÝí8�LÜw9æè£Ù¿ý¼3$É™ï}ŸÓÏjJß~tZ@D ”––ÆÍ7Þà±½÷‚ Øí'—ÿ�‚ 0B° _t¡w†w·ÀC=¦UÛ ˜2eªwŽˆ„LÛ6­Ø{ï½¼3¶·Ë‹ÿ¶Ùq�`h„CŠ¥ÕyçhO� •™3gѶÝE:- "ËÈHç†ë:yglï y[AÀgämàª\¹r´i}žw†ÈÿØvZàŒ³š1pðÛ:- ’ä:^s55jÔðÎØÞ3ù«ù»õ Ó€‘O*†Ž×\¦íEþ¶fíZºvëι­Û2iòdïqP»v-:´oï±½¥@ßÂ~qA+�°‹½ƒc©zµj´mÓÚ;Cd§fÏþ™v]Ê=÷uÑi‘$sïÝÉÈÕ‡Ô§ƒ È.ìïl�øX™ž’¹öš«(S¦Œw†ÈNåææòî{C9³IsI'ŸtbØ`·x¹(ßPà�ÁfàH•T5tG€Ä…Ì5kèÚ­;­Û¶cêÔiÞ9"%åË—§çCzgìèÅ ²Šò ;[�è_˜ˆ¹é†ë¨Vµªw†H¡Ì˜9“6\ÈïaåJI4÷Þ}5kÖôÎØÞ๢~ÓN€ ¾f—¤(RÊ•+ÇMáÚdAd—¶¶À Áï››ë$"ÐøÔShÓº•wÆŽÞ‚`yQ¿iW+��¯3&âÎoÛšC>Ø;C¤H2׬ánÒªÍ:- çêÔ®Íã=BÞ)ÛÛ<YœoÜÝ�З¼¥w©©©ôìÑÔÔTï‘"ÛvZà£?ñN‘bHKK£×ÓO„ñiµÏA°¸8߸Ë ‚LBr1 À¿Ž8œË/»Ô;C¤XÌŒY³CqVMDŠèÞ»;óï#ôÎØÑjà‘â~óîV��^(î‹Gí7ߦç-‹ˆH‚kÛº—]z±wFA)ÌCvf·@“€Ð¬[fd¤óhÏ: ""Q×ð¨ÿÐýÁ®ÞY<_’(Ì �À£%9H¤5lx×]ÛÑ;CDDXÝ:uxá¹g)Uª”wJAî‚`cI^ P@ã€oKr H»ñ†ë8ö˜c¼3DD$UªT‰7_•*U*{§d0°¤/RØ�€'Jz°HJIIá‰Ç ã™""Ç222xãÕ—Ùÿý¼SvæÎ J¼¹HQ€á„dc mjÕªÉ3O?©ëDD$"ÒÒÒxá¹gø¿ÿû—wÊÎŒ ‚àãH¼P¡€üiãñH4’5:»ïºÃ;CDDâ\jj*O>þ('Ÿt¢wÊάnŒÔ‹e�òÎ9,ŠÔÁ#¥ý—Óê¼s½3DD$N¥¦¦òøcТy3ï”`O…�� IDAT]éÁÂH½X‘€ ¶�¡¼â¡»Òð¨ÿxgˆˆHœIIIáч{pNËÞ)»2 x&’/XÔ�€~ÀäHFDB™2exõå—8ø ƒ¼SDD$N¤¥¥ñÄãrÞ¹çx§ìJ.Ð1‚­‘|Ñ"�ù×ÜɈH©P¡<½ß|ºuêx§ˆˆHÈ•.]šçžy:ìŸü^ ‚à»H¿hqV�‚àsòî êÕ«óæ¯Rµjï ©òåËÓ·÷›œyÆéÞ)»³¸'/\¬ ß]À–H…DÒþûíÇ€¾}4ˆˆÈ?Ô®]‹·õ—ëÆnÎ0_Ä{�‚àWॶDÔìÏ ý¨^­šwŠˆˆ„Dýú‡0dðÀx¹^l@C¢õâ%Y�xˆ¼Ç†Ò~õêѯÏ[DD„&gÉÁƒ¨Y³¦wJa,"‚÷ü¤D@+{#Ô°?CÞD½}÷õN©©©ÜqÛ-<ÿl/22Ò½s #¸,ZKÿÛ”t�àUBô¸à‚Ô­S‡w‡ æßGé"""1´çž{òæk¯pmÇk‚À;§°ž‚à‹h¤Ä@\KÞ…¡U©bEúö~ƒÆ§žâ"""1ðï#dÄCiÔè˜<‹Eb€ æ¥Û")##ƒW_~‘›n¼>ž&A)‚ÔÔT:^}ƒô¥V­¸8ß¿Í&ò–þ7Çâ`�ò½|Á׋Р¸é†ëyúÉÇIO‹sA""RHuj×f`ÿ¾ÜyÇm¤¥¥yçÕíALÕÁ"6�äïØØ©×Œ¦–-š3dð@öÝgï)¡ ¸¨ÝŒ9Œ£þóoïœâèÁ‹±<`$W�‚`Ð%’¯M‡ZŸá¼Ç9g·ôN‘bÚk¯½èÛûMº?Ø•råÊyçÇd S¬Ñ ß3À¸(¼nT”-[–§žxŒÇéÉ{ìá#""…Tºti®íx cFçøãŽõÎ)®eÀ9AdÇúÀ�òO\DÞþÅq£u«ó3j8N8Þ;EDDvãøãŽeÄC¹ã¶[âåÞþ‚l.‚`±ÇÁ£±@K+ÈÛÌ nÔ­S‡Þo¾ÎÃ=ºS¾|yïÙÁ~õêñÊK/ЯÏ[pÀþÞ9%uGþÃõ\De��‚à#àÑh½~´AÀùmÛðáè´lÑ\· Šˆ„@õjÕxèÁnŒ=‚ÓOkì ýƒ xÖ3 j@¾.À‡Q>FTÔ¨Qƒ^O=ÁàýiÐàPT©R%n¿õ>ûäC.lw>©©©ÞI‘ðpµwDTo’ ‚ ×Ì.&q¹VsÔþÍï aèÃxá…—øãÏ?½“DD^ÕªUèо=_Ô.Ñ.О œÁ&ï¨ï’Áj3;˜�Tˆöñ¢!%%…¶­[qîÙ-òî{¼ôÊk,]ºÔ;KD$áÔÛw_.¿ìÚ´n•ˆ›µý4öC~ +f'¸Íìt`4P:VÇŒ–M›6ñÞÐ÷éÓ·?ó,ðΑ8rIæj:¬^éJ½÷¬L¿J•½3ÄAœpÂñ\~é%œ|Ò‰¤¤Dûì´‹eÀ)AÌöÙ&¦W¸™Ù%@¿X7Zrssùü‹/éÓ·ß|ûwŽˆH\©Vµ*ç{mÚ´b¿zõ¼s¢i5Ð8‚)Þ!Û‹ù±™u&ïØù ðÁ°á >‚?ÿüË;GD$”Ê”)ÃI'6¢u«ó8åä“âq¿þ¢Ê$oÙ?tŸ]>‰›Ù ÀõÇŽ¶ÜÜ\¾ÿa"cÇ~Èç_~©a@D’^éÒ¥iÔèš7mÂiO×íz‹c Ð$‚©Þ!ñ�R€!@kãÇÒâÅ‹7þÿÛ»¿Ø:ë:Žã﯃•mlh¯ Kw## ÀPÚ Ã˜C‰"Á &D ‰˜xeP \xaôBãWšøçÂ1™Æˆ‹‰AÂ&"Ø*¸bHÜ¢QÖÑv]Iwúõâ9'ëš¶£Ýé~Ïsž÷+y’&}Î9Ÿô\ü¾ýý~Ï÷÷{~{èÏýéyΜ9S:’$­»ááaÞÿ¾›Ùwû^öݾ·ÍÕþÜÑ=#§–Š­Ågæ&àWÀ­¥2\l<ýÌaþðìw¡¤144ÄM7ÞÀèè{vríÎmn¤ö"Õþµž.úídæåÀ/€ÛJæ(å7N06>Î cl|œãÇ3;û§N*M’V\sÍöìe÷È»vÝ4ˆí­Åa஺<ê·’âåYfn~|°t–:™™™a®»\055EÎWÇ*dÂäÔä9÷NOOÓé,ìB§Ó9oQ133ÃÜÜܲ¿_êsÏg¾3Ïôôôª^³SSSÌg®Ûû«ž¶n½œwÄ@>&¶¬-[¶°á’útÀÛ~啌ŽÜÂð°m.ò$po‰“ýÖ¢x�™CT{>V:‹$I«”À7€Çº'â6B- �€ÌÜü¸§tI’Þ¦iàˆøié «U›� 3/ž OH’ïïÀ=q´tµ¨ÕBZDÌ÷?(E’¤nnêà5+��"âLD<| hÌZŠ$©:ÀW€»#âÍÒa.D­–�ËÌ;©ö4òAIÒ@9|*"~W:H?Ôº��ÈÌë©z\U:‹$©µ�Ÿˆ¥ƒôKí–�‹ˆ[€çKg‘$µÎ$ðPD|bhÀ @O·aÐ÷€ûJg‘$µÂ3Àýq¬tõPû€žˆ˜‰ˆOöÊ•$­—YàQ`ï þР€…2sU¿€Jg‘$ ”§©¦ü_.d½5f`¡ˆx¾Ž J’.Üðp[hè ÀB™¹ø!ðîÒY$It�ø\Dü¯t‹©‘3� EÄSÀÀÏJg‘$5Ê«ÀÝþ­üa� �€ˆøwD|œê4Áݰ!Iê‹Ià1àºî?‘­Ôø%€Å2sUáG…ãH’êcxx$"þS:LiW�ôdæuÀw€ÑÒY$IÅýx¸Û\N ÈÀR"â%àVª]­[Û‘$ðW`Dìwð?×ÀÎ�,”™Û€G€Ï› Ç‘$­¿—¯?Š_B+ €žÌÜ|ø4piá8’¤þûðUà€ÿÊZU�ôdæUÀÃTËC…ãH’.ÜKÀ7Ç#¢S:L´²�èÉÌ«/P͸4 IÍóÕTÿÁˆÈÒaš¤Õ@Of^<�|Ø^6$é<Þž¾GJ‡i* €2sˆª™Ðg€}ø÷‘¤:yø.ðýˆx½t˜¦s€[Ff^OUÜ Ž#Im5üø6ð”ûúÇà<2s#ð!à~ànì.(Iëmx–êž'"âµÂy’À*d滀»€Áƒ’Ô/³Àaà ð“ˆøWá<Ï`ºÍ…î>ܽ<ŽX’Þ¾y`ŒªEïo€#qºl¤v±�èƒÌ à½À~àT-ˆ· %IõÒŽG¨üCq¢l¤v³�X™¹x°ØÕ½v0Àg/HÒo¯P5ç9 üx!"¦Š¦Ò9,�.’ÌÜJU\Û½vW—qv/ÁÜd(©^8 œî^'ià5à¿ÀëT®ý8‹ˆ“e¢j5,�j*3ßIõýlàìrÂeÀ¦E·^Áò3 ›º¯YÊÂ÷]ʶî=KY*ÇZ­ô9k±¸¤ï×ï¢l3ým?ÝÏïBýqšjCÛź†ª1NÏ4Õ£s=“TÓï='©u8;¸ÏvßwbÑϧ#b5Ù$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I-ôá†(lGi����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon-off.png�����������������������������������������������������0000664�0000000�0000000�00000047343�15003540030�0021470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ôxÔú��� pHYs��Ã��ÃÇo¨d���tEXtSoftware�www.inkscape.org›î<�� �IDATxœìÝy|”×}/þÏyfÕŒv ’Іd$!Ø &ì‹“&mg¹MHÛ´N A|KR§½é¯·¿Vé½·M—KSjÍŒd~ñ¯é½W·m’´½M‚ìÇØ ÌjÚX…6„Fš™çÜ?@„E-3sÎ3óy¿^~ÙÍ< Ìù>ßsžs�""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""kªÍ”ßï÷Ùíö<)¥Ï4Í<�>)¥O1 €Oáû^)eÎ]/MàpÀˆ¢WJ’Rè7 #dšæ �— ÃèŽF£]Bˆ®†††þDþúˆâ�iïùçŸOw8•RÊ*!ÄØ¿«�ÌP�ÀHp¤!��.Þþ÷;RÊ6Ó4ïܹó|‚³M �"ÒÆž={\iiiu�–I)— !H)«�ÌVm �¼à˜”²Í0Œ#ÃÃÃ?ݵk׈ê`Dwc@DÊ´´´E£ÑµBˆ5Rʵ�p¨Î!�‡…?‰F£¯:Žƒ[·nTŠR �"J˜¯ýë9‡ãý¦iþ‚âý¸ÕÂOEQ�oø €H)_nhh+ÎD)†�ÅUsss]4ý˜â)�ËØTgÒPŸâ¦i~Ûn·ÿ »”,�ˆ(æš››W˜¦ù)�P©:ÅŒ�ø±âÛ‘HäÛ;wî¼¢:%'�DÁ`p>€_“Rþ8èÇJÀ�¼ÐÝÝýƒÆÆÆˆê@”<X�Ñ´íÞ½;Íëõ~DQàªó$¹ëRÊR~}ÇŽo«CÖÇ€ˆ¦Ìï÷?.„ø<€OHW'½.¥lúæ³Ï>;¬: Y �"𔯯F£°°ðÆaìïöuq @‹ÍfÛ³uëÖnÕaÈZX�ÑCùý~€mBˆgÌQ‡Æ5 à¦iîÞ±cÇÕaÈX�ѸZZZ2¢Ñèç�|ÖÚ‰/•™¸µhðoêëë¨: é�ÝÃï÷{„_�ðŸ�d«ÎCÓö²âÿݾ}û+ªƒžX�€[sü³gÏþ!Ä_‚­þdrÀ®¯¯YuÒ �"‚ßïÿ¸âOÌW…âæûBˆ?ؾ}ûQÕAH,�ˆRX0\ àÏ¥”ëUg¡„0|SJù円†‹ªÃZ,�ˆRßï/Bü nmÕK©ç†”ò«CCC»¹@êb@”BZ[[m½½½»„nà“ò¤”†a|qûöíÿ¤: % �¢–�ðX¡: içÇ6›íó[·n=¥:% �¢$wû±¾?ð»àQ¼4±a)å_ŒŒŒüÙ®]»FT‡¡øc@”ÄÀG¥”Ï !JUg!Ë8nšæoíØ±ã ÕA(¾X�%¡}ûöe‡Ãá½�~Uu²¤€¯fggÿ—Í›7ªCñÁ€(É455­Büâ1ÕYÈÚ¤”m†aüÖöíÛ¦: Å �¢$ÑØØh/**úÜÚ»ßP‡’FXñŸ·mÛöWB©: Å �¢$ð /ÌŠD"ÓKq"„ø‘iš¿É „’ �"‹ O�øßàþýWLÓüìŽ;þEuš9¶ ‰,, >ƒ[‡½pð§DÈ7 ãû@औ¼´8þYÐíùþÝ�~[uJYß—R~¦¡¡¡_uš�DÓÜÜœ+¥l•R¾_uJy§…ß¾}û ÕAhêX�YHKK˼h4ú=�•ª³ÝÖàWëëëÿUuš® ²ˆ`0¸2¾þ¤—l�ß;T¡©a€È‚ÁàRþ€4ÕYtçp8àp8`·Û!„€a°Ù<Án·#‰Üóµh4 Ó4F‰DîüC“#„ø/Û¶mûcî` ,�ˆ4~À×ÀŽ„HOOGzz:¼^/¼^/<<ìv;G\®‡1::Šááa„B¡;ÿÂÀÀ�FG¹[î!Ä7LÓÜÞÐÐV…Ž�‘ÆÀŸøTçPÅ0 dgg#77¹¹¹ÈÉÉ‰Û ?###èïïÇàà zzzÐÛÛ‹p8¥Ç¿ï9ŽOmÙ²%¤:MŒ�‘†¢¢¢ç¤Ü¼jZZ 0kÖ,ø|¾qÛ÷º“R¢¿¿===¸zõ*®_¿Ó4UÇJ(!Ä\.×Çž~úé!ÕYh|,�ˆ4ã÷ûBˆo�ø5ÕY%33EEEÈÏÏGff¦ê817::ŠË—/£»»===©T ¼ …>¼k×®ÕAèA,�ˆ4²oß>w8þ�V%ÞÒÒÒP\\Œ¢¢¢¤ô'‡ÑÝÝöövô÷§Ä:‡Ç·lÙÒ§:Ý‹�‘&nþßð!ÕYâEÙ³gcΜ9ÈËËSG¹þþ~\¸pÝÝÝIý´âµÑÑÑ>óÌ37Tg¡Ÿc@¤Ý»w§¥§§ÀUg‰ǃ²²2”––Âår©Ž£H$‚ÎÎNœ;wCCI;e~àÆ~öÙg‡U¡[X�)v{ðÿ€_P%ÖrrrPYY‰üü|Á›G‘RâÒ¥Kxï½÷ÐÛÛ«:N<üsvvöÇ7oÞÌç&5`½åµDI¤µµÕišæ·dmŸÏ‡E‹¡¦¦éééü'IŒŒ ”••aÖ¬YM¶Ž@ÕÈÈHͲeËþñ¥—^âfAŠÙU JU­­­¶¾¾¾ÿÀ/©Î+ùùù¨®®Fvv¶ê(–—““ƒ'žx8}ú4.]º¤:RLH)?UTTÔ àYÕYRËr"¤”¢¹¹9 ¥Ü¦:K,dgg£¦¦>ŸOu”¤•„…Àö¨Î‘Ê8@¤@QQÑðÕ9fÊëõbÑ¢E¨­­…ÇãQ'©¹\.ÁçóáÆ…¬½ÉžâCùÈGŽ~ï{ß;©:Kªb€(Á‚ÁàÓRÊo¨Î16› UUUxì±Ç`)DÝÝÝ8yò$nÞ¼©:ÊL K)×644¼©:H*b@”@ÍÍÍkLÓü�Ë> WPPÀ;~MD£Q¼ûî»8{ö¬•wlF£OìܹóŠê ©†S�D  +¤”?`ÉmïÒÒÒ°téRTWWky O*2 yyy(**Â7¬Ú È2 ã‰eË–}ó¥—^²lcEì�PÒhiiÉÍ´Ûí™�î¹=•RæLö}¤”ƒ†aD„¦aý�¡Pè†Ífihh˜Ö'lKKKF$9$„¨›ÎëU+--Å‚ 8ðk®½½'Nœ°äI„Bˆ=Û·o·üº+a@–°oß¾ìh4Zmšæ<!Ä<)e•”²J‘ @6÷ç¹@€ë�zoÿ÷y!Ä)å)·ÛýöÝ' ùý~â[°à..— ‹-BAAê(4IÃÃÃ8zô(®^½ª:ÊtüF}}ý7U‡H,�HK---E¦in�°NJ¹À|XçÏkÀÛRÊŸ†’R~@¹âLSVPP€Å‹ÃétªŽBÓpîÜ9œ8qÂjkMÓ\¶cÇŽ3ªƒ¤«| R’Û³gOfZZÚ¤”¿`€JÕ™R•a˜7oæÎ«: ÍÐÀÀ�Þ|óMܸa©3x~ …ÖìÚµkDudÇ�I™¦¦¦*!Ä'„¿`”’ÌŠ¹\.,[¶ ¹¹¹ª£P dffbݺu8zô(ºººTÇ™¬e.—ë«�~GudÇ�%”ßï¯B|À',V‡~ÎçóaéÒ¥<­/IYlJ@J)Ÿjhhø¾ê ÉŒ�ÅÝÞ½{óívû¯K)? `‰ê<ô ²²2ÔÕÕqSŸ$wíÚ5¼ù曵Äa| è۶mÛuÕA’§�(.íEEEOø-�¿Äö¾ž„¨©©á|Šðù|X³f ~úÓŸZá”ÁÙ¦iîðªƒ$+v�(¦öîÝ›o³Ù¶�Ø `Žê<41›Í†¥K—¢°°PuJ°p8Œ7Þx===ª£LÆ'ëëëÿQuˆdÄ€b¢¹¹y©iš_ð)�|nLs‡+V¬@NΤ÷G¢$cš&Þ|óM+œ.xYJY×ÐÐpMudÃ)�š‘@ ð�¿gš¦å6¹IUn·ï{ßû‘‘¡: )düq¼ýöÛº?!P „Ø àiÕA’ ;�4-MMMO†ñÇ�–©ÎB“—––†•+WÂëõªŽBšRâØ±chooWåa¤âÉíÛ·¿¤:H2a@SÒÔÔô‹†a|À ÕYhj¼^/V®\‰´´4ÕQHCmmm8þ¼ês";;{ÉæÍ›-ñƒp €&¥©©i•a `ê,4ucmþ4‘ÚÚZD£Qttt¨Ž2‘ù}}}_ðgªƒ$ v�è¡‚Á`…iš&„Ø þy±$—Ë…Õ«W³íO$¥Ä‘#GÐÝÝ­:ÊDnF£ÑÚ;wžW$°@ãzþùçÓÇJ)¿(„àÖpåp88çO“&„À’%K0::Šk×´\tï1 ã«�~UudÀ;:z€ßïÿ8€=BˆRÕYhúl6V®\ÉGýhÊÂá0^{í5 ¨Ž2)„X½}ûö×U±:v�èŽæææ9Ñhôo…O©ÎB3·xñbþ4-cûD¼úê«…BªãÜO�Ø-¥\#„ªÃX™Mu�ROJ)Š‹‹?/¥ü–b¡ê<4sóæÍCyy¹êdav»>ŸR»q¶ôÍ7ß|gÿþýÇU±2N¤¸`0X!¥l°IuŠÒÒR,^̃)6ºººpäÈÕ1Æs.;;»†NþJa@`»”ò(8ø'ììl,\È&ÅNqq1***TÇOEooïVÕ!¬Œ€´oß¾ìp8ðIÕY(vÖ¯_Ïgý)æLÓÄ믿Žë×µ;™·ÓápTmÙ²E»… VÀ@Šinn^‡ßÿ¤"„ÀÒ¥K9øS\ŒàphwªwI8nPªX�¤`0Xošæð˜Þ¤SYY‰üü|Õ1(‰¹Ýn-×–!~ßï÷{Tç°">˜^|ñEo(j–RróŒ$”““ƒêêjÕ1(bΜ9¸pá‚ê(wH) ìðßUg±v�’\sssåðððëàÎYIÉf³aÉ’%‚Ëy(1,X�G¯n!Äý~¿vóºcÄšššV™¦yPQ§: ÅÇüùó¹Í/%”ÍfÓq* Ä0ŒO«a5,�’T ØlÆ�pb8Iù|>nöCJäååi÷gOJù{RJ¶Â¦€@ àà’ð$e³Ù°hÑ"Õ1(…ÕÔÔÀív«Žq·EÁ`ðýªCX €$"¥@`7€¯€{<$µÊÊJíæa)µØív,X°@uŒ{!¾¤:ƒ•pH­­­Î¾¾¾o€‹ý’žÇãÁÆa¬ßI½×_]«£ƒMÓ\²cÇŽ·Uç°~‚$Ý»w§õ÷÷üSB]]Ò†n Ãø¢ê VÁ€ÅíÞ½;-==ý;�~Au]8N¤§§ÃívÃétÂétÂápÜùÇf»÷ÌÑÑÑ{þ{ìŸP(„›7obhhÑh4Ñ¿ŒqbùòåªcÝ£­­ çÏŸWc̰Ãá(Ú²eKŸê ºãF@–ꃿÝnGvv6²²²àõz‘žžŽôôt8Θ_+ áÆD?úûûqãÆ„“jæÏŸŸ°ëMVuu5ººº‡UG€´p8ü«�šTÑ �‹Ú·oŸ;§Ôàïp8››‹ÜÜ\äåå!333a­G·Û ·Û ŸÏwçkÑh½½½¸~ý:®_¿ŽÞÞÞ¸v Š‹‹ùÌ?iÉétbîܹ8yò¤ê(c¶€À#q À‚íEEEÿÀÇUg‰7¯×‹¢¢""33Së樂èééÁÕ«WqåÊ Æì½ ÃÀ¦M›xØi+âÀ…ô8˜Ï0Œ…Û¶mkSCg¶G éDJ)Μ9Ó‚$^ðçõzQ^^Žºº:ÔÔÔÀçóÁívk=ø·Näóx<˜5kÊËËQZZ ǃH$2ãÅòòrÅ()Qì†!®^½ª: �@JÚ¿ÿÿQCgz¢Ò‚Áà_I)“îYW»ÝŽââbÌ™3™™™ªãÄÜðð0º»»ÑÙÙ9å΀ÍfÓO> —˧tD±¡YàŠ”²¤¡¡A‹… :â� ƒõÉ6ø{½^”••¡¬¬LdzÆc&-- sçÎÅܹsÑßß . ««kRkÊÊÊ8ø“%Øl6<öØcxçwTGnmƒþa�ßVDWœ°¿ßÿa�‡$Ù»Áçó¡¶¶µµµÈÍÍ}àѼdæv»QPP€òòrØl6 NX†åË—Ãng­NÖ™™‰ööv-B8öïßߪ:‡®8`MMM‹ Ãx@†ê,3•——‡ššää䨎¢h4ŠŽŽœ={öÖéØZ"+9uêΜ9£:�Üp8³¶l٢Ŝ„nx[¡¹çž{.Ï0ŒoÁâƒff&*++¹m6› ååå˜3g:::púôi„B!†¹s窎G4eåååx÷Ýwaš¦ê(飣£ïð}ÕAtÄ@cv§ÓÙ  Bu–éJOOGMM UGÑžeee())Á… 022ÂÇþÈ’\.ŠŠŠÐÙÙ©: Ãø8X�Œ+)æ““UQQÑŸxRuŽé0 ÕÕÕX¿~=ÿ)2 ¨©©Q…hÚ{ì1Õ��Rʧ9Öƒ�MƒÁOK)ŸUc:|>.\È]ëˆRXff&²³³Ñ×§|Kþ‚¢¢¢•�©¢�òûýeRʽªsL•ÃáÀüùóQVV¦: i ¤¤D‡��>�`[D3~¿ß!„h`©eòùùùظq#"º£¤¤D‹G|¥”SAG,�4cÆŸ�xŸê“5vBÝŠ+¸Y ÝÃn·k±H1¯¥¥… ݇€Fü~ÿZ)åRc²<V¯^ÍGÕˆhB%%%ª#��¢ÑèzÕtÃ@/¾ø¢W±ù=)**ºuë­: i,//O‹m¾…,�îc‰Á&„B¡¯¨Tc2ª««ñøãkñ—šˆôf TÇ€išëTgÐ � 455­ðyÕ9E¥K—¢ººZu"²Ù³g«Ž�!D­ßï÷©Î¡�е¶¶: Ãh‚濇+W®Dqq±ê(Dd1³fÍÒ¡c(�¬UB'Z:© ¯¯ïK�æ«Îñ0kÖ¬A^^žê(DdA†aÀçSó-„à4À]X�(´wïÞr�¨:Çä§§cõêÕHOOW…ˆ,L“`•ê :a Ífûk�Õ9&âõz±jÕ*¸ÝnÕQˆÈâfÍš¥:¤”µRJ¡:‡.X�( 7ø¸êIKKÃÊ•+¹¹Å„ÇãÇ£ü~'³¥¥…Û•ÞÆ@ÆÆFÃ4ÍݪsLdlΟGÑQ,åç竎�Ó4ªÎ  � V±TuŽñ¤¥¥±íODq‘“£Å',�nc`{öìq™¦ùÕ9Æc·Û±bÅ ÞùQ\h²sh­ê�º``.—«AQª:ÇýÆ6ùÉÈÈP…ˆ’”×ë…ÓéTƒ€ÛX�$ßï÷!~_uŽñÔÔÔh±]'%7 º�5~¿_ù®D:`X;¨ßó>¥¥¥<шBƒÀi³Ùx40X�$Ìž={\†aü®ê÷ËÍÍÅ¢E‹TÇ ¢¡É¦bzœQ¬ €IKKû¬”²PuŽ»Ùív,]ºBp_ "J ÖI)Y�€@B466RJíîþ/^ÌÿD”P^¯†¡vè1M“�X�$DqqñÇT©Îq·’’-Žè$¢Ôb†;²�� €„RîRánuuuªcQŠR]�†ÁsÍÁ î@-€ ªsŒ1 K—.…ÝnW…ˆR”ê@J©Ý^,*°�ˆ¿ªÜ­¼¼\—í8‰(Ei°Õ8 €¸zþùçÓ<­:Ç·Ûª*­–"Q Ò`ñq¦ê�:`GN§óSÐèZMM n€EDjiÐðª �q$¥ü¬ê crrrPRÂ…¯D¤žç8Z[[•‡P@œ477ϰNuàÖA?µµ<�‹ˆô C'r``@‹- Ub'¦i~šüÿ---Õaÿm""�Zt�FY�¨Ä~Mu�àÖc•••ªcÝa†òÝ� ÃHùu�,�â ©©i€Õ9� ¸¸Xù3·DD÷SÝR² :@2BüŠê À­¹Þý=È4MåÏ"ªÆ „ŸT�fÏž ¯7å»\D¤!Õ§Úíö¨Ò�`c{÷î-°Hu�Üô‡ˆ´¥º�R²�P Ù†ñ!Õ�   @‹s·)µ½ûî»Uƒ4Ä@=�1&„øEÕ€[þ©ÖÞÞŽàÂ… RªŽCt‡aÕTcC~¿ß`“êN§ùùùªcAJ‰p8ŒcÇŽá•W^Aoo¯êH¤‰hTí x4e :@21 c%€,Õ9JJJ”?cK�¦iÞùï:tGå´�ÝógCN�<>†LÓ\¯z^ �÷ü'mÜßö—R¢½½/^Duu5ÊË˕ϓª;�œ` ¦ ÃX«:Cvv623µ9€RÜDóþápÇÇÁƒÑ××—àT¤Õ€"¬4€X�ÄHkk«MJ¹ZuÞý“Nµð¯¯¯ä´@Š ‡µ{oª §�bd```�å·Þ………ª#Ý1™yÞ±iK—.¡¦¦¥¥¥œHr:{£££Cª3¨Æ@ŒD£Ñª3dggÃív«ŽAtÇTzŽŽâèÑ£xõÕWù´@’QA^½z5å;�,�bçqÕx÷OÉ ¿¿‡±cÇtiSŒiÐ¸ÙØØ¨ö1 °�ˆÃ0”�|öŸt"¥œöæ?RJ\¸p@{{;7J2t�R¾ý°�ˆ‰ÖÖV§”r¡Ê N§“[ÿ’Vb1hsZ 9 +½¾”ò†Ò�š`½½½ó�¸TfÈËËãÂ)ÒJ,ïÚ9-\nÞT>ý®<€X�Ä€aóTg˜5k–êD÷ˆuÛžÓÉchHm^Á�X�Ä„”²Fu†œœÕˆî¯­^9-`}ª;�B®� €˜R*í�8¤§§«Œ@ô€xïõ>6-ÐÖÖÆi Uþû%¥d��1!„PZ�dggsþŸR’”çÏŸÇÐÑÑÁi P�\RÊ”ÿÐdå*/ž•¥ü�B¢$ò´·ÑÑQ¼ýöÛœ°€þþ~Õ�àCÁ`ð'@ Zu•X�Ìо}ûÜ�|*3ðñ?Ò‘Š»ñ±i·ÞzK‡Ífhšt��`-€·À—[[[mªÃ¨À`†FFFJ(m%±� ©:ï]J‰ÎÎN8p�çÎã´€f4é�ŒIðÕ¾¾¾Ÿøý~勹À !”¿'„à@¢qŒ9|èÐ!Ý”‰D”?8ÕBˆ7ý~ÿ—R©À`æf«¼¸Ëå‚að·‘ô£ªp¿ÞÞ^¼úê«|Z@½½½:wdÒ„Õ××÷j0œ¯:L"pä˜!!DžÊë{½^•—'š.�ðó§~üãsZ@¡žžÕ&c¥”òH øB²?)À`†„¹*¯ïñxT^žÈRîžÐh1Zʸ~ýºê“åðµ`0ø­çž{NéM^<±�˜9¥€Ë¥ô¢ éÔ¸_oo/^yå?~œÓ FÑ××§:ÆT}Ìét¾6¨,�fHJ©´:t:*/O4!ÝÛìRJœ;w/¿ü2ºººTÇIz½½½Z…Qà‡@à ªƒÄ €™Sº ;�¤+«|؇B!9r¯½öUÇIZ—.]Ra&ì�¾þn÷îÝiªÃÄ €™Sú¾ÃáPyy¢ éÞ¸_OO~ò“ŸàäÉ“ˆF£ªã$+W®¨Ž ŸÉÈÈøÑóÏ?_¨:H,°�˜!)e¦ÊëÛl)óÈ*YŒÕ �àVæ³gÏ⥗^²ú«V•Ÿ�+RÊU‡ãpSSÓrÕYfŠÀ !”�Ü€te•)€ñ ã7ÞÀáÇ“fàR)IîþïVbÆË@࣪ƒÌG™SZ�Øív•—'š;�÷»|ù2^~ùeœ>}ÚÒjÝÝݪ#ăÀ?Ï«2],�fNi@¤«d0£Ñ(NŸ>—_~9ïdãnhh(™·b¶x> þMcc£åÆÓ¤Þå(ÞöìÙ®v5ð�� �IDATãr»Ý!•Ö¯_ÌL=kP(„þð‡ªcÅTAAêêê––4‹ÁãêôéÓ8}ú´ê‰ð‡ã7¶lÙ¢tL˜ ËU,:IOOW>ò&Ë]‘UŒM ¼ûî»üû7 ª#$ʯ„Ãánii±Ìñ¬,�fFùot2̳YM$Á‰'ðꫯZi{Û„NµE”£Ñè›››•î;Y,�f ‰(ï�D"ÕˆRÖÀÀ�:„·ß~###ªãh'-- K–,IµýJV˜¦ù’ö `035ªps"õ:::ðÒK/áüùóìÊݧ¤¤›6mBUUU* ÇË~¿¿Lu‡á"À)سgËétnB|Dña�åª3-Z´eezþã"@JEÙÙÙ¨««Cvv¶ê(Ú‰F£èèè@{{{ªœÆxÁ0ŒlÛ¶í¬ê ãað­­­¶þþþ'MÓü5!Ä'�hõ·º¦¦•••ªcŒ‹�¥*!æÌ™ƒyóæ¥Ò]ï”  ½½]]]IÝÉ”RvØl¶uÛ¶m» :ËýX�L ¹¹y…iš¿`3€Õy&R^^Žºº:Õ1ÆÅ€RËåÂüùóQRR¢:жLÓÄÅ‹qáÂ…d^Py6¯{æ™g´Ú_šÀ]ü~¿Cñ+�¾à}ªóLFaa!–/×sKj�D·äåå¡®®ÊÒÚ7ÐÞÞŽÎÎNŒŽŽªŽkÇ ÃØ¸mÛ6mª��ž{î¹<§ÓYàó¸uö³edeeaݺuªcŒ‹�ÑÏ !PYY‰ªª*žáñÑhíííx÷Ýw Yf_Éø©ÍfûÀÖ­[µ8w:¥ €^xaV$ù}�;pk_gËq8øÐ‡>¤:ƸX�=ÈëõbáÂ…ðù|ª£hOJ‰ÎÎNœ={CCCªãÄÊK7nÜøågŸ}vXu”,�¾þõ¯çØl¶ßBì®:ÏL}à€ÛíVã,�ˆ&VRR‚ ÀétªŽ¢=)%º»»qêÔ©dÙXèûRÊO444(]ý˜RGɵ´´dD£Ñßð,€,ÕybehhHË€ˆ&ÖÙى˗/cÁ‚(--UGkBcöìÙ8wîΞ=kõ'>,„hð´Ê6•O”ÆÆFã3ŸùÌçLÓü€�HªÑ2;;[ËgŽ#‘Þ{ï=Õ1ˆ´eš&._¾Œžžäææ²ðBäææ¢¬¬ ÑhVÞxiñSO=Ú¿ÿAU’¾�hnn^‘žžþ�vðªÎN§……úí:É€hr†‡‡ÑÑчáe1¯›Í†üü|b``ÀÊ Ÿ|ê©§ÞÚ¿ÿ)OÚ5�{÷îÍ·Ùl ` ’|ËãÌÌL¬_¿^uŒp �ÑÔù|>,[¶ŒM’”çÏŸÇ©S§¬z6Ê ”rMCCñD_8éF)¥ðûý 6›í€­HÂ_ãýnܸh4ª:Íaðù|ü§@ŠŠ ¬_¿³fÍRg:2„ß}á…>©:�{÷î-·ÛíÍRÊ÷«Î’h«V­B^^žê÷`€hò222°dÉde%Íúd%:::ÐÖÖfÅ›¢W²³³?°yóæ„퀔k�¤”¢¸¸øó†aü48¡OÇ£]À5�Df*++±téR¤¥¥©ŽcyYYY(,,Doo¯ÕŽhž …ŠöïßÿÝD]Ðò€T¾ë¿Û¬Y³ð¾÷éµ{1;�D—™™‰Å‹ó®?LÓĉ'pîÜ9ÕQ¦DñÙíÛ·¿˜ˆkYz~< þ†Íf;–êƒ?�\¿~¦iªŽAD“`·ÛQ[[‹uëÖqðÃ0P[[‹Çv»u¶¼‘R>×ÜÜœ#^-ÙØ·oŸ;‰ü¹”r—ê,:Y¹r¥VÛ‹²@ô üü|,\¸íþÄáÇ­´‹àϲ³³WÇ{=€å:�MMM ÂáðütåÊÕˆhN§K—.ÅŠ+8ø'XFFÖ¬YƒœœÕQ&kYooï÷E,U�ß2 ã§�jUgÑÑÕ«WUG ¢û!PVV†M›6¡¸¸Xuœ”år¹°jÕ*””XãÀW!Ä—ü~ÿãzx¾y¬øý~‡âo4¨Î¢» 6hsæ8§�(ÕeffbáÂ…VºóL gΜÁ©SJ6ß›!Ä¥H$²xçÎqiïjßhnnÎBü+8øOJww·êD)Ïáp ¶¶k×®å௡ªª*ÔÕÕ©ŽñHRÊB›Í¶/^ï¯uÐÔÔTešæA�›Tg± �Dj`Æ ¨¨¨€ahý›ÒÊË˱téR+üýr øL<ÞXÛ_y0ü…Ûóý)¹±Ït ¡¯¯Ou ¢”ãõz±bÅ <ñÄ<žÛ"Š‹‹±lÙ2+_óûý1ÄKË_u0¬—Rþ��ÅšvˆÇ0 TWWcÆ ÈÏÏW‡¦¨  �Ë—/×½ð !þ<ÖoªÝ¯8ü¶”² €uvnÐLww·•ÏÈ&² ŸÏ‡õë×£ººZ÷„"??ß €-Á`ðÉX¾¡V¿Ú@ ðe�{`‘§t …pýúuÕ1ˆ’–ËåÂ’%K°råJ¤§§«ŽC1PPP€Å‹Cm‡!¥Ü»oß¾˜Í/iq—-¥Á`p€ÿ¨:K²èííU~8‚Çš¦0Ó4­x"ÛC†ŠŠ TUUYj{Yšœââb„Ãa´µµ©Ž2‘êH$òe�_‰Å›)/uZ[[m}}}/�xZu†ǃôôt¤¥¥Áív#-- .— v»‡v»RÊ;®‘HÑhôÎN'ÒÓÓuogQ’ëîîÆ›o¾©:FÌäää`áÂ…ÈÌÌT…âLó}Fl6Ûâ­[·Î8 ÒVJ)@P‘’ƒ¿ÍfCvvö¼^/222tnA¥§Ó‰ Xf9š¹ªª* ¡³³Su”ñ¸¢ÑèW|b¦o¤t¤ñûý-„ø¢Ê ‰äp8——ŸÏ‡¼¼<¤§§s°§¤eõ€¥¥¥˜?>§²R”¯½öšÎë©ÖÔ×ךÉ(ë�øýþÆTü³²²PPP€üü|deeqÀ'²�náKB,_¾¯¾úª®§þ9€u3y%£Q0ü¢”ò¯U\;Þ„ÈÍÍÅìÙ³QXXÈ A(eY±àp8P]]9sæp ¸u”ð¡C‡‡UGÏÇêëë¿;Ý'¼�ƒ[¤”-*®Oééé())Aqq1ú$‚õ €Ù³g£®®.—KuÒÌåË—qøðaÕ1ÆóNvvö¢Í›7Oëq›„N455=%¥ "IÃ0PPP€òòråÜÑôx½^,\¸>_ÌwZ¥$QPP€ŠŠ œ;wNu”û-èëëû,€¦óâ„ Ä@ Àk�ô8«vìv;æÌ™ƒŠŠ ¶ø‰& {Àf³¡²²sçÎe»ŸIJ‰ƒêxÖJç7ªŸ}öÙ᩾0!€çž{.Àw`ñÁßn·£¼¼=öœN§ê8D4M¨­­…ÇãQ…,Bǯ¼òŠnëJÒÓÓ·øÛ©¾Ð‡0÷hll´gggÀ²x_+^ Ã@yy9–/_Ž‚‚ØlqÿßFdyƒƒƒ¸xñ¢ê÷ðz½Xºt)ª««ùhM™Ãá€×ëÕîÏ5€šeË–=ÿÒK/™SyQÜ;�³gÏþ€˜`H>ŸµµµÈȰtó‚(¥Ùl6Ì;•••l÷ӌ̞=ÅÅÅèêêRånÅÅÅ¿àMåEq-�nëûL<¯/uuu<Þ“ÈâØî§X«­­Åµk×022¢:ÊÝ~S,�âV 755-—RNyNB5!ÊË˱~ýzþDæñxðÄOà‰'žààO1åt:QWW§:Æ=¤”Ëý~ÿ¦©¼&.€_|Ñ …¾ ÀR+å¼^//^ŒÜÜ\ÕQˆhš ø³ºŸëu(^fÏžÙ³gkµ@ñ»�LöûãÒù€êx¼w¼”––bݺuü‰,,??7nDuu5Š»ºº:Ýžû¥æææI·&bÞƒÿAJ¹-Öï/v» .Dqq±ê(D4M ,@aa¡ê(”B\.ª««ÑÖÖ¦:Êašæï�Ø:™oŽi`ïÞ½Å�±|ÏxÊÈÈÀºuë8øY”a¨ªªÂ† 8ø“eeeº=%öé–––IŠYÐØØhØíöoH)-±'n~~>V¯^ ¯×«: MƒÏçÃúõë1oÞ<¶ûIÃ0P[[«:ÆÝ¼ÑhôS“ùƘM=#¥|¬Þ/žªªªP]]Í£y‰,(-- µµµ¼ã'mø|>àòå˪£ŒÙ‚Iœ“@KKK€ÿ‹÷Š'Ã0°dÉÌ›7ƒ?‘Å!PYY‰7rð'íÔÖÖê4®¬ijjªzÔ7Ť�ˆF£ 3ï/6› Ë—/GII‰ê(D4EyyyX¿~=jjjØî'-y<”••©Ž1FØl¶Ï>ê›f\�øýþØ<Ó÷‰'‡Ã•+Wrc"‹q¹\X²d V­Z¥ÛB+¢TVVjÓRþVkkëC«å­سg Àž™¼G¼9N~xYŒa˜3gæÍ›»=!‡–ÍXZZJKKÑÞÞ®: �÷÷÷? àß&ú†ýÍr»Ý�`ÞLÞ#ž\.V®\ÉÁŸÈB|>êêêžž®: Ñ”UUU¡££RJÕQ ¥üM<¤�˜ö€ßïŸ àËÓ}}¼9NþDâr¹ðøãcåÊ•üɲƺ�:Bürccã„7úÓî�†ñ'RJ×t_Ocsþü‰ôg***PUUÅv?%…¹sçjÑRæ­ðòx??­¿m~¿¡”òWg”,N„X¶l23µ~(ˆpkumm-ÿ¾RRñz½˜5k®\¹¢: „Oa‚`ZS�Bˆ?îkãI¥K—Âçó©ŽBDáv»ï¬îçàOɨ¼¼\u�€”òãýÜ”ñ@ °ÀGf”(N,X€¢¢"Õ1ˆhcíþ 6pOJj³fÍÒe«ù¹~¿¿f¼Ÿ˜Î]¼–;þ•––¢¢¢Bu "š@nn.Ö­[‡ÚÚZ8ÕqˆâJ¡M�ÀGÇûâ” €¦¦¦_°)&qb('' .TƒˆÆ1¶™ÏêÕ«¹0—RJII‰;WÞ^ð€)�6›íb'vÜn7–-[ÃÐnIQJB ´´7nd»ŸR’ÃáÐeÚU~¿?ëþ/NzÔ ƒ+¥”«b›ifÆý¹ÝnÕQˆbFJ‰óçÏ£¯¯Ou”)((ÀâÅ‹Ùî§”¦IñkB¬¼ÿ‹S¹m~6†ab¢ªª yyyªcÅL__<ˆ¶¶6„ÃaÕqf„]9" ??N§Su !ÖÜÿµIíÐÜÜ<Ç4ÍOÄ>Òôåå塪ꑧYB8ÆéÓ§qþüù;›‡˜¦©8Í”………ÊÏR®½ÿk“*Ñ¥”¿žK‡K—.ÕæÔ%¢ék÷ÿøÇ?ƹsçîÙ9Lõ.bDÅÅŪ#�ÀŠû·~ä ÞÒÒ’F·Å/ÓÔÍŸ?Ÿóþdy}}}hkk›p®Ÿ�¢ä›› —Ë…‘‘•1¼………K�¼1ö…G�¦inðÀêAU|>ÊÊÊTÇ š¶ñÚýD”¼„ðù|èêêRšÃf³­Å]À#§�¤”õqM46› ‹-RƒhZÖî;�DÉC“ÇWßýƒ‡v�š››W˜¦9?¾y&¯²²Gu ¢)ëííE[[úûû'ývˆ’‡ÏçƒBéßk)åûîþñC;�ÑhôéøÆ™<ǃ¹s窎A4%ápÇÇ¡C‡¦4ø,�ˆ’‰ËåBV–òÙôÒ–––;ÛqNØhmmuöõõ}:1™mÁ‚|®˜,CJ‰ .àÔ©SÓ~žŸS�DÉeÖ¬Yª7øÑh´Àaà!@ooï/ !´8W×çó¡°°Pu ¢I™N»<ì�%—ììlÕ ¥\€G�BˆÏ&,Ñ#TWW«Ž@ôH±^ÝÏ�QrÑ` �†aÜ9xÜ ¹¹9×4Í_J\¤‰åçç#77Wu ¢ I)ÑÕÕ…wÞy£££1}_"Jn·n·¡PHY†Û���RÊOp%,ÑCðîŸt«vÿxX�%Ÿ¬¬,¥�€;À¸«ê¤”ãžœhùùùZÌ™ÝottGÖêþÉb@”|4˜¨Ø½{w0NàöO¼?á‘ÆñØc©Ž@t)%ÚÛÛqòäɸŸÖÇ5�DÉ'==]u›×ë­pìÀãñ|�@Zâ3Ý+##>Ÿ!�úûûÑÖÖ†ÞÞÞ„\�¢ä“–¦|x€Ù¯�0 C‹ö?ïþIªöîg@”|¼^¯êÀ­àÞ5�RJà—•Ä¹‹Ãá@QQ‘ê”âÆÚý˜ÔÞý±Æ)�¢äãt:a·?ò¾¸B÷­hnn~€òƒ‹ a³ÙTÇ –èv?¥Ž´´4 *»¾”òÁ@Jùa5qîURR¢:¥¨ÑÑQœ<yÊ[ðì�%'Õ�nOÜ߇xRA{¤¥¥qãJ8)%:::pòäɘnæ3ª "ЧөôúLìÙ³Çà}¾"A !„PƒRÈÀÀ�Ž;¦]»Ÿ�¢ääp8”^ÿ)€´´´RJ·ºH·¨Ž@)"ãäÉ“hoo×òn›�QrÒ® ¥Ü .Î-v»íJˆÎÎNœ8q###ª£QŠQÝ�àî]°NQ;|> cÜ݉‰bbppÇŽÃõë×UGy$v�ˆ’“ê��{ccã­‡ �«Õæ¹µ÷?Q<D£Qœ>}çγÌÀªã´Íœê}��`Ö¬Yn;�Π|ƒb�—/_F[[†‡‡UG™�DÉI‡N·”Òe�Ã0©“••·[ùDJ"7oÞÄñãÇqùòeÕQ¦…�QrÒáI7›Ív« ¥¬RˆÿP¬˜¦‰wß}gÏžE4UgÚ¬2UADS£CÀ0Œ[€bŽê0\ýO±ÐÓÓƒ¶¶6Õ»lÅ;�DÉI‡­îïL�(Uš@vv¶êda¡Pï¼óº»»UG‰™h4Šp8¬:)b†ÅžêŽûí î± Ge¯× —Ë¥2Y””.\À©S§’n°À¿þ뿪ŽAŠTVV¢¦¦Fu JR†aˆ±5�™*+’¬¬,e×&ëêïïDZcÇÐ××§: Ѥi²¾çæØ€L•)ÒÓ•?H‰DpêÔ)œ?žóäDd9:�‘Hdxl Cev�h²¸…/Y�Æ:�P\�°@288ˆ¶¶6ôôô¨ŽBD4#:�áp8d÷ûýÜ{&@B !àñxT]ž4gÅ-|‰ˆF‡Ï2—Ë5l tôMKKÓâ‘ÒÏ¥K—püøqËmáKDô0‘HDy„†††°Ý4MC峦¼û§ûY} _"¢‡Uá&�(/�¸ÿ?1Mï½÷Μ9cé-|‰ˆFƒ=K†�Àn†Ò­¦¸·6½9zô(Ÿé'¢¤§º ¥¼�v—Ëe¨œp:Ê®Mê-ò{ï½÷øL?¥Õ€¢�ìápX¨\„Ç u]¹rmmm¸yó¦ê(DD £Ap¸= ò΋‡]¤žp8Œ'N ½½]u"¢„Sýd“iš]�`·ÙlBå€Ý®l R «« ÇW^© ¥T^�!n­0MÓP„€Ô022‚cÇŽáÒ¥Kª£)322¢|# ;S�ª �J~}}}8|ø0÷ï'¢”744¤:¤”Ý�ÀÁŸâÎívÃëõªŽAD¤œêö?�D£Ñ[€aJ{|ô+ù¹Ýn¬Zµ óçχa°æ$¢Ô588¨:Âp^^Þ%@ƒ@õ\%†sçÎźuë““£:‘ýýýJ¯/¥<¹yóæ(�###JG`nùšZ222°fÍ,Z´‡Cu"¢„R]��8>öÊ;�,�RSYY6l؀ٳg«ŽBD”CCCÊÏB¼3ößÊ �Õÿ3H·ÛeË–áñÇç¡PD”ôTGЫ�à£aTTT„ 6 ´´Tu"¢¸¹~ýºêˆF£?/�B¡Ò  ©¼<iÂáp`ñâÅXµj$¢¤tõêUÕB¹¹¹ïý@y€[ÂÒÝòòò°qãFÌ›7»DQÒ¸yó&nܸ¡:Æ'��Àˆ*^…Ç“àè~BTUUaÆ (((P‡ˆhÆ®\¹¢:¤”mwÿØ0MSiIróæMnDãòx<xâ‰'°|ùr.$"KÓ¡�0 ãõ{~¼k×®�Êúð¦ir�=Taa!6mÚ„êêj!TÇ!"š’p8¬Ãü?¢Ñè«wÿxl_V¥Ï&h°5"iÎf³¡ººëׯGnn®ê8DD“véÒ%:ݹ¹¹÷NÜþ·ÒX‡g#É222°zõj,Y².—Ku"¢GêêêR�^¿{ �Øoÿ[é¬ÁÖˆd1%%%(((À‰'ÐÑÑ¡CuMDô€‘‘-žÿBºÿkZ�ì�Ðt8,Z´¥¥¥8vìXÒý92 ƒ‹SÏÊH]]]Zz'¥<xÿׯ €k Îr¡¡!ŒŒŒ°¥KÓ’““ƒuëÖáüùó8}útÒl/™™‰µkתŽAD3ÐÑÑ¡:�DÂáðë÷ql @{‚Ã<àÚ5¥5YœذaJJJTlj >ñ@dm½½½º,rû™gžyà‘�„çç>,�(Ün7–,Y‚•+WZ~Ka�DÖÖÞ®üÞ� „xi¼¯�FO&4Í8zzzTG $âóù°aÃTWWÃ0ŒG¿@CVÍMD·žý¿xñ¢êc~0Þ �p8G›åA7oÞÄðð°ê”D Ã@uu56n܈üü|Õq¦Œ�"ëêèè@$Q�²²²^ï' �غuk7€Ë 4NP<x<¬X±Ë–-³Ôªzv�ˆ¬IJ‰ .¨Ž1æß6oÞ<în¿wÂŒ[!$ �Š§Ù³gcãÆxì±Ç8¸QÜ\¹rCCCªc��„ÿ8ÑÏÝù”Rþ$1q&våÊ-ž—¤äe·Û±`Á¬[·999ªã<‹"k:wîœêc† ÃøÞD?yçF¡¼�‡ÃZœ˜DÉ/##kÖ¬ÁâÅ‹át:UÇ×�YO¿NÝìÙºuë„Ï!Þ)�º»»èKH¤‡èîîVRHii)6mÚ„òòrí\ÝòÑ£={Vu„;„ÿ°Ÿ¿S�466šÐ`À¥K—’f'7²‡Ãºº:í¦X�YËÐÐ.]º¤:ƘkÃÃÃûö ÷L2 !~ß<fš&§H‰ÌÌL¬^½Z›i® ²–3gÎhs0™”rß®]»Fö=÷|†ñÏñ49œ U„ÚL °� ²Ž›7oê4vI!Dó£¾éžO˜­[·ž’R¾¿L“såÊŒŽŽûØ"QBŒM ¬]»VÙ´�§�ˆ¬ãôéÓ:=Åör}}ýéG}Ó·Bˆ‰OžÉ“Rêr‚¥¸¬¬,¬Y³K–,Iøi•ì�YÃÐÐNwÿB&ó}|˜¦ùÐE‰rþüymæRˆJJJ°qãFÌ™3‡wæDt“'Ojs÷/„è±ÛíßšÌ÷>P�äææþ<8<<ŒË—•ïNLt‡ÃáÀÂ… ±fÍdggÇýzì�鯷·W§C ¥|qË–-¡É|ïŸ0·÷ þnÌSMƒF»)Ý‘5kÖ`Ñ¢Eq}Z€"ý½óÎ;ª#Ü-F÷Lö›'ºÅ˜pïàDêééá1Á¤%!ÊÊʰiÓ&TTTÄe°f@¤·ŽŽôööªŽq·ÿ±sçÎó“ýæq �‡Ãñ Á4�pke%‘®jkkã2-À)�"}E"œ:uJuŒ»I�1•Œû ³eË–âbi†Ø +ÈÎÎÆÚµk±dÉ’˜M °@¤¯“'O"šÔT{¢|§¾¾þøT^ð°[Œg&fNž<©:ѤŒ=-PVV6ãœ�‘žzzzpáÂÕ1î!¥üêT_3a°mÛ¶WuظµÊ²««Ku ¢Iq:X´hÑŒ7b@¤Ÿh4Š£Gêö˜úþ}ª/š°�BH!Ä7f–)vNœ8h4ª:ѤeeeaõêÕX¸p!Ç”_Ï5�Dúyçw044¤:Æ=„S¾û>€h4Ú 2­D1 …´:f‘h2„˜3g6mÚ4åi�DzéêêÒ®õàõíÛ·ÿÛt^øÐO˜;wvÐbg@àÖ9˃ƒƒªcMÙt¦8@¤¡¡!;vLuŒH)ÿ`º¯}ä-†”²iºokRJ¼ýöۺͽMÚT¦X�é!‰à7Þ@$¢ECü)åþ†††Ó}ý# €úúú ÍVG}}}xï=-Ö&MËd§X�©'¥Ä‘#Gtì>G¥”¿?“7xd „RÊ)m.o§OŸÆ7TÇ š‘±i{È Ý��XIDATÕ«W#++ëŸg@¤ÞÉ“'µ<—F±oÇŽ3º9Ÿì*£¿Ð>“ ÅR4ÅÏ~ö3mN_"š‰œœ¬]»uuu÷L °� R«³³ï¾û®êã¹iÆÏôM&U�444„¥”ÿ}¦‹¥ÁÁAÝa š6!ÊËËñä“OÞ9[€O©síÚ5=zTuŒ‰ìÞºuk÷Lßd*Ÿ0Í�®Íô‚±tþüy\ºtIu ¢˜;[`õêÕHKKS‡(%õõõá7ÞеË|5 ýe,ÞhÒ@CCÃM�‹‹ÆÒ[o½¥ãâ ¢ÉÉÉAzzºêD)ghh‡ÖnÅÿ!Ä—wíÚ5‹÷šRÑ0Œç�h5ÚF">|£££ª£‘… ãßÿýß122¢:ÊD^Ù¶mÛÿ«7›R°mÛ¶ë�ü±ºx¬Ü¼yGŽáþ�DD4-ÃÃÃxíµ×póæMÕQ&2jšæ!D̺)¯2²Ûí &í‡Xºzõ*ÚÚÚTÇ ""‹ …BºþRþÕLû»ß” €Ï}îsWÄdB¬]¸p§OŸVƒˆˆ,âæÍ›8tèÖƒ?€ iiië7ÖsFn·û¯\Œq–˜8}ú4Ο?¯:inppÔ}ð‡”ò™§Ÿ~:æGN«�xú駇„1Î3ÇGw÷Œ‘$"¢$uýúu<xPç��)åÿjhhø~<Þ{Ú;deeµH)Ä2L¬H)ñÖ[o± "¢twwãõ×_×öQ¿»\‹D"_Œ×›O»�ؼysTñh¹ôÞ4M9r]]]ª£‘&Î;‡#GŽèºÉÏ=¤”Ÿæ™gâ¶ÛÝŒ7ƒß”Rþz,ÂăaX¼x1Š‹‹UG!""ELÓıcÇÐÑÑ¡:ʤH)÷544|.ž×˜ñfã6›í‹�®Æ K\Œu4=Јˆâlì1?« þ�ÎŒŒÄ­õ?&&Çßðb,Þ+ž***°`Áž²FD”"zzzðæ›oj¿Øï.¦iš›vìØñ“x_(f#a Øà#±z¿x)..ÆâÅ‹yÒQ“RâÌ™38s挥v‰Bü·íÛ·ÿa"®eÕÙl¶†h4Ú 'Vï]]]ÄO<ÁÓÖˆˆ’Ððð0Þzë-ôôô¨Ž2UofeeýI¢.Ó^x ø€–X¾g¼¸Ýn,[¶ 99Z×+DD4ÝÝÝ8zô¨ñ»_?€õõõ ÛÎ6¦€”RƒÁðÁX¾o¼†  ¼¼\u""šP(„¶¶6\º·§æâÉð±úúúï%ò¢1BHÃ0ê¡áaAã1Mmmm8|ø0Âá°ê8DD4 xùå—­:øÀW=ø1î�Œ ƒŸ–RþÏx¼w¼¸Ýn,]ºyyyª£Ñ$ܸqmmm¸víšê(3ñíÛ·"–ÇüN–-oºÿþãO=õT€÷Åãýã!‰ ³³¡Pyyy|J€ˆHSÑhgϞő#G04ó3rFJy À‡—/_Rqý¸=ßÚÚêìëëû ,TŒñx<X´h|>Ÿê(DDt›”8yò¤•žëŸÈ€”ò} 'UˆëŽ8~¿¿Lñ3�–I‹‹‹1þ|¸ÝnÕQˆˆRÚåË—qòäI ªŽ &€OÔ××Weˆ¸o‰×ÔÔô‹†a|1^p˜(v»•••xì±Ç8-@D”`×®]éS§ÐÛÛ«:JÌH)¿ÐÐаGuŽ„ì‰¾àq­xñx<¨®®Fqq1·&"Š³Ë—/ãìÙ³I5ðßöWõõõ¿§:§E€÷[¶lÙO222V�¨JÄõâ!ãÒ¥K¸xñ"œN'ÒÓÓYÅišèêêÂ[o½…sçÎ!R²6.žþGww÷ç_zé%-ö&NØÖÒÒ’F_°(Q׌§ŒŒ TTT ¸¸6[Bê("¢¤ …ÐÞÞŽ .$Ã⾉|WJùɆ†m6Iè-ì /¼P‰DþÀìD^7žœN'æÌ™ƒ9sæp± Ñ$I)qåÊtttàÊ•+0MSu¤xú7‡ÃñÑ-[¶hÕÒHx; .“R�‘èkÇ“>Ÿ%%%(,,dW€ˆhèîî¾³ïJ ø±ÛíþèÓO?­Ý†J&±›ššÞûÉ�—ŠëÇ›Ãá@AAòóó‘ŸŸ»=f‡.YÎàà .^¼ˆ®®.KoÜ3URÊùõ]»vi9¯¡l[0ü˜”òÃ#‰u$„@ff& PPP€¬¬,Õ‘ˆˆâ*¢··W¯^Å¥K—RjпˋÝÝÝ[µ=–Pé2öÛÇaÑ=¦ÃëõbÖ¬YÈÉÉANN<êHDD3"¥Äàÿmïîbã¸Ê0Ž?ïŒ7qÚ&Ô‰’pQ>R „ÒP‰VDBH¥€Zјý°Sa$D+à†”�E MDäìØ8ªBô¢å’*”\ >Ô‚h…@ÅBŽ“:Ø ›Ø3ûr;¤–Ýøcí3»ûÿ]Ùïìã±äóìì9g§¦4>>®ññqMLL(˲б‚1³o‹Å/…Øß9‚¯cK’ä^IÃZ§%‰y³qãFmÛ¶M===Ú¼y³â8VÇêêêRE¯˜KÇ1›È…z½®³gÏêÌ™3:w§§CGÊw÷û+•Êáƒ,Eð IµZí“fö=µùÛÍÖÕÕµà^fvÝyK)…Báºçhödǵœ/±ØõÂÊeYÖî³·_!Ms{7·é²,[ôU|½^ïÔÛú¯æ²¤R¹\þAè K•›ÿ†CCCûÜý1I¯>ê��/£f¶¯T*=:Èräæ~r©Tú‰»DRG¬ �´…gfffvµÚà/åèÀœ$IöJzB»ê��rËÝ“žžžÏìß¿¿%'@ä®�HÒÐÐÐwÿ©¤­¡³��0ÏeIËåòHè «‘Ë IÇsE?“ô–ÐY��˜õ(Šî*‹§CY­Ü̘¯¯¯ï…4Mo—ô‹ÐY��0³§iúÎvü¥ß˜S­V»vìØñˆ™Ý: � #MšÙ}¥RéÑÐAš)÷`N’$Ÿ•ô°:tà �Àú3³ßÆq|wooïßCgi¶Ü¾0_¹\>âî–t6t�@Û›q÷/ŽŽŽÞÑŽƒ¿ÔBw�æ ¾.ŽãG%í �ЖþàT*¿d-µ\$w·¡¡¡Iß;�š£îîõôôjÕµýËÑ’`ÎðððîF£ñ˜¤7…Î�hi¿”ÔW.—ÿ:Èzié IGÝÒÝÝ}\Ò'Bg�´œ13{ T*}?tõÖò`N’$÷˜Ù·Ý}[è,�€ÜsI#Q=P,_&„¶)�’4<<¼5˲¯›Y9t�@nýNÒçÊåòoB ©­ Àœ$I>èîGÌ졳��rã3ûB©Tz"t<hË I§NÚpþüù~IJº)t�@0’¾yéÒ¥G.‡“m[�æ ¾>ŽãC’îR m|�XµiIßIÓôÁƒN„“7m_�æ$Iò63û²»ïSýÞ�Ц%=EÑWŠÅâ‹¡ÃäUÇ „µZí]Q}ÍÝß: � ©¦ÝýdE_-•Jÿ &ï:®�Ì©Õjw˜Ù!Iw†Î�X•‹föÝ4Mêïï ¦Utl®l)œ$ÉÌì~Iï �°,/›YÇñáÞÞÞñÐaZMG€k%Ir›¤ÏKú¨¤®Àq��‹;-i°P(üèÀ—B‡iU€yŽ;¶½P(|JR¿¤[Bç�H’.›Ù“fv¤X,>:L; �,¢Z­víܹóC’î•´W|ê �„ð'w?™eÙI–ò5` FFF^›¦éÇ%Ý-iwè<�Ðæ^4³ÇÝý‡årù¹ÐaÚ`™†‡‡oi43³}î~»¸†�°Z©¤Óîþ”»?Ù××÷|è@€ÁkjµÚ3Ûkfïw÷÷Iº9t&�h‘ôs3{ºÑhüªR©ü't NCh’Ù9»Íl¤;ÝýÝ’6Ž�yÐp÷ç£(zVÒ¯Ó4}šõúáQ�ÖHµZíÚ¾}û;Ìì63Û%i—¤·Š%†�Ú×´¤—$ýSÒ_%ýÙÌž«×ë˜  óQ�Öщ'º³,»5˲[Íìí’Þ i«®|HQ,i‹$¹ûFI7H’™Ý¤ÿ¯@¸YüÍ�¬­TÒ”¤)w¯›Ù…ÙïëfvÁÝ'%MJ—4Öh4Æã8OÓô¥±±±W«ÕFÀìX“uøðáMÝÝÝÝ ‹ã8. [{lš¦[Ì,^蘙u7M s÷HÒkVøÊ¹o4³ +}ün˜-KMáî›ÌlÁkºBÝî¾àµ\ 3Û`f76ë|X{î~Ñݧ—úófvQW^E_Ï%]ýXÛÙAzæšçt÷ìšã×|Ýtõýö(Š.¤iZϲl*Žã •Jåêy�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������­å8õ Ú^X����IEND®B`‚���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon-pause.png���������������������������������������������������0000664�0000000�0000000�00000040222�15003540030�0022020�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ôxÔú��� pHYs��Ã��ÃÇo¨d���tEXtSoftware�www.inkscape.org›î<�� �IDATxœíÝwxTeÚÇñïC脎*`—¦kA,`o`GÅ.‚}í½wÔuײëZ]{ìÁ¨(*Å(UA@šÔ�!Üï“øFLBÊÌÜgf~ŸëâRæ|QäÜóœ""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""ñ¼DDªÊÌêMÍ ÿYô£Y±Ÿý}W¨^øó, P�¬�rµ…?_ä�« =ø ˜ ,!ä%ú÷%’H�D$ò̬&°5°=°Cá?‹~´j:d-äÿ‚™Àd`0%„°Â¡G¤B4�ˆH¤˜ÙÀž@g`b;ü6ÄÞ­§Š_)Àă/BÓ|“DþL€ˆ¸1³l`Wb;ú¢í]£gð50øøZ‡Ä“�I3« tvª¹FùÉÆï…~tî‘ £@DÊÌZÇþبå[Y3€wÁÀ˜B¾s¤9 �"wf¶p p<°›sN*ú¼ !¬sî‘4¤@Dâ¢ðþiÄvüÚéÇÏ2à5à…Â×Þ1’>4�ˆH¥™Y-bKû½€nüÿõõ’?€çB³½c$µi�‘ 3³€‹‰íø›8çd¢|`ð0,„`Î=’‚4�ˆH¹˜Y5à`ààRëºüt6x èBXã#©C€ˆ”ÉÌj;¶#ÐÎ9GJ·xx „0×;F¢O€ˆ”ÈÌêW[9çHù­#vÒàƒ!„ÉÞ1]�DäO ßñ÷îZúÖH0¸=„ðwŒD�þ8Æ"p±‡ìHz( vOÛBßzÇHth�Ììb;þŽÞ-’0Ä Ü¢K4�ˆd43Ûø°¯w‹$M.ð_à!„åÞ1âG€H2³–ÀÃÀ©Þ-âæw /ðDa½wŒ$Ÿ�‘ Rxœÿ<à_@C牆éÀe!„Þ!’\�D2„™í< ìåÝ"‘4¸8„ð«wˆ$G¦>‡[$c˜YC3{‡vþRº£Éfv¥™é.@+�"iÌÌz ëù¥b¾Î !üè"‰£�‘4Tø®¿0íü¥âö¾3³ûͬ¦wŒ$†V�DÒŒ™ <‡nß+ñ1èB˜â"ñ¥�‘4afÕÍìÀhç/ñ³0ÞÌ.ô‘øÒ €H0³Í€W€Ã¼[$­½œBøÝ;DªN€HŠ3³‰Ýâµ…sŠd†¹Àé!„ϼC¤jt@$…™ÙyÄ–üµó—dÙ if7x‡HÕh@$^§}/ ¿„ÅÓëÄ ¬ö‘ŠÓ� ’b̬>±¿xôn¾N!Ìô‘ŠÑ� ’BÌl+à=`7ï‘b–=BŸz‡Hùé�‘af{ãÑÎ_¢§ ð™á"å§�‘`f§ÿj{·DAAA¿-XÀìÙsb?æÌféÒe¬ZµŠU«rX¹jeìŸ+VüéûV®Z…™•øš Ô'þ•X«vmêgg“]ììl4h@ýúõiѼ9-Z4§yóælѲ%-[¶ ;;;á¿ßbÀ­!„û¼CdÓ4�ˆDœ™ÝÜEÿÿºhÑ"&Lü† &2ñ›oøiê4òòò¼³�Øb‹–´Ýi'vÜqGڵ݉Ú³u›6ÞYÞú{²`wˆ”.cÿB‰:3«ü¸Ä»%ÙV¯^Íç_Œå“O?ãó/Æ2wî\ï¤ iÞ¼9{ïÕ™½÷êÌ^;Óºu+ï$¯g‡Ö{‡HÉ4�ˆDPáX^�NõnI–¹sç2âƒùäÓÏ7~ë×§Ï~c»í¶¥ûGpćӮíNÞ9É4èBÈõ‘¿Ò� 1fVx 8Ü»%Ñ-^̰÷‡3dè0¾ûîûRϧ“6mZÓ½[7N:±G¦*BÈñ‘?Ó� !…×øöónI”uëÖ1|Ä zã-¾7Ž‚‚Ì<LB`Ï=;qJÏ“8âðè];­Ïïü82„°Æ;DþŸ�‘ˆ0³†ÀûÀ>Þ-‰0ëçŸy}À Þzû–/_î) ÔçøãŽåì³Î¢M›ÖÞ9‰ò±•€µÞ!£@$̬10ØÓ»%žÌŒO>ýŒgÿ÷_~õuF,ñWEµjÕ8äàƒèsv/:wN«? E†LJÖy‡ˆ�w…ïü?&öÜõ´ŸŸÏaïÓ¿ÿ³ü4uªwNJêØ¡}úœÍÑGv'++Ë;'žÞN !ä{‡d: �"Ž Oøtñn‰‡õë×3ð7éßÿYæÎ›ç“Z·nÅEœOã£FÞ9ñòtáBïˆL§@ĉ™Õ‡z·TUAAƒ‡ å?ÿ}Œ_ýÕ;'-m±EK.¼à|NéyÕ«W÷Ή‡;Bw{Gd2 �" oòó&p¼wKU9ЇþýS§MóNÉmÚ´æê+¯àÈîÝ!¥ÿ 7bþŸwH¦Jé?="©ÊÌ®ò™3gqWß{øbì—Þ)i玹é†ëRýdÁ Ä.üÐ;$i�I23;xÚ»£²Ö®Í¥ÿ³ÏòT¿þ‘¹&;øàƒ¸ó¶[Ùb‹–Þ)•µ Ø+„0Ý;$Óh�I"3ëFì¸JÄ}øî¹ï~.\è"ÅÔ­[—K/ù;çöéªW Lö !¬ôÉ$�$m^NWhPø£¤ç´6¢ì?÷ù@ñ¿„Ö¹@°¾ðsy•ù‹ÊÌ:Ÿ¶¥”¥K—rûw3|ÄÞ)R†öíÛñÏûî¥]»¶Þ)•18NOL �’Ì,�Û;;þØ¡ðc $Ó`)±åË¥…?fÓiÀøÂÒ¢/6³¶Ä.÷K¹Û¼ ñ·ßy7K—.Ýô‹»êÕ«sÁùçréŧfÍšÞ9Õ7„p»wD¦Ð� ‘dfÕÝ€®Àþ…ÿlæU1L&5€#€”ºÙûªU«¸íŽ»2t˜wŠTB»¶;ñï‡dûí·óN©ˆàˆÂGÞ!™@€D†™µºÛYv¡ä%|I‚I“'sù•W3gŽ®éOeµk׿¦¯çôSOI¥K»†x‡¤»”ù!é§ð™÷‡»þ #žu/¿ò*÷Ýÿ/áŸF?ìPþuÿ}dg§ÌL=8\· N, �’TfV‡ØÎþDàh’ì^J±fÍn¸éÞ>Â;E`Ûm¶á‰Çþ›J‡n!ôõŽHg�$á OàÛèœ4ô-’ÍŸÿ^| ?þø“wŠ$Pݺuùç?î¥{·#¼SÊc=±K'x‡¤+ �’0fÖ 8ØŽkß)ÍÄo¾áâK/gÉ’ß½S$ B\}åüý¢ ¼SÊc2Ð)„ë’Ž4�H\Þãþ0à"à %ïJ’)Þy÷=n¾õvïÏ@=O<¾wß™ z0„pwD:Ò� qafuÞÄîo¿½o”dzÿ{Žûÿõ fæ"NºvÙ—'”:uêx§”¥�8 „0Æ;$Ýh�*1³Í€K ¤Òuú­_ÿgxàÁ‡½3$vÙ¹#ÿ{æi5Šôù¸Ó€¿éP@|i�J1³À À…@¤ß>Èÿ+((àλúòêë¼S$Bڵ݉çž}†fÍšz§”å®ÂÞéD€Tˆ™5'¶ã¿íøSJAA7Üt o¿ó®wŠDÐöÛoÇ+/¾@Ó¦M¼SJ³ŽØ*ÀTït¡@ÊÅÌê×Wus¤‚ÌŒ[n»ƒƒÞðN‘k»ÓN¼üâsQ>088„ Wâ šw€D›™U7³¿{ÈÍ­hçŸrÌŒ»úÞ«¿lÒOS§rö9ç±rå*ï”Òœé‘.´ ¥2³C€GvÞ-Ry÷Ýÿ/þ÷ÜóÞ’BöܳÏ?ÛŸZµjy§”d>°caµwHªÓ €ü…™µ4³ÑÎ?¥õæÚùK…7žË¯¼šüüHÞŠ bç!Ii�?˜Y53»‚ØclÏB+D)møˆxà!]ê'•óñÈQÜÿϼ3Js­™µöŽHu��3۞ظþÔwΑ*úþ‡I\wÃx§H {î…y}À@Ôî÷ŽHuz‡—á̬:±³ûo"yÀO*fîܹœÐóT–.]ê"i F¼üâsì±ûîÞ)3 sa¼wHªÒ�ÁÌl[à`oï‰ÜÜ\zžzºžê'qµY³f¼ýæ@Z´há²±!„nÞ©J‡�2”™õ& Z¹óî¾ÚùKÜ-^²„K.¿’ 6x§lì3;Ð;"Ui�È0f–mf¯ÈÞíC*îµ×òÆ›o{gHšúî»ïùÏ#zg”¤¯w@ªÒ!€ bfí€7Ñ¥}iç‡I“8å´3õX_I¨jÕªñlÿ§Ø¯kWï”u! ÷ŽH5�2„™ ô²½[$¾Ö®Íå¸'2ë矽S$4kÖ”÷‡¼GãÆ½SŠû<„¹©$êt Í^Ûÿ/à5´óOK<ôvþ’4K–üÎwEnÕ½‹™uñŽH5�Ò˜™e[ò¿Î»Eãó/ÆòÒ˯zgH†öþp†½¹wý=WA:¦ ï’õð7ïIŒ•+Wqä1Dz`ÁBïÉ@7fİÁ4i™Ç�C?z‡¤ ­�¤!3ë�ŒA;ÿ´öÐÃÿÖÎ_Ü,[¶Œ>ðwFqÕ€k¼#R‰€4cf{Ÿ�­¼[$q&MžÌëygH†{ëíwøò«¯¼3Š;Ó̶ðŽH�Òˆ™õ�FM½[$qòóó¹õ¶;£ú¤6É fÆ]}ïÒ ‚j—{G¤ŠêÞ…—ù½„þ›þ!??Ÿ¥Ë–±bù V¬\ÁŠå+X¾b+V¬ oýú?¾.„@ƒúõÿøy“&MhÚ´ M›4eóÍ7£N:^¿…½úÚëLš<Ù;C€éÓgðÒ+¯Òçì^Þ)E.0³;C¹Þ!Q§“�Ó€™ <d9§¸Xµj³fýÌÌY³˜5ëg~þåf̜ɜ9¿²¾ØŽ¾²š5kÊÖmÚ°õÖmغMÚ¶mKûöíØ|³ÍâP_1+W®âàCgùŠIß¶Hi5lÈG§QÆÞ)EÎ!èò˜MÐ�â̬7ð,t8gñ’%Œ7žqã'0nÜx¦MŸîòØÛÍš5£}ûvì¾ÛntÞ³»ì²3µj%öŠ>üžê÷tB·!R}zŸÍ-7ÝàQddáÓ� —ý_&Íßùçåå1æó/øè㑌7žŸùÅ;©D5kÖd—;ҥ˾ì¿ß~ìܱÕªÅo.[´x1‡vk×jeS¢§F|ðþZµŠÄùÇìB˜ée�R”™ ¼ÔðnI„ÜÜ\>ýl #F|ÀÇ£F“““ãTaMš4a¿.ûrøá‡qÀþûQ»ví*½ÞíwÜÅ«¯ˆSHüÐãxþuÿ}ÞEî !Üêe�R™ ª¶G‰3cì—_1`à Fþ„5kÖx'ÅMݺu9ø 9²{7:ð�jÔ¨ØÜ6gίÞý¨(m-òYYY 6˜m¶ÞÚ;`Ð&„ ËeJ¡ Å»ÉOÚ<ÊwõêÕ 2”_z…iÓ§{ç$\à èÞ½gœ~íÚîT®ï¹ñæ[ô¨_I Ç{ =ðOïŒ"G‡†zGD•€bf[_[y·ÄÃôé3xù•WyûÝ÷ÒêÝ~Etî¼'çõéÍAH%ÿï8gίÖíH]÷/)!b«�ƒB'{GD•€afõO]½[ªjÆŒ™<òèc ñfæ Ûm·-ç{=Ž;–êÕÿ|+½û—Tsú©§p÷]wxg�ä�›‡Öz‡D‘€`fÕ€w£½[ªböì9ü÷±Ç<d¨Ëe{©`«-·äï]À =ާFz÷/)©víÚ|6úc7nìp\á=ïˆ(Ò�ÌìàïŽÊúí·ü÷ÑÇxëwµ#+§-·Ø‚‹.¼€ 'òλú»KRÏ•W\Æ¥ÿÝ;àùBïˆ(Ò�qfÖ@ þ·Ú°aÏ=ÿ"ÿ}ìqÖ®Õ œH&iÖ¬)ŸŽú˜š5kz§ü´!èšdÌÝãR‘™už#wþã'Lä¸'ñÏÔÎ_$-Yò;#>øÐ;bGÛÏ;"Š4�D”™Õ#öοžwKE¬X¹’{îý§ŸÙ‹©Ó¦y爈£Ao¼éP¤‡w@¥Ü;ËLafϽ½;*bÔèO¸á¦[Xºt©wŠˆD@?NëÖî·žBhã5Zˆ 3;‹ÚùçååqϽÿà‚‹.ÖÎ_Dþ`f z3«�­Íl{Ñ�1f¶5ð˜wGyÍœ5‹O>•ç_|I×ô‹È_¼ýλQ¹ì÷�Ñ�!…×û?4ðn)Ao¼Éñ'ôäÇòN‘ˆZ°`!ß~÷wh�ø �Ñr%p wĦäççÓ÷Þû¸é–Ût†¿ˆlÒûïðN€ø»5Ùt`D˜ÙŽÀwDü «W¯æŠ«®aô'Ÿz§ˆHŠhÑ¢9ŸYêó.’hÛÂÏÞQ¡€0³�<IÄwþóçÿÆ)§©¿ˆTÈ‚ ùö[ˆ �Ñpp°wDY¾ÿa'|*?Mê"")hô§‘xã   �Î̬ ð€wGY&LœÈYg÷aÑâÅÞ)"’¢>ûlŒwÀžÞQ¢Àß}ÀæÞ¥™0q"çœw!«W¯öN‘6iò”(Ü'd'3«å�™Yà\ïŽÒŒŸ ¿ˆÄGAA_ŒýÒ;£:ÐÖ;"*4�øz„ØÈÈ7n<}Î=_;‰› ��»xD…�'fvpˆwGI¾ùæ[Î9ÿB]ã/"qõÍ·ßz'�tôˆ �Ì,‹Ø±ÿÈ™;o¿ô2íüE$îf̘Ɋ•+½3´PH€3€öÞËÉÉá‹.aÉ’ß½SD$ ™?|ÿƒwÆÎÞQ¡ É̬p‡wÇÆ6lØÀ%—]ÁÔiÓ¼SD$Mô? °¥™5òŽˆ �ÉרÖ;bcwÜÕ—Ï¿ë!"i."kå‘<=]û¿Á»cc/½ü òÎ(·³{ÉÍ7Fî_£›3{õfÜø ÞqõÖéоwF$ÌŸ?Ÿƒ=Â;#n¦OŸá�°à~,›€ä:…ˆ½ûŸ6}:ÿ|à!ïŒ ©ª‘••åþX‰»êYYúo\(++½þšþuî\rss©]ÛõÑ'Z@‡�’íZï€âÖ­[ÇUW_Gnn®wŠˆdˆüü|fÍr ß–ÞQ  I̬°›wGqwßsŸNú‘¤›>s¦w‚V�Ð�L—y7âƒS긿ˆ¤ysçy'lå�’À̶ºywY¼d 7ßr›w†ˆd¨ßüæ �4�$ËÅDèßõý÷ÿ+ wã‘ õÛo ¼šzDAdvJéÊÌj½½;Š|ýõ8Þ2Ô;CD2Øüùî+�ÙÞQ  ñŽšxG@ìnwÜÝ3óN‘ ¶hñbï„:…÷eÉh�¯w@‘ç_x)*7á‘ ¶jÕ* ¼3êyxÓ�@f¶%p¨wÀÂ… yôñ'¼3DDÈÏÏ''gµwF}ï�o�ë ËLO<õ4«W»ÿ'"ÀÊ•+¼2þ<� �‰ÕÓ;�bïþ½ñ¦w†ˆÈV®\å À; ]™Ù6ÀÞ�ýú?C^^žw†ˆÈr׹߂\ç�x¤±ž€ûSZ/YÂÀAz÷/"ÑRPà~5’{€7 �‰ÓÃ;�àé§ŸÑÃ~D$ròó7¸'xxÓ��f¶ÐÙ»céÒ¥¼>p w†ˆÈ_äç»_¨À; MNþݾóî{¬]«wÿ"=¸€û„7÷Tš:Ò;�` Îü‘ˆªU«–w‚�ï€tcfÕˆ­�¸úæ›o™1Ãý™Û""%ª]Û}�Ð!�ï€4Ôhæ1pÐÞ ""¥ª[§®w‚V�¼ÒÐ~ÞkÖ¬aèûý3DDJUË@€w@Úß;`Øû#X³fw†ˆH©²³ÝoÄ—ñIj�ˆ?÷€á#Fx'ˆˆ”ªzõê4lÐÀ;#㎢ ŽÌ¬ ÐÒ³aÕªU|þÅXÏ‘25kÖ”Üo”šãàM@|¹ßûÔèOX¿~½w†ˆH©š5u?O:7„ «�¼ÒÌîÞŸ|ò©w‚ˆH™š6mâñïþA@¼íé¹q3㋱_z&ˆˆlÒ–[láñÇÿA@¼íì¹ñŸ¦Ncñ’%ž ""›Ô¦Mkï �h�ˆ3k„ó €cÇêä?‰¾6mÚx'è��â©wÀ„‰ßx'ˆˆlR›Öî+��Ð�O;y|óÍ·Þ ""eÊÊÊ¢U«­¼3t� �ñä:�Ì;—E‹{&ˆˆlÒ6ÛlMíÚµ½3ššYïo�âgkÏOš<Åsó""åÒ±Cï€}Ifvˆwˆ' �ñÓÊsãÓ¦M÷ܼˆH¹thï~ºT‘mÍìE3s¿1 �ñãzVËÔiÓ<7/"R.Y(€³ˆ­œà“l�âÀ̪ã| àŒ™3=7/"²IYYY´Î @q-7Íì53ÛÜ;&Y4�ÄG º×ÆÍŒyóæ{m^D¤\:´oG½zõ¼3Êr*0ÙÌÎðI �ñáúd‹E‹‘››ë™ "²I{íÕÙ;¡<š/›Ùé~n€€øpýC2wÞ<ÏÍ‹ˆ”Kç=]—RQ'ߘÙ~Þ!‰¢ >šzn|ñbÝÿ_D¢-++‹N{¸?0µ¢Z£Ììn3Ëòމ7 �ñẰté2ÏÍ‹ˆlÒn»þúõë{gTFp0ÄÌ{ÇÄ“€øhè¹ñß—þî¹y‘M:ô”¿çN7`œ™¹>õ5ž4�ÄGϯZ¥çZˆH´|ðÎq±ð…™ä�âÃu�X§+�D$¶Ùzk¶ÝfïŒxÉšÙ=f¼cªB@|¸�¹ëÖyn^D¤L‡šòËÿ À-À«fæþd£ÊÒ�®@^^žçæEDÊtì±G{'$Ê©ÀGfæz/˜ÊÒ�®@µÒ«P"’ÆvØa{Úîäú´ôDë|efm½C*J@|¸�YÕÝîB,"R¦Çç Ûc̬«wHEh�ˆß šþ3ŠHôT«Vc>Ê;#Yš˜Ù1Þ!å¥=G|¸�uêÖñܼˆH©¦Ï˜áLuˆ=U0%&¤ >\€½»–ˆ¤¹‚‚Î9ïBn¼ùæÏÿÍ;'Yj�/šÙ%Þ!›¢ ’̬™™if¯á|'ÀìzÙž›)•™ñÆ›osС‡såÕ×òÝ÷?x'%C5à13»Õ;¤,:{¬̬p2±§Du&vhw7òN)S~~>C†cÈÐaì´ãŽœtbŽ=æš6Më'îö5³ìÂÞ!%Ñ À&˜Y 3»ÌÌÆ�³}ˆÈΠùæ›{'ˆˆ”ÛÔiÓ¸÷ÿ¤ËþrÑÅ—2rÔhòóó½³å3»Ó;¢$Z(Aáí.#ö�ˆÈììKÒ¢Esï‘ Û°a}<’>Éæ›oΉ'OÏO¤uëVÞiñv‡™­ !üÓ;¤8­�cfõ OÜøEÄwþ�Í›k�‘Ô¶hÑ"ž|êi9¼g÷9—1c>÷NŠ·ûÍìrïˆâ4��fÖÒÌæ)uÛª† Ш‘Î‘Ôgf|þÅXzŸ{>Çö8‘aïO§Ãÿ1³ó½#Šdô�`fÍ wü3«p¾œ¯*ÒèI[""�L™ò#—_y5‡w?Š¥Ã €§ÌìLïÈÐÀÌ67³€YÄvü)'m·ÝÚ;AD$!fÏžÃ-·ÝA÷£ŽåÃ>öΩªjÀsfv|B2†™Õ5³»‰íø¯ê:'ÅÍÛoï "’P³~þ™¿_r§qVªßO :ðŠ™íá‘€™3; ˜ ÜÔsNŠ»Ú{'ˆˆ$Ÿñ8éäS¹òêkY´x±wNeÕÞ3³-½Ò~�0³Ý€O€W­œs¦c‡TÓCD$C˜C†ãˆîGó‹/SPPàT[�ƒÍÌåMiÚî1̬‰™= ŒöóîI´ììlÚ´ní!"’T«V­¢ï½÷qF¯ÞÌúùgïœÊØ xÉÌ’¾?NËÀÌN�&瓦¿Ç’ìµWgïãÆçèc{ðT¿§Sq5 p_²7šV;ÇÂËúo-¼{’­Ë¾ûx'ˆˆ¸ÉËËãÁ‡ÿC¯Þç²páBºÁÌú$sƒi3�˜YO`p’w‹—}öÞKçˆHÆûò«¯è~ô± {¸wJE=if»'kc)¿·0³-O#¸�� �IDAT¦fö.0hæÝã©Q£F´k×Ö;CDÄÝÊ•«¸âªk¸óî{ÈËËóÎ)¯ZÀkf–”g¼§ô�`f�ßÇz·DE—}t@DbW ¼üÊ«œ~ÖÙ©t¹àŽÀ3ÉØPJ�f–UøxÅ·k(£hß}ööN‰”o¿ýŽãOèÉ÷?LòN)¯SÌììDo$å�3kŒî žÔ—l:íAíÚµ½3DD"eÑ¢Eœ~f/† æR^›YBé¦Ô�`fß×õWVíÚµ9ð€ý½3DD"'77—«®¹ŽGþû˜wJyÔ˜YÂÞÑ¥Ì�`f—#€¦Þ-QwÌÑGy'ˆˆD’™ñèãOpÇw§ÂývLÔ‹G~�0³êfö(ð(±(È&tà4hPß;CD$²^yíu.½üÊT¸BàâÂÕ︋ô�`fÍ€K½[RIÍš59â°Ã¼3DD"íƒ?âÜó/dõêÕÞ)e Äî÷C‘�̬08Ð9%%sÌÑÞ ""‘7ö˯8ç¼ Y³fwJYv$ö$Û¸Šä�PøŒäO�=ݦ’öÞ«3Í›7÷Ή¼ 'ÒçÜ X»v­wJY®/|ºmÜDn�(<Ö1 ØÌ»%•U«V#»wóÎI &NäÒ˯dýúõÞ)¥©<efq»ü=R@áSü†:ƒmV¬\ÉŠ•+Ë<vÕ¦u«$‰ˆ¤¶O>ýŒ+®º†üü|ï”Òt.‹×‹Eæ¬ú§ õ'ÃoîSPPÀüùó™>c&³fÍbþo¿1þo,X°ÅK““³šœœœ¿|_VVÙõêQ·^]Bäää°rå*‡ßˆHêúàøþ†›xðBðÎ)É=föVaNU_(€™AìÞÇ‘Z‘H´üü|fÌœÉ÷?Lâ‡ïà‡I“™1sk׿V굊VDD¤òÞ<„­ZmÅUW\îR’zÀ=@¯ª¾û�P¸ìÿ<°óÏÏÏgò”ùò«¯øê«¯7~BÔÏ<ÉHO<Ù­Û´¡ÇñÇy§”ä 3ûwᛪ¼ˆë�`fÝ×¼;iùŠ|úég|<rŸ£ey‘`fÜ|ëíl¹Åtî¼§wÎÆªÿªt÷¯™¼ ÔôjH”å+Vðþû#2tã'Lˆò %""RŠõë×sñ¥—óÆ ×ÙºMïœjfÝBÃ+û.€™í¼ÔñØ~"lذ>É[ï¼ËgŸ‰ò¥$""RNËW¬àâK.çÍA¨S'rOZý—™}B¨Ô»Ì¤�f¶0ÈNö¶aÑ¢E¼>p¯Ä¢E‹¼sDD$ΦMŸÎw÷åŸÿ¸×;ec;;ð¹Ê|sR�3k š$s»‰0sÖ,žx²C‡½Ï† ¼sDD$Þ|ëmöìÔ‰“Nìá²±¾föz¡Â·1LÚ�Px÷¢—’µÍD˜1c&ýú?Ã{ƒ‡èؾˆH¹ã®»éоíÚµõN)nK ðDE¿1™—Þý82‰Û‹«E‹sÓÍ·rä1Çñö;ïjç/"’aÖ­[Ç•W_[©{µ$Ø5•¹EpR�3;¸8ÛŠ·ÜÜ\úõ†Ã»Å 7ߢ  À;IDDœÌœ5‹‡ÿýïŒm ô¬è7%|�0³C€§½Døô³1t;òxðáo¿+""™ç…—^fÜø Þ»¾¢ßÐÀÌ6'vÜ?¥nô³båJn½ýNÎ=ÿBæÎ›ç#""RPPÀu7ܵ;¹îffº1PÂ�3«¼´HÔ6áƒ?â°#Žäõ13¹sçòPôÜP‘/Nä À5Àá |ý¸Z»6—[o¿“‹/½œ¥K—z爈HĽôò«|óÍ·ÞÅbfÊûÅ � îIÄk'”)?rü 'ñú€Þ)""’" ¸ûžû¢vrx¹a÷À̲WH‘{ü2”SN?ƒ™³fy§ˆˆHŠùaÒ$Þxó-ïŒâN2³FåùÂD¬�<였׫üü|î½ï~®ºæº(^Ó)"")â¡ÿ'JOz­œRž/Œë�`fG»/q¤­X¹’ÞçœÇs/¼è""")î÷ß—òßGóÎ(îœò|QÜ�3«C%nE˜l¿ý¶€SO?“±_~å"""iâ¥W^å—Ù³½3Št6³Ž›ú¢x®�ÜNìnD‘5}ú N>ít¦OŸá"""i$??ŸÇÒ;£¸M®Äe�0³‰]öY&NääÓÎà·ßx§ˆˆH<dh”N(?ËÌÊ<¿Ê@á žjTõµeܸñô9÷V­ŠÌI""’fòóóyô±È oSÖÄcà|`ß8¼NB|õõלsþ…Q»e£ˆˆ¤¡aïÒaæËúd•�3k�ô­Êk$Ò×_ã¼ .bíÚµÞ)""’ xâ©~ÞEº›Y©«óU]¸Ø¬Š¯‘?MÊE—\ªküED$©†½?<*ç›5(í“•� ŸôwEe¿?‘fÏžCïs΋ÒDD$CäççóÊk¯{g9®´OTeà ~¾?!-^L¯Þç°dÉïÞ)""’¡^0ÜÜH¬@kf¡¤OTj�0³m€óª””�ëÖ­ãâK/gÞüùÞ)""’Á–/_Î{ƒ‡xg�´v+é•]¸ˆ=ìÇ̸ñæ[ùöÛï¼SDDDxáÅ—½Š”x Â@áMÊõ dzò©§<d¨w†ˆˆ�S§Mcòä)Þ�G•ôÁʬ�\ ”x<ÁË—_}Å#ÑzƒˆˆïFã0À®fÖpãVh�0³-€Sã–K–üÎÕ×\O~~¾wŠˆˆÈŸ¼7xpöOYÀ^°¢+�—¡cÿ\sÝõ,Z¼Ø;EDDä/–,ù±c¿ôÎ�èºñÊ=�˜Y6pA\sªèɧžæó/Æzgˆˆˆ”êíwßóN�ØoãTdà\ qüZªæ§©Syì‰H=zQDDä/>9Š 6xgìµñÓË5�˜YpYB’*!??Ÿoº•õë×{§ˆˆˆ”)''‡ 'zgÔv/þò®� l÷œJzâ©~Lš<Ù;CDD¤\Fò©wlt ¼ÀÙ ©”Ù³çðäSO{gˆˆˆ”[D€?] °ÉÀÌšG',§‚î¹ïäååygˆˆˆ”Ûôé3¢p›úöÅQž€ÓZ‰i©˜‘#G1jô'Þ"""öÙgc¼v(~"`y€Þ‰k)¿ 6pÏ?î÷Ω”ï¾ûÞ;¡:°cÑ/Ê�̬#5èeÀ 7˜3çWï ‘Jùîû¼ Øa€M­�œ“àrY·nO>ÕÏ;CDD¤ÒfÌœÉêÕ«½36=�˜Y "Oý{åÕ×X°`¡w†ˆˆH¥0åÇŸ¼3:ý¤¬€NÀ‰o)[^^ýŸùŸw†ˆˆH•Mžâþxàr86 !›ôîà!,^²Ä;CDD¤ÊfÏžã°}á ô€^xÉ;ADD$.~ýÕýdöš@S(e�0³6À.É,*ÉgcÆðÓÔ©Þ"""q‘«ÙZBé+�Ç%1¤T¯ä ""7sçÍ£  À;£ÌÀýÖ¿Ë—/gä¨ÑÞ"""q“——ÇÂ…‹¼3Z@ €™e$=g#ƒ‡ Õã~ED$í,Z¼Ø;¡Ô€.ÄNpõö;ïz'ˆˆˆÄÝòå˽J�LnÇ_-Z´ˆ&MöΉ»eË–y'”:�¸/ÿ53óΉ»eþ+�Í`£ÀÌê{¸ä3Rü‘4€:ð×�÷ãÿ¹¹¹Œ;Ö3ADD$aV,_áPþ:�trù“±_~ÅÚµ¹Þ""" ‘——çPâ�°£CÈŸŒÖò¿ˆˆ¤1 �¥ýÉ§Þ """ ³.¢ÀN!˜;oóæÏ÷LI¨Üä®�̬ …Oò2aÂDÏÍ‹ˆˆ$\€¿¬�´v ùÃĉßx'ˆˆˆ$Ô† ¼þ¼�4t ù÷ßï ""’P5kºßmüy�hà@~~>3fÌôLI¸5jx'¬ �3gÍbݺuž """ €µðç ¾S�S§NóܼˆˆHRD`à/€ë ÀÏ¿üâ¹y‘¤ˆÀ À_d;…�0gίž›IŠ:uj{'üe ºS�³çÌñܼˆˆHR4jÔÈ;á/ÀÆwLª xn^DD$)û�9à6�˜K–üîµy‘¤iܸ±wÂ"ˆÈ�°|ùò(ÜQDD$á"0�̃ˆ �Ë–/÷Ú´ˆˆHREàÀˆÈ�°zõj¯M‹ˆˆ$U‹ͽ¢³ @DD2A½zõ¢p`>Dd�X³f­×¦EDD’¦uëVÞ ¥À̼6-""’4mZ·öNȧ„s�‚O‹ã†EDD’¨u+÷€…!„|øó�ïA#€ˆˆ¤¿m¶ÙÚ¹€_Š~R|�(H~GLMÿ'#‰ˆˆ$\ÇŽ½&ý$@Ýzu½6-""’uêÔfǶ÷ΈØ�PW€ˆˆ¤·öíÚ“••å­ ~ýú^›IŠ;vðN�˜Rô“H �›5kFЉ€""’Ævß}7ï„¥!„ùE¿ˆÄ�P³fM6hàµy‘„ÊÊÊ¢k—.Þ“‹ÿ"�Àæ›oî¹y‘„Ù}÷ÝhÐÀýpw©À†$‡üI«V[yn^DD$a<`ÿ¢ø�“ä?iÓ¦çæEDDæÐCöN�ø¢ø/Š�«’ò'mÚ¸ßYDD$îÚµkËvÛnë±’2V�V&·åÏvÜaÏÍ‹ˆˆ$ÄÑGé�ðyÑ3�ŠDf�h×v'ªUs{ ¡ˆˆHÜ…8êÈîÞ�ŸoüÈ �ÙÙÙ:PDDÒÊn»îÊV[néQ��:vˆÄ]’DDDâ¢gϽ�ò€¯6þ`ñà×äµ”lÝw÷N‰‹zõêqT÷nÞ�Bk7þà@a ð{R“6²g§=<7/""7ÇstTv7¦¤n|ÖÝ´$„”jÇwˆÂ’DDDªì”“{z'ù°¤Fj�ÈÊÊbŸ½÷öL©²ÝwÛÚ{g@ìü¾OJúD¤�€ý÷ëê ""R%½Ï>Ë;¡È!„¼’>±ñ�0> 1e:`ÿý¼DDD*­eËqøaÞE—ö‰€/€õ‰m)[‹-t9 ˆˆ¤¬3Ï8¬¬,ï €uÀ»¥}òO@!˜è¢M9æèHÜ6QDD¤BêÕ«¥“ÿ†‡V”öÉ’î½[âÉÉtôQGê¶À""’rÎ8íT5lèQd@YŸ,i/ûi‚BÊ­yóæìÙ©“w†ˆˆH¹ÕªU‹Þ½{ygYNËÿPò�0çó�@‡DD$µœròIl¾ÙfÞE^!¬)ë þ2�„V;ÐU·nGP£F ï ‘MªY³&çŸ{®wFqOoê J;Ðþ~œC*¬Qƺ'€ˆˆ¤„3N?–-[xgù4„0eS_TÚ�0,Î1•rrÏ“¼DDDÊT·n].ºà|ïŒâú•ç‹J�B?�³ãšS x�­ZµòÎ)Õyçô¡iÓ&ÞE–�o–ç ˺֮\/HÕªU£×™§{gˆˆˆ”¨I“&œ{NoïŒâž!¬+Ï–5�¼§˜*9éĨW¯žw†ˆˆÈ_\{õ•QÚGm�ž,ï—5�|IÔ¯_Ÿ^gá!""ò';tà¤OðÎ(îÕ¬ò~q©@ÁˆÀa�€sÏéCýúõ½3DDD€Ø!ê»î¼=Jw­-�î¯È7lªü¥Ê·ÄO£† ésvdî®$""îÔ“{ò·]vöÎ(î­Âù†êe}2„ð­™}ü­JYqЧw/^~õ5–.]ê"ΦϜÉ;ï¾ç¿/ùÝ;!îFŽÍ´éÓ½3"aÙ²eÞ ²‘­¶Ü’®¿Ö;£8î«è7…M¾ªÙUÀÕ)Š·× äÖÛïôΑ Bà¹gûӵ˾Þ)Å !]Ño*ÏÁ‹W€¼Š÷Ä_Ï“N¤}ûvÞ""’¡z÷:+j;€{+óM›�B‹€·*óâñ–••Åí·ÞB›\¸‰«Ývە믻Æ;cc‡ÆVæË{úâ•yñDè´ÇîœrrOï É M›6áÑÿ<µ‡Ôpce¿¹Üo¥Íì{ §<æääpä1Ç1þoÞ)""’æ²²²xþϰÏÞ{y§lìåÂY•ýæŠ\Àøhe7oÙÙÙÜ×÷n ‘„»í–›¢¸óÏn©Ê Td�x˜W•ÅS×®]8픓½3DD$uæœyF$ŸIóHaNU^ Bo¡ÍìJàßUÙ`<­[·Ž“N9üÉ;EDDÒÌ~]»òÌÓO’••å²±ÅÀ!„Uy‘Š�u_€Íª²ÑxúeölŽëq«W¯öN‘4±ËÎyé…ç¢ô Ÿâ. !<^Õ©ÐMŒCk€ÿTu£ñ´u›6ÜuÇmÞ""’&¶Ûn[žíß/ª;ÿ©ÀÓñx¡Ê<Åà1`y<6/Çw,g÷:Ó;CDDR\‹Íùß3OÓ¸qcï”Ò\BXªð�BX Tyé!Þn¾ñöëÚÕ;CDDRT‹ÍyåÅØr‹-¼SJóJah¼^¬R×Ñ™ÙfÀÏ@¤ÖGV®\EÏSNcæ¬r?YDD„–-[ðÊ‹/кu+ï”Òü´ !,Ž× VêAÆ…‘x@Pq Ô§¿'Ù¬Y3ïI)°ó¸6ž;¨ä �€™eÓñˉ©Ó¦qú½X±r¥wŠˆˆDXëÖ­xþÙg¢¾óÿ8(„`ñ|ÑJ­��„r€¾ql‰›vÜ‘gû÷£N:Þ)""Q;wìÈ^‹úÎpQ¼wþP… PàÇx„ÄÛ®»þÇ}„Zµjy§ˆˆHÄtíÚ…W^zž&Mšx§lÊÝ!„„Üí®J@ᥗũ%îö߯+ýž|œÚµk{§ˆˆHDœ~ê)<ÓïIêÖ­ë²)€õâqyšŽ™ "ûŒÞqã'pÞén"",++‹«¯º‚ Ï?Ï;¥<Võîâ7�´"v( R—7~ÂD.¼èb("’5lÈÃ=Àþû¥Ìýb.!ôOäªz��!„_ãñZ‰ÒiÝøú«Q¾Áƒˆˆ$@‡íyûÍ©´ó'Ñ;ˆÓ �€™`pX¼^3-^Ìy\Ä”)‘<wQDDâ¨ÇñÇÑ÷®;Ré\°yÀßB¿'zCq��Ìlà{ ;ž¯o999\sÝ |<r”wŠˆˆ$@à è{÷Ù½›wJE�‡‡>NÆÆâr Hágàúx¾f"dggóÔqݵWS­Z\ÿˆˆˆ³}öÞ‹¡ƒßIµ?ÀƒÉÚùCœW�àC‡Äûµáã‘£¸æºÈÉÉñN‘*¨S§6W]q9½Ïoî>דþÊ#î�€™m L%âõãmÞüù\síõŒŸ0Ñ;EDD*aÏ=;qoß»Øv›m¼S*c°gaQ27š�ÀÌΞIÔëÇ[~~><úýž~†üü|ï)‡F rã ×sâ ÇBÂvi‰´èBø&ÙNè¿-3{èÈmÄÛ¸qã¹áæ[˜3çWï)EVV§žÜ“«®¼œFRb±¹$œBà±ñD�u€Ïݹx[»6—G}”çžQ«""³g§=¸ýÖ[h×®­wJUý#„p³×ƾ^Rxiàx òO\ØØ“&qËmwèž""°ÃÛsÍUWrè!{§ÄÃà¸BW@R˜˜Ywb¿Ù”;-³  €7Þ|‹‡ÿóK–$ü¾ ""²‘­¶Ü’+.¿”c9š¬¬,ïœxø8 „àzoú¤1afw�w&k{ñ–““ÃOöã…—^fݺuÞ9""ioë6m¸ðÂóéqܱT¯^Ý;'^¦û‡z‡$s�¨¼•¬m&¢E‹xêéþ ø†‘h×®-žÝ»‘.ïø‹üJìŒÿ9Þ!Ä�ÀÌ�£I±“K²páBžzúÞxó-Ö®]ë#"’Ò²²²8ìÐCèuætî¼§wN","öΪwH‘¤_4if-ˆ]°m²·+V®dàÀ7xù•W™7¾wŽˆHJiÑ¢9'žÐƒSO>™–-[xç$Ê à kýËâr×3۞ذ¹Çö!??ŸGŽâ­·ßá“O?cýú¤ÝÍQD$¥ÔªU‹Ã=„O8ž.û·í­ˆ5@·ÂgÞ!s»m’™íŒê{5$ÊòåË2tƒ‡ ãÛï¾Ó½D$ãÕªU‹ýºv¡{·#8äàƒÈÎŽôCcãepLásï’¸Þ7Ñ̆5=;iÙ²e|:æsFþ„ÏÆ|Îòå˽“DD’¢I“&ì¿_W<`<`ÿLÙé™KìÿdïÒ¸ß8ÙÌN^&ïPQùùù|÷ý÷Œý cÆ|Á”Ôꀈ¤5j°ëßvaß}÷á€ý÷§c‡öé¾¼_š)Ävþ‘¾§¼û�� /is¡gy¬]»–)?þȤIS?a_~õ5Ë–-óÎ)·V­ZÑeß}è²ï>tí²/õë§ÝQÝŠú8*„°Ä;dS"1��˜ÙIÀ+¤ñá€M13~ûmË–-c]Þ:Ö®ÍeÕªU˜�ëÖ­#77÷¯_››K^^Þ_^gÍš5lX¿¡Ìm­ZµŠ‚Â×-­eåʲoR•ŸŸÏêÕ«ËüšŠX³v-ëówòäêÕ«Ù —¸ª_?›j!³Þá5lØÀ;!©ªUË";»ÞŸ>Ö¸Q#öÞ{/öÙ{/7nìTIƒSCk¼CÊ#2�€™ ¼Ôñn)'þÜBH™w‘��Ìì�bSTƯ#‰ˆHä­ú„Þô©¨È ��fÖ…ØÕ ½[DDDJ18!„0Å;¤2"yð®ðšÉCÞ-"""%tJÕ?Dt��!Œ:‘ºu¢ˆˆd´õÀµÄNöËñŽ©ŠH(ÎÌjÏ�gx·ˆˆHF›ôŠêý**²+�EB¹ÀYÀ@sŽˆˆdžvI—?¤À @q…÷ x¨ëÝ"""a!pna¨wH¼¥Ô��`f»»WÀ6Þ-""’Ö!,õI„ÈØXa"°±»ŠˆˆÄÛàŒÂ)éºó‡\(ÎÌzý�Ý‹RDDªÊˆ=œîšÂbï˜DKé�ÀÌÚ�/ûy·ˆˆHÊš\BøØ;$YRîÀÆB³ƒˆ]%¸'ɈˆH:Z ÜEì ÿŒÙùC¬�gf{O»z·ˆˆHä . !üìâ!åW�Š !|ì\”ý,[ÉT?'‡ŽÊÔ?¤Ù @qfÖ‚ØãÏ$Ÿ""Rnó€»gSé±½‰’ö;ÆÂÇ ?´÷n«Ç€{C«¼c¢"í��3« \Ü�4uΑäXOì¾weÂe}•@3Ë.!vÅ@#çIŒ<`�Ð7„0Ý;&ª2j�(bfõ‹›Î9""«gBs½c¢.#€"fÖŒØs/ê8爈H嬞î!üæ“*2z�(RxÅÀÀE@Kç)Ÿ_‰Ýûåñ ï˜T£ 3ËŽ$¶"p¨sŽˆˆüU0’ØÉ}o‡68÷¤, �¥0³Ýˆ­œ ÔuÎÉt ç§C³œ[Ò‚€M0³¦Ä†€3NÎ9""™$Aì }ï„òœ{ÒŠ€ 0³¶Ä“œsDDÒÑ:`40x+„°Ì7'}i�¨$3ëœ�ô@©ŠÙÀGÄÎóA!ǹ'#h�ˆ3Û8€Ø‰ƒÇ-|‹DD"m1±wùŸcB|s2“€83³jÀÎÀþÀ~@Wti¡ˆd®|`ðUá/€BæZ%�’Á̶"ö˜âNÀî@G µk”ˆHü¬æs üBl§?ø1„°Î/MJ£ÀIáíˆÛm‰Ýޏ^á§ê5 Þ¨Vø£aáÇjûZˆý7,ë¹µ)û.‡ €¬R>—…n•,’N X^¯óˆÝFw[øó•ÄvìE?Ïr ž,!viÞb³OM�¤Ò̬.P+Ž/™ Ôˆãëmjø©¨ê@ý8¾@ã8¿^}bâ£"g¬o¼3Þ”b;è­(ü\‘å…¯ €Î¢Éyd!���IDATÿ”I¦Þ/Ú����IEND®B`‚������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon-pause.svg���������������������������������������������������0000664�0000000�0000000�00000015150�15003540030�0022035�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="512" height="512" viewBox="0 0 135.46667 135.46667" version="1.1" id="svg8" inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" sodipodi:docname="icon-pause.svg" inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/opensnitch/res/icon-pause.png" inkscape:export-xdpi="96" inkscape:export-ydpi="96" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs2" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.8520834" inkscape:cx="92.868389" inkscape:cy="235.9505" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="1600" inkscape:window-height="847" inkscape:window-x="0" inkscape:window-y="204" inkscape:window-maximized="1" units="px" inkscape:document-rotation="0" showguides="true" inkscape:guide-bbox="true" inkscape:pagecheckerboard="0"> <sodipodi:guide position="45.643586,19.473337" orientation="0,-1" id="guide858" /> <sodipodi:guide position="48.574825,118.50736" orientation="0,-1" id="guide860" /> </sodipodi:namedview> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-284.30001)"> <path style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.88352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 24.756487,346.52813 9.319538,-24.69115 25.892395,-5.7321 24.204196,-12.72968 25.173834,11.71117 4.05241,24.92736 16.3729,12.59899 3.85152,17.78296 -6.72818,16.22266 -13.39906,8.50947 -91.980588,0.9444 -15.4585065,-11.35245 -3.13783,-17.10855 7.8853195,-15.623 z" id="path1481" sodipodi:nodetypes="ccccccccccccccc" inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" inkscape:export-xdpi="96" inkscape:export-ydpi="96" /> <path style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.260914;stroke-opacity:1" d="M 25.617431,400.30128 C 13.956756,398.62993 4.455433,390.16178 1.3179549,378.64484 c -0.61010168,-2.23995 -0.68141103,-2.97449 -0.68141103,-7.02809 0,-4.05349 0.0709898,-4.78804 0.68141103,-7.02767 1.4422687,-5.29433 4.0049287,-9.7113 7.7967237,-13.43793 3.1792184,-3.12477 7.6935334,-5.90721 11.3127154,-6.97315 l 1.011141,-0.29572 0.159592,-2.88031 c 0.699721,-12.63666 9.525535,-23.14365 22.243579,-26.48047 2.251529,-0.59144 3.06051,-0.67035 7.018915,-0.67878 2.525469,-0.006 4.987882,0.11997 5.653762,0.28414 1.179544,0.29572 1.179787,0.29572 1.94729,-0.66193 1.527585,-1.90257 4.987766,-4.99031 7.28526,-6.50045 5.401898,-3.5513 10.887485,-5.3302 17.382981,-5.63665 5.770238,-0.26941 10.984996,0.78506 16.16673,3.27379 7.128836,3.42523 12.181556,8.39142 15.666736,15.39805 2.50603,5.03807 3.59513,10.17644 3.34371,15.77554 l -0.13051,2.91272 2.13481,1.38154 c 1.17413,0.7598 3.3948,2.61049 4.93483,4.112 8.71561,8.49782 11.93955,20.33805 8.75495,32.15396 -1.29904,4.81951 -4.90315,10.89827 -8.63914,14.56995 -3.56956,3.50836 -9.80593,7.14911 -14.36425,8.38564 -4.82636,1.30967 -4.22578,1.29325 -45.398785,1.26083 -21.133453,-0.021 -39.125022,-0.13154 -39.981254,-0.25467 z m 81.947239,-8.71712 c 5.8504,-1.53751 11.05542,-5.03703 14.56544,-9.79276 1.44781,-1.96191 3.2449,-5.86701 3.9156,-8.50866 0.82755,-3.25884 0.82928,-8.34365 0.004,-11.5926 -1.99124,-7.83999 -8.14631,-14.63425 -15.64619,-17.27125 -1.49953,-0.52724 -2.28209,-0.9303 -2.20209,-1.1355 2.47536,-6.33996 2.20303,-13.3187 -0.75958,-19.46544 -2.59443,-5.38335 -6.55609,-9.27909 -12.007089,-11.8076 -3.90889,-1.81322 -6.232969,-2.31309 -10.75536,-2.31309 -3.127696,0 -4.219681,0.10629 -6.053166,0.58617 -7.241285,1.89288 -13.31075,6.68923 -16.465647,13.01161 l -0.732485,1.46794 -1.204478,-0.5988 c -2.240975,-1.11298 -5.542791,-1.97328 -8.178424,-2.13124 -9.130628,-0.54723 -18.012897,5.39587 -20.93206,14.00441 -1.653072,4.87486 -1.486231,9.52955 0.524816,14.64108 0.134869,0.34097 -0.08673,0.38411 -1.535235,0.29256 -2.040978,-0.12839 -5.359579,0.4904 -7.966056,1.4852 -5.358562,2.0439 -10.218999,7.06398 -12.038096,12.43418 -1.6865107,4.9784 -1.5773642,9.50883 0.346691,14.39252 2.080429,5.27991 7.182215,10.04153 12.692712,11.84623 1.10934,0.36201 2.648952,0.76296 3.421359,0.8924 0.794802,0.13259 18.196341,0.21573 40.092031,0.19047 l 38.687677,-0.0421 z" id="path826" inkscape:connector-curvature="0" sodipodi:nodetypes="ccsccccccccccccsccccccccccccccscccsccccsscccccccccc" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.6842px;line-height:47.9278px;font-family:Korataki;-inkscape-font-specification:Korataki;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.91711px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="34.512127" y="456.6312" id="text841" transform="scale(1.1975484,0.83503932)"><tspan sodipodi:role="line" id="tspan839" x="34.512127" y="456.6312" style="fill:#ffffff;stroke-width:1.91711px">I</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.6842px;line-height:47.9278px;font-family:Korataki;-inkscape-font-specification:Korataki;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.91711px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="61.164921" y="456.6312" id="text841-7" transform="scale(1.1975484,0.83503932)"><tspan sodipodi:role="line" id="tspan839-6" x="61.164921" y="456.6312" style="fill:#ffffff;stroke-width:1.91711px">I</tspan></text> </g> </svg> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon-red.png�����������������������������������������������������0000664�0000000�0000000�00000007422�15003540030�0021462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ôxÔú���bKGD�ÿ�ÿ�ÿ ½§“��� pHYs�� �� �šœ���tIMEâ h¼úÅ��ŸIDATxÚíÝ[rÛ:PÅýoÙ÷+©[eÇ–%>f¦ÏY@$0Ý€”øñ�������������������������������������������������������������������������������������������������������������������������������8Ææ�}<E†¨9Š�05è��a¯ ��|…��@à+(�€ÐúÊ� � ôQP��¡2€�}”�@ð£ ��BE��ü((�€àG@�?Š� � øQP��Á¯`�ÂE��ü((�€àG@�„?J� � øQP��á€�~�@ø£ ��‚%��þ((�€ðG @�„?J� �~Á"€�”��þ  �€ð%��„?(xI ü…Ž÷¡(�€à,Þ— ��ÂDˆx‡J€�á½z§ � $„÷ì+�€PÞ»w®��B@�XÖ€�~Cßz°� hØôÖ†µ¡��AÞp·N¬�êº5cÍ(�@Ð 7Ä­ëG�‚†·Ám-YK �``cMYSì`H÷~ö�n�pú)„Ý´à·'¬3� µ_0CûÅzS� `€ æ„=dÝ)�`X ~ì-ëO�/ü±ß¬C�„¾ðÇþ³�ü-ö£µ©�`È`ÀbZŸ � †+öªuª�``¨bïZ¯ �††)ö±5«�```bO[» �†(J� �'9{Ý:V�004QP�ü–¤ìkZ@ðcP¢ð‚å ü…?X;n�üàÐf>Xç �ÂC%€_Ø=�7�œþÁ-@�_´‰þ`m¹@ðc@CÏYb¸@øà@ø{ Nÿà�7�6)¬9¼DÁA sf=áÀ†0Q�,|œþÁDþ�桀Ŏ“X‹ïá óf‘½ò=? ȆŽž9à�á/ì+¿ 'Ì%ûE°É~àû1Ð0›ìÀøaïË ÃŒ²oŽä;�þ‹þ.>¶Ü�àôÜÒ_yN1˜Uæ‹�Jð»�p üÿôwìYan™7 €M$øß·g†Ùeö(�6à{÷žf˜tßÀ¦óÜ�7�hÎ p ÀO´al2€@>þ‚À �Àly78ýÛÜ@Æl3³Ü�Ø 6€�Â0/�œþmf��á/ü3W ÒöxlÂpxP�j¢6.€€Ö€ÀäÓ¿ð}ÀCø  kþ™qn�°1��œþP�pú@`úé_øæŒ€M €ÀäÓ¿ðÌd�'��4M��œþP�þ�æÀ\ÿ � } �8ý ���@Àé� _þ@Àé�Àé��œþP��� ™j×ÿNÿ�(��€0Ó?wûx<>|)�@�€™CÎézìP��Ü€� €@C®ÿéV<P� 1p�(�8ý£� �S<P��·€à´Š� �ð—ÏÿQ´�p�(�€"@÷ç)(��‚�^åóÜ� � � ÀÙCÇS�{kE�p€µ¢��îT?u'®`�_�„ž¡ƒµ¢��8áa­(��†;n��ÃÝ3W��ÃåE�PP��eQ�p \ùû(��Š�J€�vrÅúP��ÜÉàõ¡�XÔ€"@`&ø?ä¼ðÊ? À°d*?ƒÃè¾6Ü��¸ þg�@ °(��NºžIàßßgX^¬ï�@¹Áj¶šå׃E:`+�`ð›£Ö‚¸X�°?ÍWkA\˜ �اæ­u �.B�ìWsØP�œ�ö¬¹l (� L�{לöþ€À¥�€=ln{÷ €&陃=lŽ{÷?Ú-€:sJx.,Íu7�Âß �ØÓŠ“÷ý‚5yQ@°)JÞwHü�y%`B¸:»F€äàWzÀÉÖ3S~cÄ—�… Äøÿó“ 7�^2 ü™ô,¯Ê5È‚÷žÀÞÍÏyT�„JŸAâ]à7‹j®‘å%Ï;ò™~‹Ü{Aä& mÍl^˜ãÝàWòÖÏò’�„¿w’§tþ@÷4J@Õ<ôd,lïœ.£ušSG®­åE�ï,O© üÎ!"ü•€N9éÛâ´÷ N4›YG¬»åòÞ©¢Å ö·}Eõûî:Ü=`ƒ°?ùü®§gÔm‹Yø&8EÙ¯XÃ÷­ËÝ5@�û–ȵ-ü °ßí_Òn–‡xÎË0<@yÇzpP� è¿çícº®çWÖîîÁà„góÜ:™”e—|01ü]óƒð‡*^ÉÙ½âʰ�ìeÜœþwþ†äÌ�{™Éùö›õ½{†ØÏ¹œþ ˜=ìgÜ|¶’Šay'~ûYqQèþ?`8CgÏæðò¨ pꇼì8ôK€OÿÄðîÚ똇Ý�À^†žÉäØ� ˜±íedÊ7�~à ø¡×-À:û7þ€=ŒµYÏîÅ�ö0Dî©ù§ƒîqÄŒ°I\÷Wdàø/`ÿ€§Ãú¿ý‹âZ°�@ðCÿÊì‘ `ß‚õü½_ÿ+€î?é0(7�† ôÙ«ö+\ã«Ãûz÷þ€½ ýÖùîAö)äyú òéßPúÁoŸ¢ô–û³õ.�† ýÙoÖ7ÌÙÏÕ2sïö‡ƒà}­ÿ€ÓÂU¯ùñÀÕ?~À �p`ð à®GËàôS? ·Þ³wRÂÁpr�?0Órúá(�ÀÁ/üj‡Ø?si÷àÀ‰p`€ð´¸púGð\p�<üÂèx ]ÕO2Nÿ8õÏÿ�‚P�jqúGðœ3Ï|�„?~PiÈ9ý#øÎã�þü˜|ÀU�À©TòK€®ÿü��ÃÁ£ÜG�Nÿ€ o�@ð(� ø[†"ÂÀ À­|þà,� ø®á?Bø¸�Á �\Èçÿ~€ €Á‰ð÷w£&#"n�@8(� øNä_ üÜ�ÜÃç\~€À�‚àZ>@ø¸�Á �€àÉG��7� ø�ü�#ù�áà�?@Â,T�ü�|€ðP����Þä=ÔœÍ �—,4E�À �n�"Uú^”€Û��7� �(�pAð��Ü�ŒTåóÿ?óV@�p�uÚ)� �n�P�P�ú¨øsQ–C—"à)�7KÝ�à6� €"�p¢ª·Ü �­‹€§�ðò ­ÑN s&6lû¨ö�•»ðE@Þ]ØÂþÏó�NÌ��Ü�(� �¡ÒõÿW³pyX$Oà‹`@â6� ë@ë#�€ásO€6@Âé¿dð=�Ü�\X� B€Y‡ØïfÛò�A ‚o��·@ÎáU�E�:Çž.�•žè°�ºf–�p�*]�Ü �Nÿ¯Í+7�pÃÆ„»€jÃÍ-�n�Ž?¤¸�E�< .®kÜ�UfÑ2ÌÀm�w0]6(@^­ï†˜× ïO„Õ™³<xp�äñ¯�@�ÂNÿ?€ŠË-�Ý‹€§�Âß €Û�@æÜr°Xgý€"�¸p �J6ÈšBócy1à6�È ÿ§ €ÁŠ�ÿqó¦ÿÃ00H ÿ#ro]ù›ij�ÿ–—�}rä¨ùºã7U�n�¼D�äÆÅñuço�Âÿžü]^(�Â?Ï6ùÁ¹­�`BøŸ‘gÛô¨� ü?[^4�Â?Ï–ò0Ý�þ†!½økg=TE�@øË«ƒ~q%��áß+§¶Ô‡¬�þäŒZ�æ{ÞuKèn�„b&m¾"� øórhY6s@âì6¿‹Ü�LS·�nÓsgób”��ÁŸ—7›—¤�þ¼ŒQ��Á˜+›§�þ¼,Ù¼DE�@ðçåÇæ…*�‚?/36/W�úy9±yÙÊ�€ÈË@�0û³`³,�s>oîo‡…`¦çÍøÍ‚±`�Ìí¼Y¾YL Èɛ˛…�y‡²2P%��áX�”��„hP�þ¡@�@ð�%��áZ�”��„hP�ü¡@ �@ø‡�E��á\�”��hP�þ¡@�@ð�%��áZ��pP�ü¡@�@ð�E��á\�”��hP�?Á@�ü�E�@è{^(�‚_P”�Á¯�(�}@�@è+�Š��B_P�ú €B�€ÀW�� �{�å�@Ð������������������������������������������������������������������������������������������������������������������������������ÐÏIê®Í7¬û����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon-white.png���������������������������������������������������0000664�0000000�0000000�00000050430�15003540030�0022025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ôxÔú��� pHYs��Ã��ÃÇo¨d���tEXtSoftware�www.inkscape.org›î<�� �IDATxœìÝwxTåÖÆá癄ÐQº JP±W<p¤éQQPšH/bo  G‘" ˆt¤HAéÒŠ”„–2e}„ðQ’23kÏÌs_×¹¤$³!{åÝ³ß 6£@D”]"rS"PÐÜd€‚žäÞ n êŠÏÅ#D�¸ Àl@¼�q��œ7Àqb<@t Úç·9"á�@D–'"¹œ@iPF€Ò&ùŸe�Ü 0�›Ÿ“.�8 à¨�‡ °Ã�ÛœÀöHcø¹…(K8�‘eˆH¸¨à>p�å‘|¢¿U9-3Î"y Øê¶`³Ø`ŒIÔ#º�"R#"EÜÀc< à1�wSÎò…�¿X `µXcŒ9§ÜD!Ž�ùˆäwÿPÀ$y ?¹ü‰ä`XaŒq*7Qˆá�@D>•(RÑ4�P@U�vå$+Š` ˜eru€ü�y]’Èhj€†”Öî 0‰�– 0+ ˜eŒ9¡DÁ‰�yE¢H9ÐÒ�-yÒ÷'€øÖ,0Ƹ´ƒ(xp� ¢,‘H7PW€N�ªk÷¹ÓøÑ|‘Ø¿´c(ðq� ¢LK¹×t 9€\Ú=!h�ß„ßcâµc(0q� ¢ ›¨#@Wð»}«8 àð™1&F;† �"J—ˆätô0@IíJU¼Æ»€"ŒÙ£C�¥JDr;ç е_(ó�X�àÓ0c–hǵq� ¢«ˆHNð:€^�òi÷P–­�Ð?̘UÚ!dM�ˆÀåkü=Àû\ê*k�ô 3f…vY �"‚K¤¡�#�”Ón!Ÿ™ïÞ 7f‹vY�¢–$òÞð„v ù…G€ïÃ€ÞÆ˜£Ú1¤‹�Q‘.àS� µ[HÅy�#ÀGÜG tq� !"bw] 0ÜÀ'ä pØtsó“v ù�¢‘$RÅ�c< ÝB–³Ì tŽ0æoíò›v�ù–ˆätŠŒ4ÀFðäO©{ÚlvŠ ‘píò®�1—H}0Ê�ŵ[(`l Cc6j‡oq€(‰H¾$‘)ÌæÉŸ2©‚~sŠ ‘Ú1ä;\ 2N‘‡ 0I€Ûµ[(àm»´ð‡vyW�ˆ‚„ˆ8œ"#�¬æÉŸ¼¤â¥Õ€ž"Âoƒ ÿƒ)ä&ƒé%ßYê�Úr¡àÁ€(À%‰Ü`:÷ï'?8 }˜1 µC(ûx €(€%‰¼j€5<ù“ŸÜ `¾Sd�/ >þ$ @"âp ÐE»…BÖ|ÐÚ§BYÀ(ÀˆH0 À´[(äíö� ÃÙ©B™Ç€(€$ˆÜå�æ PZ»…è’X�-ÂŒY¤B™Ã÷�ˆ$‘‡ìÀ*žüÉbò˜Ÿ$ò²veW�ˆ€KäY&ˆÔn±2ÁÙsçpþìYœ;wn‰‰‰HHH¼îc/^¼ˆœ9s^õk¹¢¢`w؉¨\QÈ•+¢¢¢ü• †:€ÆÑ¡ã�@dqI"] ð ¸bǃ}ÿìÃ?ûöáàÁƒ8xð<ˆ˜èhœ;wçÎóú11È“;7ò(€Â… ãÖ[oÁ­EnÅ-…oA‰’%P¶ì]ÈŸ?¿×¨¼hŒqj·Pú8�YØ¥ýújwhILLÄÖ­ÛðÇÆ?°iÓ&lÚ´Ù''ùìºùæ›q×]w¢lÙ²¸÷Þ{PõþªÈ;·v–¦y ©1&A;„ÒÆ€È‚DÄæF r×UcbŽbÅŠXñë ¬_¿ññw±Ûí¨P¡<|ðA<üÈÃxðÁ`·Ûµ³üm©h`Œ¹ B©ã�@d1"æÆ ¥v‹¿ìÞ½óç/Àòe¿b÷îÝÚ9^—/_>ü§úP«Ö3x衇àp8´“üeµ¨cŒ9«B×ã�@d!"á~PG»Å×¢cb0Þ|Ì›;?(OúiÉ—7/jÖª‰fÍš¢|…òÚ9þð»ø¯1&V;„®Æ€È".ügxF»ÅWœN'-ZŒ©SÀÿ€Hh¿Y¼B… hÞ¼)êÔ­ìwüvi8¯Bÿ�‘ˆH䥓ÿµ[|áHt4¦M†3fàÔ©ÓÚ9–“3gN4lÔÏuhâ%ŠkçøÊòK—âµC(�"e—Nþ³ÔÐnñ¶-mÁر_aùò_áñx´s,Ïf³¡FêxrOí_øÙ44Æ$i‡�"U"’ãÒwþµ´[¼iíÚßðÕØ¯°nÝz픀uÏ=÷ ã‹/àé§«Á˜ úR=Ý´0Æp"TTªˆ‰ˆØÀd4Ónñ–•+WaÔ磰eËVí” Q¶ì]èüjgÔ¨Q=˜ÃŒé¡ê‚æOQ ã¾ £v‹7lÙ²~ðÖ¯çwü¾rçw¢ëë]P½zp<R€×só™vG(ã�@¤À)2À[ÚÙuàÀ|üÑ'X¼ø—G¿¿Üwß½èÕ«'*W©¬’]4v3K;$Tq� ò3—H;ÆkwdG||<¾üb ¾ýv\.—vNÈ1Æà™gþ‹7{½‰bE‹jçdG¼�å0f“vH(â�@äGN‘G,®Ý’UË—ÿŠaC‡#::Z;%äEDD ã‹/àÅ;"<<0ÿH p( ¸ßsB»%Ôp� ò“x‘ÛÀz�…´[²"&æ(†ŠåËÕN¡k”*U öÇ#<¬’U¿:€Æ.'ù� "’@ž$ ®zл2ü¼VÎpðx€¸pÀ à<€DcÌŬ¶¹€µ�*fåóµÍüi&FŒiÉ'ñQ2c š4m‚Þ½{"W®\Ú9YñY˜1¯kG„�D$Ÿ¸ÓÜ%Éÿ+c€2ä7@^�ùà¿?ϱ8%Ài�g8e€ì1Àßà¯+Ÿ€&"9]ÀLà.'OžÂÀ±té2íÊ "EnÅСCðècj§dšÚ8Œù^»#Tp� K‘"nàI�Kò?Ë!pþ¼º�üe€?H ¾JiGeÖÒ¥ËÐÿí8}š[÷c Ú´mž=ßDŽ9´s2㜸/˜=Ú!¡ P¾ R‘<n :€š�ª PZ»)T¹Ýn|øáG÷íw¼µ/À•-W}ôn¿ýví”ÌøÃ<jŒIÔ v�HM‚H;ÐÉÛà> L9)ä8qÝ»õÀð®¬`‰ÁC£~½ºÚ)™ñI˜1ݵ#‚�ò«D‘²6  ’ÿð;™“õë×£G÷7ø´¾ Õ¾};ôêÝv»];%#Ä�õÆÌ× f�ÈçDäf'ÐÊ�íå#ÎÝôiÓ1xðPnêä|ðA|òéGÈŸ?Ã7Åh:ê�*c8‘ú�ò q¸zt@ò?—÷-Èãñàƒ>ķߌÓN!?)^¢8¾þz,J•*¥rC|ŸÃ˜6ÚÁŠ�y•ˆÜìžà”Ôî¡´ÅÇǣ盽°dÉRíò³|yóbÔèÏQõþªÚ)7d€&cfhw#�äI"÷�xÃ�MÔ}G¡(6.^| [þÚ¢BJÂÃÃñÁ‡ï£FêÚ)7rüÒ¥€“Ú!ÁƦ@Í)RÝ)²È�› Ð<ù[Þ¿'O¢}Û<ù‡¸ÄÄDt{½;æÌ§r#…ÀGÚÁˆ+�”%.‘z pŸv e\LÌQ<×áy<xP;…,Âf³aðàhÚ¬©vJzÀÓaÆüªL8�P¦8Ej àíʜÇ£}ûˆ‰9ªBcŒÁ€ýѲe í”ôìt�UŒ1IÚ!Á‚—�(Cœ";EVø<ùœcÇŽ¡C‡çyò§T‰††™3gi§¤§œ xC;"˜p€Ò/r›xÇ�ÍÀ?/éäÉShÛ¦öï߯Bg·ÛñáG fÍg´SÒrÑTˆ4æ€vH0à �¥JDr9EF:€hžüRl\^x¾#Oþ”!n·o¾ÑkÖ¬ÕNIKN;0R;"Xð‹:]Ç%ÒÐ|f€âÚ-”u hßþ9üõç_Ú)`råÊ…ÉS&áÎ;ïÔNIðHcÖi‡:®�Ðe"RÒ)2G€™<ù6Aß>oñäOYrþüytzñ%?~\;%5Æ�‰¿Í&�1I"]À6�õ´{(û>ýô3üüóBí `ÇŽÇË/wFBB‚vJjv'o:FÙÀ Äŋܿ–`4€\Ú=”}3gΘ/ÇjgPعc'¼=@;#-#E„e€–$ò¢Ø šv yÇ–-[1pÀ í "sæÎÃøñ´3®#ÀmNàíŽ@Æk(!HDò¹€¯4Ñn!CㆣBAÆn·cü„ïPµªå6þ<â�Êc,yÂê¸bœ":?Á“Pñx<èݳ7Oþän·=ßì‰Ø¸8í”ks/iG*�!$I¤€å|Loð;æ+¬X±R;ƒ‚ØÑ£Çп_íŒë ˆäÔîD�B€ˆD%‰L1ÀX�aÚ=ä]›7oƨQ£µ3(üòËLòƒvƵnq¯hG"¾ È%ˆ”¶3TÔn!ï‹O@£†ÏâÀÚ)""##1oÞl-VL;åJGÀíÆ§vH á @sŠ<lÖ€'ÿ õá‡òäO~¾}߆ˆh§\©˜;yËrÊ�AÊ%Ò ÀR�7k·o¬[·ßOš¬A!hÆ ˜:ÕZ—èÉÝ3‡ÿg!§È��ƒÀÿ¾A+>>uëÖGô‘#Ú)¢¢¢¢°àçy(\¸°vÊ•j„³D;"Pp ˆˆˆqŠ|`0xòjcÇŽåÉŸT]¸pï½û¾vƵÞÐ$<I ÉáÆ …v ùÖ¡ƒ‡P¯^$&&j§aÜwßâá‡ÒθL€*9ŒáS°2€+�A@D"]ÀlžüCÃСÃxò'Ë:d\.—vÆe6 ›vC à @€K9ù¨¡ÝbgΜÁ?ÿìñcÇp6.±qqˆ‹C\\òÿ®=yæÉ“çòóçÏ‚ ¢`Á(tóÍ(V¬J–,‰ÈÈÿk¤jÉ’¥xíÕ.ÚDWéß¿Z·i­‘"Þ1ÆÄj‡X€�ê'ÿ .àÏ?ÿÂî¿ÿƾý°ß>üóÏ>œ9sÆëǺå–Â(Uªîºë.”¯PÊ—Çm·ß»Ýîõc¥Åív£núØ¿¿ßŽI” Àâ_"W.k<PT€Wr3F»Ãê8�(‰psB'ÿsçÎaãÆ?°qãFü¾áwlß¾n·[­'22•*UÆý÷WEÕû«¢råÊ>])˜1ã'ô{ëmŸ½>Qv¼ôr'tïn™Õ÷ aÆ<¨au�ˆ8\Àt� µ[|íÀXøó"üòËìÚµKõ„#aaa¸÷Þ{ñÄãÉ'Ÿ@é2¥½öÚIII¨ùL-ÄÄõÚkySddý²7*¤�ð�w‡³M»ÃÊ8�1nà[:h·øJÊIá…صëoíœ,+Z¤ª×¨ŽZµj¢r•Ê0&ëÝ&L˜ˆÃßñb‘÷µk×oõë«‘â£0cx[`:8�§ÈÂ{]/^¼ˆ9³çbê?`×Î]Ú9^W´H<Së4~öYÜQúŽL}n||jT¯“'Où¨ŽÈ;"""°x‰eVN8€b|>@Ú8�$‘N—žè4:ŒiÓ¦cú´éV|Ö¸OT¨PÍ›7E½úõyÃ?~Þ1ÒeDÙ×¾C{ôíÛ[;�`€FcfiwX€�á©#Éïø÷ßÛÎ}hÍšµ˜8a"V®\Ç££"_Þ¼hÕºÚ´m ¤ú1‰‰‰¨Qýœ8qÂÏuDYeË—"þüÚ)�0#̘&ÚVÅ€@’He¦ Nþ߈V-Ûà…ç;â×_W„ìÉ�bãâðÅ_â?OWÇСÃqôè±ë>æ§?ñäO%>>?L¦‘â±Æ&Ä�‹‘‚nàwnÓnÉŽÝ»wã‹Ñ_báÂEÚ)–†FÏ6«¯¾‚Â… Ãårá™5£F”)7ÝTK—-Axx¸v P×aÌ|í+â�`a—n÷[àií–¬Ú·o>ùøSüòË«=?ܲÂÃÃѼy3,Xü‰vQ–Œ|÷4lØ@;øŸÃ˜µ;¬ˆ€…9E>ÐC»#+ñÕØ¯ñõ×ÿCRR’vùY¹òå0sæ í �8~ikàнޘ¾À¢\"Í 'ÿõë×£AýF=ú žü‰BÔÎ;±u«%öá)ì¬ó¸B á�`A"RB€/µ;2+îìY 0Ú?h瑲Ÿfü¤BÿZ„ñ€ÅˆH˜ X  ö±^¹rúöé‹S§Nk§‘EäÉ“+W­@D„úñÿ3¦¬v„ÕpÀb\ÀÐÉßårá½÷ÞÇK^æÉŸˆ®röì9,þe‰v�Ü%"E´#¬†€…8EÐK»#£¢A«–­ñí7ãø"JÕìY³µ��nà í«á�`"e€qÿ& .BƱeËVí"²°õëÖ[e›o�׈“M(p#ðÞóc}DD0jÔht{½;Î;§CDçr¹°lÉ2í ð¸vƒÕp�°�§ÈtÖӉ޽ú`Ô磵Sˆ(€,^¼X;�*ˆÈMÚVÂ@™ˆä�0ÿowö,^xþEÌ™3W;…ˆÌÚµ¿YaÅиǴ#¬ÄÒ'PàÞ�PN»#=ÑGŽ e‹Vذaƒv  ¤¤$¬]û›v/\ƒ€¢x‘R�ÞÖîHϾ}ûТy+ìûgŸv °µk×j'�ÀÃÚVÂ@‘ø@N펴8p�Ú?OžÔN!¢�·zµ%€ " ð.á� Ä)ò€†Úi‰Ž‰Á Ïuä³è‰È+¢Á¡ƒ‡´3ò�(¡a�ˆˆ ÀGÚi9rø0Ú´jÃçБW­±Àe�7p·vƒUp�PàÚ¸G»#511GѾÝs8zô˜v ™Í›6k'@8�\ÆÀÏD$À`íŽÔ\¸p¯¼Ü™ßù‘Oüõ×íPA»Á*8�ø™xÉ�ŵ;®åñxÐóÍ^øû￵Sˆ(H:tgΜQm0\¸Œ€‰HNôÑîHÍûïˆeË–kgQ+<?¤¬ˆ„iGX�?r¯�¸U»ãZ3~œqߎÓÎ ¢°mÛ6í„�øh`p�ð›K×þßÔî¸ÖÆ`Р!ÚD"öîÙ«�PL»Á 8�øÉ¥wþߢÝq¥³gÏ¡WÏ^p:Ú)D"öîýG;†���~!"6cÁïþ „˜˜£ÚDBöïß—Ë¥Ú ��p�ð wòŽe´;®ôÓŒ™X°àgí " 1.—Ë ;r���¿ «vÕŽ>ŒáÃGhgQˆ:xð vBQí�+à�àc‰"�<©Ý‘Âív£Ç=qáÂí" QÇŽ©ï4j¹½X4p�ð1;ð²vÕ&Mœ„-Ø‹ˆB—öVãÂÛ�p�ð)É%@;íŽÿž<‰Ï?­AD!îØQÝ7›ä§†<�>äšÂBÐ>|ÿCœ?^;ƒˆB\Œþ%€(í�+à�àC&ùÞKؼy3fÏž£AD¤þ<��a"’C;B�‘’�×î�’ô3bø;í""œ‹;«��¹´´q�ðWòwÿ–øÿwÆ3°u«úþÛDD�€¸³�¬À'¨ ÕR;�HÞtcÌØ¯µ3ˆˆ.KLLDbb¢jCßÀÀEÊ(«Ý�sçÌEô‘#ÚDDW9{îœêñ W�8�ø‚ h¬Ý�$oú3–ßý‘¹œºÏ0@¤j€p�ð&Ú�ðóÏ qàÀí "¢ëxÄ£àÖÐÆÀËâEJ¨¤Ý!";æ+í "¢T‰[÷ü+�8�x›xF»�V¬X‰={öhgPˆ{ñÅŽÈŸ?¿vYÛÃ�m�¼Ì�5µ�à§?i'¡IÓÆøyÑ4oÑ 6¿ÜÐÿ³À¾$ºoB°�þô" PM»#66¿þºB;ƒv‡ùòæÅàÁƒ0cÆtT¹§ŠvYDDD„êñ…��orÈ«Ý1{Öl$%%igÁa·_þq¹òå0yò$ 2ùòåS¬"+Ð�ÀK��¼ì í��˜9s–v�Àá»êç6› Íš7â_¢]»¶°_1 PhÑ�¸ÀÀÛÓضmvíú[;ƒ�`·§þ%&ož<x«__Lýa î¾»¢Ÿ«H›1áá᪠8U,€€—ˆˆÀ#Ú3gÎÖN ºÌáp¤ûûwß]?L›ÊË!&** ÆÕ†à¢j€p�ðgò½ÿy4DK—,ÑL ºÊ�àÿ/ ,\´�M›5åÝ! `Á‚Ú �pA;@ÿ¦yÏÚ[·nñcǵ3ˆ.ËÈ�"_¾|:t0~üq*W©ìÃ*ÒvÓMꀀ+��¼ÅܫݰtéRí¢«dåM~å+”Ç”)ßcðàAÈ—Wý¦ò h'\4ƨïD¤€—ˆ€+Vj']f³Ù²¼œo³ÙмE3üÌËAÉ—�B~ùà�à"’ÀÝš gΜÁî¿wk&]%3ËÿiÉŸ??/ ¡"EnU=¾ΫX�/Hî zOËo¿­ƒGom¢Ë¼yÊeAƒò²@(^¢„êñ…×ÿp�ð {ò� ê·ßÖi']%,,û+�W²ÙlhѲ9~^´�Mš6áe�V¢Dqí®�€€WPV»aÓ¦MÚ DW±Û½;�¤ÈŸ??† ‚é?þ€J•ÕŸ¼MYPBy�|���^!Ê+�çÎÃþ}û5ˆ®ãðñ6¿*TÀÔ©“1pÐ�^  @îܹµ38�€€Wå`Ë_[xýŸ,ÇáåK�©±ÙlhÙ²,œÆMó²@�¸óÎ2Ú �."º[Z�ÿ¶xG)̓oß±CóðD©²_ó _*P �†ÊË |…òÚ �ðŒ X™ r§vˆ&�Ù$"�nÒlØ»{æá‰RåëK�©I¹,ðî{#‘?~¿Ÿn¬\¹rÚ )³:Ez_z–KÈá�M‰@q�ªKI»÷p� ë±;t¾¦Úl64hP?/Z€víÚò²€ÅT°Æ @ŠH�#]ÀÊDõ7sûÿfd“(¦y|ǃýûh&¥Ê᣻�2*_Þ¼x«__Lž<É*ËÎ!/gΜ(Y²¤vFj±›œ"o„Òj�€l2€ê–VÇŸ@bb¢fQª¼± 7T¹§ ¦OÿöGÞ<ªì y÷Ü{W7ˆò²H�¸€Õ‰"–¹NáK�²É¨nj}äðaÍÃ¥Éwd”ÝnGËV-±hÉ"^PôÀý÷k'dÄC6`s’ÈëÁ~§�ÿd“Tku$:ZóðDi²Û¬÷Þ•—Ê– ¹K¾êî«zŸvBF…à0SDÔŸ\ä+�²Ou�ø÷Ä ÍÃ¥É*—�RSåž*˜1c:úõ{Ë ›Ò„„ˆˆTª¤úÌ´¬hàJ¾SàIí_à�M¢| àÔé3š‡'J“Cé.€Œ²ÛíhÛ® æÿ<õêÕÕÎ z÷Ýw/räÈ¡‘Å�,Iy];ÄÛ8�d“T÷ =sê”æá‰Òdå€+Ý\¨Þÿà=Lœ8¥Ë”ÖÎ ZÕªUÓNȇ>I™$"‘Ú1Þ ûT×ãΞÕ<<Qšì2�¤¸ÿû1sæ ôx£;"#ƒæk¼e<õÔÚ Ùf€Ö.`©ˆÜ¢Ýâ �²Oõ¾¢øøÍÃ¥Ic'Àì C§N/bÞ‚¹¨^ý?Ú9AãŽÒw XqõG�{ËÃ.à÷$‘ªÚ!ÙÅ ›Œò�È€¬)P.¤¦h‘"5úsŒûe0¸Ô<ØËÿ©)f€.‘úÚ!ÙÁ ›Dy�HLà&@dM<�¤xê©'1oÞltîüJ ¾ÍjÖzF;Ár ðS’Hgí¬â�}ª�LV �|ûZ××»`þü¹xâ‰ÇµsNÉ’%Q¡Bí _±`´SäS ¸óipü U""á. \³Aë+qs¡BX¹z…v‘W/Q_}=š¿Ä�� �IDATË—ÿŠ¡C†"&æ¨vR@¨ß žv‚?tuEE¤1&`®ËÜÄb1ê‹k?p…(ÔT«öæÎ›ƒçžÎÊûÚ[F½º!³ÇBcð³ˆÌÎR�²!Qù@ ù]ËDä_QQQèÝ»'fΚª³½­ß-R%J–ÐÎð§§\ÀQÝ!6£8�dƒÍ+�QQQÚ D!ëÎ;ïÄÄIðÎÈ(X0 ¾æûUtL z÷êƒØ¸8ízÀü{p�È þ4‘ܹsi'…4c 5jˆ  eË|Òà5fÏžƒÚ5ëàË/Æ„Ò p· X!"–^þàŸÔL‘p§HM—Ȩ$‘ýLÑnŠÊÅ€È òæÉƒƒà‡iSQ±bEíK9}ú4>ýô3T{òi 2 ;wìÔNò‡;ÀÊËî/ÍàDÄî©áùÖð³�¯ ”v�.;YÌÝwWÄ´éS1p`äÍ£~•ÐRâãã1ùûÉhÔ¨15jŒI¿êíÌ PÒ,‘’Ú-©á�†$‘\"Ÿ¹€h�‹x@>í®kÝR¸°v]Ãf³¡e«–X°p6l�cŒv’åìܱÆ Ç=‰^={cãÆ?´“|Â�ÅÝÉo ´Ü{8�\ADÂ\"-œ"ë °^€.�,}†-tóÍÚ D”†‚ `ä»ï`âÄñ¸£ôÚ9–”˜˜ˆ9sæ¢M붨]«.¾÷bccµ³¼J€Ò.`±ÕîàX @D º€N�:#ùÙÏcûöíhülSíŒTq# ¢ÿçt:ñ¿¯¿Á˜1c‘˜È-¼Ó¦M›âùžÃ-·XîçìØà�ªcÎi‡�!>�ˆH!ÐÀË�rj÷dÅùóçQõ¾´3RÅ€èzÄ ƒñÛoë´S,/,, õÔG§NQ²¤%/£gů ¶1&^;$$�ÉïÞÐ@À¿þñGŸÀ¿'Ojg\‡�QÚfÏžƒwG¾‡Ó§Ok§XžÝnGíڵЭ[W-P‹´i™ï�cœš!õ�Éíàöx Apò€R·ß¦@D™Ô A},X8?Ë7 Þ€ÛíÆÜ¹óP«V]¼ÿþ8wÎ+èÙQÇ |£€ˆØ’D^p{ W»É›î¼óNí"Ê‚|yóbøˆa˜8q<J•*¥cyIIIøæßâ¿5jbò÷“áv»µ“²Ì�m"½5‚~�HyÀüf€ÿÊ·ÌW(_^;ˆ²¡êýU1sÖOhÓ¶5W2àÌ™32dê×oˆÍ›7kçdÇ—H}­ƒí� "7»Dþg€ß�Xó]r^R¾�¢@·ßî‡qß}‹|yƒj‘ÒgþÙûZ·j‹áÃGàâÅ‹Ú9Ya`R¢ÈÝ*×8¨/‰ˆIyÉü-À ÂÇk•.}"""´3ˆ(›\.Ö®ý-¨wÇó6ǃ‰&¡^ÝX½zµvNVä6À)äïÕÉ1^¤” øÅ�c`Á]û|Åáp R¥JÚD” ÿìýÍ›µÄWc¿‚ˆhçœèèht|¡úõëøøíœL1@)0CDrøó¸A1�\ú®¿³Ø à?Ú=øLr¢Àär¹ðåcШQclß¾];'àÍøqš4iŠÝ»wk§dÖãnà 0à€+¾ë ¹­/+î»ï^í"ʤ;v¢Y³øôÓÏ””¤4þÙûš6iމ&i§dŠ�/¸DÚùëxývS—H¾DŸøSÄÇÇãÁ¶ÔnD”º‹/â“?Å÷~+[ ¨U«&† Ѝ¨(플:çî0f¯¯+�"áùT€‰àÉ�ÉË�D`ÕªÕ¨W§>&L˜È“¿üüóB´hÞ G¢£µS2*·˜ê÷Ü�(RÞlDò6¾t…'žx\;ˆÒpúôiôz³^ìØ Ñ11Ú9!eÏž=hÞ´96mÚ¤’Q÷¹€a¾>H@ �I"lÀ�´[¬èñÇ9�YÇãÁôiÓQ»fÌ™;O;'d:uÚ?Y³fk§dÔN‘ÿúò�1�ˆH˜KdŒÆ˜ 9þvGé;PºLií "ºd×Î]hÕª ú÷ˆØ¸8휗””„>½ûâÓO?ÓNÉ€ñ"â³l-?�ˆH°H€—´[AÚµµˆBÞùóçñΈ‘hܸ)þÜü§v]ãË/Æ`èÐá°ßÂ-®äo|}ÂÒ@‚H°@5í–@Q·nî%N¤hùò_Q¯n}Œ?oò³°ï'}^={£Ú.‘Ö¾xaË�N‘öäëýeµ[IñÅQ±"ß"Aäo‡Æ‹;á•—;ãèÑcÚ9”sçÎÃk¯uEbb¢vJºøDDnòöëZr�Hé`Bh;_oª[·ŽvQÈHLLÄçŸB:õ°jU@îEÒ–/[Ž×^íb©=TRq“x×Û/j¹ I¤Ë¥½üÚ-ªVÚ°ÛíÚDAoݺõhÔ°1FþÂê'JǪU«Ñ½[K_à9§ÈÓÞ|MK �N‘Þø ¾C¡¶› Â}÷qS "_9qâúôî‹íŸÃ¾}û´sÈ –.]†¾}Þ‚ÇãÑNI‹ð¥ˆxíѯ–ø.[DŒøL€×´[‚Å=÷VÁ† T<âácMCXް0DFFjgx•ÛíÆ„ñðùç£õùó”Ž9sæ"WîÜ0àmí”´ÜézìSÿN[DìNà[øíVæv»qðÀAìÛ¿GcŽ"æèQ;z§NÆù çqöì9œ?w‡‘9�¹¢r"<"Q9“~úÌìݳ.—Kó_…B\ÍšÏà“O?ÖÎðšM›6aÐÀ!ø”9ʤ—_y ݺ½®‘–D7P9˜¿³ûBª+�—¾óÿ:TOþñññؾm;¶lÙŠmÛ¶aïÞ°ÿ~8Î ¾Â)ŸöpæÌ¼ÿþ˜ùÓ¬@¸oœ¼`Ì—cQ¢x <Û¸‘vJjÂíÀH�ÙŽS�\ÀG�žÓl𧸳gñû†ß±nÝ:ü¾a#öîÝké7…2ǃ?ÎÀ‡|Ä]üBÐÀƒP¢d «>d­¡Sä‘0cÖfçEÔ�§È �Ý´Žï"‚;v`ÙÒåX±b%vîÜÉ>Q�صs Â]üB˜ÓéD—׺búô©(V¼¸vNjÞ­À¨ �I"Ý� Ô8¶¯y<lúcæÏ_€åË—ãØ±ãÚID”AçÏŸÇçŸÂ¤IßsX'œ9s/½ÜS§NFîܹµs®õ˜K¤¾Ã˜9Y}¿�I"Ï™ä¥ÿ ²ïŸ}˜5{æÏÇG}  ~ÆÈwÞʼn'´SÈBþÙûúôî‹Q£?·Ü6ë¼#"ó1YšVý:�¸Dê ð5,p÷7¸Ýn,Y²ßOš¬~ËeÍÁƒ1dðP¬Y“­Ë©Ä–.]† &¢}{˽_½¼hàÛ¬|²ß€D‘ | à·¨»xñ"&Ož‚ï'}Ï=¿‰T||¾úê+|ó¿o¹‹ÝÐïˆ{ï½wß]Q;å*,"SŒ1ñ™ý\¿ �"RÐ ÌÀrQ2ãâÅ‹˜4é{Œûö;œ9sF;‡ˆ²héÒe1üDGGk§P€p:èÞý Ìœù£ÕÞPÌ tðyf?Ñç[‹ˆÃLà_ËW\.ÆŸ€ÿ<]}ø1OþDêð¡Ãx©ÓËxµók<ùS¦9|o÷ë¯qÐ]D2ý ½Ï�7ð �¯>ÀÀŸÖ®ý 6Æ;#FòÄO 0jÔhÔ­[+V¬ÔΡ�¶hÑbÌ™;O;ã*Üægöó|z I¤“�¯úò¾r$:Cå ¢�·|ù¯>ü9|X;…‚Ĉa#ðÈÃ㦛 j§\&À›�~ÈÌçøl I¤ªÉÂ5 mßOúõë6àÉŸ(€E9‚W^y¯¼Ü™'òªØØX 2T;ãZU"Õ2ó >�D$Ê$¿ã?‡/^ßW<ˆvmÛcèÐá|ÒQ€JLLÄèÑ_ NúX¾l¹v©E‹cÑ¢ÅÚ×z33ì“àÒuÿ;}ñÚ¾2sæ,4jØ7þ¡BDY´bÅJÔ«×�Ÿ6 Ú9ä† ‚ØØXíŒ+ÕJÉð}Š^�\"ÏJò- áÂ… èÕ³7úöy‹ßõ¨#ÑÑxíÕ.x©ÓË8tðv…ˆS§Nã³ÏFig\ÉØîý`¯�"RT€¯¼ùš¾´gÏ<Û¨ æÌ™«BDY””„1_ŽE½:õ°dÉRí A?Lý{öìÑθL€æ"’¡ ¼6�ˆˆÍŒ`·E¦cÅŠ•hÕ² <¨BDY°nÝz4lø,>ùäSÄÇs¹Ÿt¸Ýn 6B;ãJQN iF>Ðk€3ùv¿ÿxëõ|EDðåcðÊËqîÜ9í"ʤ˜˜£èÚåuthÿöý³O;‡ëÖ­Çҥ˴3.3Àsù8¯ �"RÄ�üñZ¾är¹Ð§w_|úégðx<Ú9D” N§_}õ5êÔ®‹Å‹ÑÎ!ºÊ»ï¾§Ó©‘âÑ‘27ú ¯ �Nàc�y¼ñZ¾W;¿†Ù³³üèd"R²aÃ4jÔ}ø1âã3ýÌ"Ÿ;tð~š1S;#…±'?%0]Ù�œ"ÿ5@³ì¾Ž/ÅÆÅáùç:rc¢�sâÄ ôéÝíÚvÀÞ={µsˆÒ5vìX+­t‘tŸ¾›­@DÂ|–×ðµÓ§O£mëvؼy³v eËåÂÄ “P«fÌš5[;‡(CbbŽbæO–Y(êºÁsx²5�¸€·�Ü•×ð¥S§N£}»ç,u‹¥oݺõ¨_¿!† .hçeÊØ1_Yf@€¶éý~–€‘;�ôÎêçûÚéÓ§Ñ¡=OþDâĉx£Ç›|w?´è˜ˬ vz Îò�`†�ÏêçûÒùóçñüóyò' �.— 㾇Z5ë`þüÚ9DÙöÍ7ã¬r§YAðhZ¿™¥ Qän´Èz“ï8N¼ÞµvíÜ¥BD7°ñ÷x¶Q¼ûîû\î§ qðàA¬^½F;#E½´~#K€ ‘ÕÏõ%ǃžoöš5kµSˆ(ÿž<‰>½û¢mÛöؽ{·v‘×Mš8I;�`€†iý^¦OâN‘G�ÔÍV‘Œ|ç],\¸H;ƒˆÒàv»1~üÔz¦6fÍš ÑN"ò‰Õ«×Xb«yîH)›Úïeå»xKîø7sæ,L˜0Q;ƒˆÒ°qãx¶Q¼3b$Ο?¯CäSßOš¬�°õÓøõŒsŠÔPÍ+E^´yóf 0H;ƒˆRqêÔiôîÕmÛ´Ãßÿ­Cä73gÎBB‚%T•êû�2»0À !^uüøqt}íu$%%i§ÑD3š‰:µê`öì9\î§sîÜ9,[¶\;�‘¼×þ¢Éèg'‰<d€ß¼Û”=n·:<ß7ü®Bä5v»-Z¶ÀÖ-[°eËVíœ,s8p¹\ÚDªž~º¾ør´v�Ô 3æª7ÉexÀ�=¼ß“=_~1†' *+VÄ”©“Ñ¿?äÉsÝÀPxò'V­ZØØXí •ý�24�ˆHI�¼ž“ ߈/¿£AäùòæE¿~oaÚô©¨Tén�@XXšxQ€p:X¼h±v�<ví/dh�p]�Xæ«Ñùóçѳg/¸Ýní¢l±ÛíhÕº-Y„¶íÚÀfûÿ¿’‡eþÊQ6Ì™;O;�¸v[à~…‘Ü. £ïš2ïÝwßÇѣǴ3ˆ²¥bÅŠ0°ÿåïø¯e·§û$O" ›þØ„S§N£`ÁšQN  €)¿pÃ�'ð�Ë\Œ\¿~=~œþ£vQ–¥¶ÜŸ®�ǃµk-±CíU—n8� “ïZ2'>>o½ÕŸ·Q@²ÛíhÙªeªËý©á�@<V­\¥�<råÏÓý “$ò�€r>-Ê„¯¾úÑGŽhgeZå*•1p@”¯P>ßÃK�DÁcÍš5ðx<7ü}I€¯üyº€ hg•ïµ>Œo¿§A”)ùòæÅ«¯½ŠÖmZeú/¾+�DAãÔ©ÓØ±c*V¬¨Ö`€â"’ÛsH瀈䠹ÿÒÒ7rä{HLLÔΠʻݎ–-[dx¹?5¼@\V¯RD°q— ”æW7PÀM~Iºß~[‡%K–jgeH¥Ê•0p`T¨P![¯ãà%�¢ ò×–-Ú °åü¤3�ÐÞoE70z”%¶Q$JWÊr«Ö-½rýž—�ˆ‚Ë6 lí-7Z‘. –ÿ’Ò¶bÅJlÜø‡vQšl6êׯ‡Þ}z!þü^{Ý0�DAåß“'qüøq.\X3ãò;‘Sý ãš Ü=©|þÙ(í ¢4yk¹?5v/›mÛ¶«�æŠ Õw&™4žìo«V­Æ¶mÛ´3ˆ®“?~ 6S§NöÉÉà›�‰‚ÑömÛU/Àm" ¤² "‘.à?þÏºÞøïÆk']Åf³¡i³&èÞ£;òåõí™;�¢`sèÐ!í{PÀÖë¾Â¸ê�"ýßtµ½{öbíÚß´3ˆ.+_¡<èÊU*ûåx¼@|>¬�p+R�`‘åÿqßç–¿d yóäÁk]^óÚ»û3Š·Ÿ#‡õw³5ÉÀÕ—�Dĸ’ïÿWuþüy,˜?_;ƒBœÍfC“¦MУG7äË—ÏïÇw8Âü~L"ò­Ó§OãÂ… ˆŠŠRkààšÀ Ük€¢:IÿoѢňOÐΠæïåþÔð�Qp:räîºë.Í„ë�ÔÑi¹ÚìYs´(DåË—=ztC“¦MTÚp�¢`uìØqÕ@R»�ài…–«DÇÄ`ãÆÚbl67iŒ7Þ讲ܟîHœbccUo®]‘p×5 Ô°ä—%ðx<ÚBÊ•/‡ú£Ê=U´S®ÂÇ'í�×�.à�j9—¬\±R;BD¾¼yѽGw4m¦¿ÜŸnDœ,7��xR)䲋/â÷ß¹üO¾eŒAƒõѳWO,X@;'M\ N±gÔ€(àêàq¥ËÖ¬Y‹¤¤$í beÊ”ÁÀAPµê}Ú)7ÆÛ�‰‚‘V�"’¼Æ("6ðˆvÑ .ÿ“DFFâµ×^Eûífi+�DÁéâÅ‹Ú �á�€Dà;K³DD°jå*Í RÕž®†·û÷CÑ"E´S2…;'§Ó©��á�p�•´7Ýݹc'Ž?®\AÁ¤Xñâxûí·ðÔSêooÉGX`¬TQæ$Yc�H^ ŒvÉo¿­ÓN  ‘#G¼Ðñy¼ôR'DD¨ßØ’eÜ ˜(8Yá½n‰)€Jj¯�lÚ´I¹€‚ÁýÜAàŽÒwh§d/'+\0)—�(®""øóÏ?5(ÀÝ\¨zõé…ºu-±›µW„‡‡#ož<Ú¤$1) |&J0²È�‘r‘1¿fÈ‘ÃGpêÔiÍ Pv»-[µÀë¯wEîܹµs¼ªl¹²Xÿ;/…ª±c¾ÂÇ¢A>``´�À¤ �ªßflß±Cóð ÊW(Aƒ¢R¥»µSˆˆ2,GŽÚ àbÊ%€<šóÈî¿w+Mž<¹Ñ¥K´jÝ’÷ÊQÀ Ë¡ÿ_Ÿò&@ÕµÓ]»vižD láKD”žaú+�)+�P�öíÛ¯yx �¥Ë”Æ PõþªÚ)DDÙb…�� Ééºú™�~åv»­ux²¸ÈÈH¼úZgtèÐ>`¶ð%"JÞ€K—�rj;zÔ·DõÔ¨Q}ßê‹"EnÕN!"òšÜQQÚ .cŒÓ@õAèÖ<<YPÑbÅп¿€Ý—ˆ(=yóçÓN¸$/ý«�ÿþû¯æáÉBžáytîür@oáKD”ž|ùÔ€ @ò� zÕÉOjž,¢l¹²6l*V¬¨BDäSò«î½�Ç� ¬�œ<uJóð¤,22_íŒçŸŽ÷ôQHȧ?�Ä�ɀꞄgãâ4OŠü1 <ÅŠÕN!"ò›¼yóªß�GÀ‘�Ø5o®ºxñ¢âÑICÞ<yðÆ›=Ьy3í""¿Ó¾³I€h�på��¡¥~ýzèûVä×_#"ò;›Í†"EЍ6ˆUÞÏÇ]†‚‚ `ÐàA¨Q£ºv ‘šÂ…oVßÈ–r �Ê€@4O~P©ÒÝørÌ—Ü¿ŸˆB^±âŵà¹ô&@Õ“?`™ç"“9Šýûù¼"¢âÅ‹i' ìŠÀ£b €`÷ïÉ“h×¶=Þÿ$&&j穹í¶Û´âqé=�ê@k<‰|Ìãñà›ÿ}‹gŸm‚M›6i穨P¡‚vÂ.cŒ�l¢<�äÌ©ú,"ò³öþƒÖ­ÚbÀ€A8þ¼v‘ßcP¾ByÕ¶§üØ¡<�DFFjžˆ¦ý0 µkÖÁ¢E‹µsˆˆü¢D‰ȧ¿ ÐŽ”«_È'·æáIщÿÅë]»¡ÛëÝñïI>‚ˆ‚[åïþ‹ �7¼Ióðd .BÝZu1ãÇám¡DœªÜSE;.+ �… Ò<<YDÜÙ³èׯ?Ú·{Ž· QPzì±GµÂ})?Q�n*Ä�ú6l@ýz ñÉ'Ÿ"!»DQp(Z¬n¿ývíŒËw��É€;ö¹bÅø$8ºšÓéĘ/Ç¢Núøõ×Ú9DDÙöÄk'@€mWþÜ@õ^¬bÅŠñ9ð”ªè#GðòK¯ Ëk]qôè1í"¢,{ê©'µ�`Ý•?±c$)Å Gޏ¥pa­ÃS�øå—%¨]«6F §Ó©CD”)yóäÁ#<¬�«¯üIʳ�Î*„\vûê×EÈââã0êóÑx¶QlÜø‡vQ†Uÿo „…©ïz{6,•K�0À9ždåÊ•Ó<<={ö m›vèÓ»/N:­CDtCuêÔÖN�€uW¾¸4�ˆò @Ùre5OFD0kÖlÔ©UÓ~˜GõF"¢4,X�>ø€v�¬½ö—þ©:�Xaw$ <±qq0`fü4ƒ@¹òÁµ’”””„Çkg’¸³qÚ äõê×·ÊÝ×\û �œ"?hä÷œ+<ñøS8qâ„f0»ÝŽÖ­[¡ëë]+W.í¯Øºuš6i¦ADÙ0oÞ”.SZ;Ãå�òc®ºë/åM€‡‚®b‘% Pn·&LD­gjcöì9Ú9^áv¹´ˆ(ªÜSÅ '�øëÚ“?ðÿï8à÷œk<ôÐƒÚ þ=y½{õÁ ÏwÄÁƒµs²ÅåVÝ£‹ˆ²©i“ÆÚ )~MíSîØå×”Tp€¼iÍšµ¨W·>ÿlµs²ÄÅ�¢€•;wnÔª]K;#Å‚Ô~Ñ�`‹[®W¬xq-RD;ƒ‚HRRFþuêÔÇŠ+µs2Íåâ¦GDªq“ÆÈ™3§v�œu\³PŠä�cb�¨¿Ýø^ 8rø0^êô2^ïÚ ÇŽΖÂ./"›Í†Ö­Zjg¤øÅ“ên¿¶+~œê„àO[c«D R‹-FíZuñݸïà€ëëÐHD×{ò©'Q¼Dqí �€f¤õ{—�Ô×HŸ®öÂÃõ3(ˆ]¼x#G¾‡g5Á¦M›´sÒÅ÷�¦¶mÛh'¤ˆ·óÒúÍ+W�Ô€\¹ráÉ'ŸÐΠð÷ߣu«¶è÷ÖÛ8}Úš[ s� <å+”·Êƒ�`¡1&Í­þ/�aÉoŒõKR:j[cÏd "‚3~B­gjcÊä)–[r·ZÝØK/uÒN¸Ì�“ÓûýË€1Æ ¼ Zµ§‚f'7 qgÏbðà¡hܸ)6oÞ¬s™ËÉ�¢@rÛm·¡FêÚ)NÚ¹é}À•—� ÀRßöÜXxx8ž®ö”v… ];w%_è×ß—Ün�DäÅ;Âf³Ýøýcœ1&ÝMP®*õ�?û¶'cjñ2�)ñx<˜ñã Ô®YS¦LU}Ò w$ ÅŠGýõ´3Rˆøß>èª ˜¿ °ÏwMóØc"þüÚÂbãâ0xÐ4mÒ[þÒÙ'‹—�ˆÇk¯u†Ãá¸ñúÇŠcvßèƒR[«X胘L Cëì¡L!lûöíhÞ¼%úôî‹S§ü{Y€;†Ûn» õêÕÕθÌ�_eäã®�äoð—­ZXåÊâD³fÍFZu0uÊ~»,àV¼ü@D×­ûëV:_²33ò× �`,p;`Ñ"EPo$ ‰‹Ã AƒÑ¬i lÙ²ÕçÇã%�"ë«T¹þûßÚWš`ŒIÈÈ^7�c’°ÄÕ;th¯@tmÛ¶¡Eó–èß bc}7+»x\ˆHí��IDAT�‘¥cðv¿·`ŒÑNIátŸeôƒS½_Á–ÎÞÁþTõþªxà>&˜¬Çãñ`ú´éx¦FMLœ0É'›öp'@"kkب!*U®¤q™�S"9ÑOu�°‹aË��Ð¥Ë«Ú DiŠ;{Ç@óf-±uë6¯¾¶›O$²¬¨¨(tïÑM;ãJ"À{™ù„T�cL‚~ôNSöÜÿÀý\ ËÛ¶mš5mŽ>½ûzm!'W�ˆ,«Gn¸¹P!íŒ+Í7f{f>!Í-‹˜ýïxãîVºÆB”ª”»jת‹i?LËöÝÜ ÈšªÞ_-[µÔθŠ�#3û9i�`µ6€ÊU*[êK¢ôÄÆÆbÀ€AhÒ¤þúó¯,¿Ž‹—�ˆ,'22ǵҖ¿�°,‡1ë3ûIiþcD€ñÙkòžotGdd¤vQ†íؾ-[¶ÆÀƒ—éÏçÓ�‰¬§OŸ>(Y²¤vƵ2ýÝ?Î���à�–X‡¼å–[ЩӋÚD™âñxðÃÔi¨ùßZ˜>mz¦. p'@"k©_¯.š·h¦q­uaÆü’•OLw�0ÆDÃ";@Ç_@™2e´3ˆ2-66ýûÌÔenDd¥J•ÂÀÁµ3RóVV?1#1ÆdõŽ-,, ÃG ³Ò–‹D™’rY`ÐÀÁ7¼,ÀK�DÖ…Ñ£?GTT”vʵ憳<«Ÿ|ÃÀü`GVàm•*ÝvíÛige™ÇãÁÔ©? Ö3µñãôÓ¼,À€ˆôÙl6¼ÿÁ{¸£ôÚ)×r{€>Ùy�Æ1™Ü\À׺ví‚Ûï¸];ƒ([Μ9ƒ·ß€-Zaûöëoßå �‘¾î=ºáé§«ig\Ç�ãÂÉÖ7çºÁLàPväM‘‘øøã®B”m[þÚ‚¦Mšcð !W]à �‘® êãÅ;jg¤æ¢Èö24�cœ�>ÌîÁ¼é®»îBïÞ=µ3ˆ¼Âãñ`Ê”©¨yéÙ‡;)zè¡1tØ팴|dŒ‰Éî‹dx{=Éé¸)»õ¦®]^ÇâÅYº‚Ȳ*W©ŒsgÏaß>KìÅER*V¬ˆñÆYñM�ð¯(mŒ9›ÝÊÔþºN‘�g÷ Þtá´hÞ {öìÑN!"¢�W¼DqL™27ÝTP;%U<ŸÃ˜qÞx­L �"RÀ�Û÷–è#GФIsœ9sF;…ˆˆT‘"·bâÄñ(Z¬˜vJZV9€'1âËÔfÆÆ˜Ó�ÆzãÀÞT´X1|ðÁ{Ü€ˆˆ²¤h‘"˜0i‚•OþIàeoüL��àH¾%0Û×¼íÑÇEÿþokgQ€¹õÖ[0~Â8+ZT;%=d÷¶¿kez�0Æü à}oFxK‹–Íñꫵ3ˆˆ(@-V ãLJbÅ‹k§¤I€ƒ`„·_7KÏ3t�8êå¯èÒõ5´nÓZ;ƒˆˆ,®t™Ò˜2eJ”,¡’.ðª1æÿÚ»Ó ¹ªóãÏ;ÝÓ9;Xl¢H"DƒÅ¢ Âfƒ$Œ…Œ*`Š­ ¦à"ÈaÇo`‰ /…m,;Äbh L Q‰Ù؈ĉ€Ä@!V—FBÓÓ='F+)3šî>Ý=ÏïÓ¨n÷¹ÿéQÍûι÷œ»¹ãŽ\DlNðég©™k®¹ŠÙ³OÊC’Ô¤;l*÷Þ{{íµWî(;•àþbÄcõ{D«�¶—R*Tà'Àk˜§fªÕ*W\~%?þ·¹£H’šÈœ9³ùÂ-Ÿo…ÝdQ„?Žˆ×ë1ø.Í��DD¸¨Ù‰µT(XrÛbN9å#¹£H’šÄ9çœÍ’Û·Bñ'àâzÿmãN9¥{άE˜z¨V«\õWÓÝýhî(’¤LJ¥7ÝüiæÏ?5w”a øf1âü:ŸctRJ{V`-°g òÔEJ‰Å‹—ð¯ß;Š$©ÁöÙgoî¸ãv>pÈrG–€/À¡µØîwgvùÀˆx+àŠZ„©—ˆ`Ñ¢+¹öÚkèèõ·,IjGy$}÷¡–)þÀ@‚óê]ü¡3�CúSêšþ‚ûòåÝ\Ý ôõõåŽ"Iª“B¡ÀÂ…±ðâ‹Zm—ØÏvF4dW»š5�)¥‰øà·j5f½<ÿÜó\rÉ¥¼újSne I…‰÷åÖ[oáð#Ïe¤ÖaFD”q²šÍ‡GÄ« ®¬Õxõ4åà),{`‡zHî(’¤:ùä¹<üÈíXü7Váã*þPÃ�€”RTàqàÄZŽ[/år™[oý"÷|ç^RjÊÕŒ’¤aØs®¿ñzN<ñù£ìŠ€yň†.W«i�RšTŸ»×zìzY±âI®½úZþ{ãÆÜQ$I#4þ©\uÍU¼w÷–);ïtcgÄÍ>iÍ�€JJ§'XV±ëåµ×^gÑ¢OñÓŸü4wIÒ0pà\ýu̘1=w”Ñx¤ókù˜ßáªK�PI鎟¬×øõRâÁä–[¾ÈæÍ5î‚$©ÆÏv>^ø J¥Rî8£ñ¯E˜Y¦ŸëÖ�¤”Jø;`Z½ÎQ/ë_y…ë®»Õ«”;Š$i›B¡À©óOå²Ë>Ùôñ†ž˜6.â…\êÖ��¤”~§Ï�êyžzH)ÑÝý(K߯o¼‘;Ž$iÇW\q9þÞ¹£ÔÂ@ÀübÄòœ!êÚ��ô§4xŒ.9l¤-[¶ðÕ¯ÞÅÒo.¥\nØê I0kÖL.¹ôb¦Nš;JÍ$¸¬qGîuo��úSº ¸¡窗WÖ­ãÎ;¿Lw÷£T«ÕÜq$©mEÇó',\xQ;îײ¸3bQîР ¥ÔQG98_=½üòË|éÎ/óýï?ÎÀÀ@î8’Ô6J¥sçÎáÜsÏaÊÁSrÇ©¹÷uÂYÑÅ£! �@Ji· üh™'2ìÌK/¾ÄÒo}›îåÝlݺ5wIjY{N˜Àé?3Î8ƒ ÞŸ;N½,/‚ˆèÏdHÃ�€”ÒoWàÇÀ¾<o=mذû—=À}÷-ófAI¦B¡ÀÑGÅi Nã¸ãŽ¥X,æŽTOOá£ÑT-6´�(§tXÀJ`·FŸ»žªÕ*«W¯æ‘‡—óÄ?pV@’ÞÅAMfîÉs™7ï£ì½÷Þ¹ã4“ۊÓm.Óð� ?¥\0.Çùë­··—+žä©•O±jÕ*zz6åŽ$IÙLž<™“fŸÈœ9³ÙÿýsÇi¤ïá̈hÊçÏgi��*)ÍKðÐÖó>Õj•^x•+Ÿbå“O±víÚÜ‘$©®ººº˜:õƒÌ˜9ƒ}脱VôHðíN¸ "*¹³ìH¶� œÒù_£E÷Øëþk?\µŠgŸYÃ3kžeýúõ¹#IÒ¨ttt0ù ÉÌš9“3gpØaSéêêÊ+§/ášûûDÖ� œÒy_ ¹³äðæ›oòÌ3kxvͳ¼ôÒKlêí¥Ü×Gïæ-”·n¥¯ïW3G›·l¡RiÚfRÒ²ßĉLŸ9ƒY³f2}ú4öØcÜ‘šAuF,Éd8²7��•”ÎLð-Úür@­õôl"ñë æ@µÊæÞÞ¾÷í­[ÿß 7nìÙéñr¹\ó›7õôR}šæ-[¶ÐoUSã»ÆS*uæŽÑ0»í¾;Mñk³î:;;?~ü»Ûo¿‰Lš4©Á‰š^_À'Š“;Èp5ÍÿäJJ Ü Œß&’¤v°>Á‚RDK=A®i�€JJ'o»1pL_<’$µŒUÛ6øy=w‘jª›ïŠ| p½$©©ÜU„ã[±øC“Í� éOéXເw•H’šM_‚‹Kwç2MÙ��lMé÷ ƒúƒÜY$IHðÀ饈§sg­¦º°½®ˆŸa:ðdî,’$vÂÔv(þÐÄ �@Dl(ÂI_ÊE’4fõœÝñ§±!w˜ZiÚK�ïTNé²€%ŒÑ ƒ$IY¬®ÂY]/çRkM=°½RÄíó€_äÎ"Ij{ýÀÕE8ª‹?´Ð À”Ò~øplî,’¤¶ô .(E¬É¤žZf`HD¬/Âñ þœÁM’¤Zx¸©ÓÚ½øC Î�l¯œÒ‘po‚sg‘$µ´•U¸¨+âßri”–›Ø^)âéLMp_î,’¤–ôFÀ¹Ç¥â->°½JJg'ø+àý¹³H’š^ ¸»�ŸŠˆ_æ“CÛ4��)¥=ªðùæÎ"IjZ?.ïŒø‡ÜArj«`H%¥�·'8 wIRÓøyÀUňïåÒ Ú²�H)•úaaÀg€ßÌG’”Íà–"üuDôåÓ,Ú¶òvJ¿[€ÏœN‹ßô(I‘2pg>ÓN[øÖJÛ7�CúRúøXÀú¾%i *'¸�nîŠx1w˜f5æ a9¥iŸNÈE’TS倥øËˆx%w˜f7æ€!ý)|8:wIÒ¨l¾Q„[#b}î0­bÌ6��)¥¨ÂÜ‹€crç‘$È/»Šp[D¼•;L«Ó ÀöÊ)\ð1 ˜;$i‡žNð•NX[s‡iU6�ïRÚ§ç¦Á%„“rç‘$Ð,nïŒX•;L;°Ø”R± §$8˜tfŽ$IcÑÏ,í„¥.å«-€aH)íÙgœ™;$µ³€·-ã»o\ÄÚÜyÚ• À¥”&Uà4÷˜ŽŸ¡$Vxè€åã"žËh,°xBJiß~˜0ø0ð¾Ü™$©E<ð`EžŠˆ¹56�5’R*V/ËàÞ³€Ý²†’¤æ0�<ð#àï °ÂõúùÙ�ÔIJ©Ø‡GtÀá Æ%†’ÚW9`]‚ÿ xa�þ9`mþ)"zr‡ÓÿeÐ@)¥®~˜ÒSüQ‚ýö`ð!E`÷m/ð€4ø$áïß™¤úª�›l xè6±í뀞=À[ Þ耷à­NX¼³k,&-*¥4èÚÁáB߯š‰_Ó1x¬ðnÇbpÌñ;zkÀ{Gt;ð@iWßÿ.Þ0®†ãíì3Ý]±ãÏrW”Òàg¨ƒ[Ô–‡ûú4ü×oap]üÐûzþ¡w èêvÇ·_>7௷@ï¸Áâ¾ èˆ~$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I-é�»t•ôAïS����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon-white.svg���������������������������������������������������0000664�0000000�0000000�00000013443�15003540030�0022043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="512" height="512" viewBox="0 0 135.46667 135.46667" version="1.1" id="svg8" inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" sodipodi:docname="icon-white.svg" inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" inkscape:export-xdpi="96" inkscape:export-ydpi="96" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs2" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.9899495" inkscape:cx="288.90363" inkscape:cy="223.24371" inkscape:document-units="px" inkscape:current-layer="g3307" showgrid="false" inkscape:window-width="1600" inkscape:window-height="847" inkscape:window-x="0" inkscape:window-y="204" inkscape:window-maximized="1" units="px" inkscape:pagecheckerboard="0" showguides="true" inkscape:guide-bbox="true" /> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-284.30001)"> <g id="g3307" transform="matrix(10.756234,0,0,10.756234,-0.76070676,-2776.4763)"> <path style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.268079px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 2.4759494,290.29453 0.8664313,-2.29552 2.4071989,-0.53291 2.2502482,-1.18347 2.3403932,1.08878 0.37675,2.31748 1.522178,1.17132 0.358073,1.65327 -0.625514,1.50821 -1.245702,0.79112 -8.5513735,0.0878 -1.43716703,-1.05543 -0.29172201,-1.59057 0.73309294,-1.45246 z" id="path1481" sodipodi:nodetypes="ccccccccccccccc" inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" inkscape:export-xdpi="96" inkscape:export-ydpi="96" /> <path style="fill:#fbffff;fill-opacity:1;stroke:none;stroke-width:0.0245664;stroke-opacity:1" d="m 2.4221019,295.34968 c -1.0878659,-0.15882 -1.97427807,-0.9635 -2.26698456,-2.05789 -0.05691855,-0.21285 -0.06357126,-0.28265 -0.06357126,-0.66784 0,-0.38518 0.0066228,-0.45498 0.06357126,-0.6678 0.13455437,-0.50309 0.37363404,-0.92281 0.72738412,-1.27693 0.29660044,-0.29693 0.71775704,-0.56133 1.05540334,-0.66262 l 0.094333,-0.0281 0.014889,-0.2737 c 0.065279,-1.20079 0.888671,-2.19921 2.0751826,-2.51629 0.2100531,-0.0562 0.2855258,-0.0637 0.6548195,-0.0645 0.2356099,-5.9e-4 0.4653373,0.0114 0.5274596,0.027 0.1100438,0.0281 0.1100666,0.0281 0.1816696,-0.0629 0.1425139,-0.18079 0.4653264,-0.4742 0.6796678,-0.6177 0.5039623,-0.33746 1.0157321,-0.5065 1.6217201,-0.53562 0.538326,-0.0256 1.0248294,0.0746 1.5082517,0.31109 0.665074,0.32548 1.1364593,0.79739 1.4616053,1.46319 0.233795,0.47874 0.335401,0.96701 0.311946,1.49906 l -0.01218,0.27678 0.199164,0.13128 c 0.109539,0.0722 0.316714,0.24806 0.460388,0.39074 0.813111,0.8075 1.113884,1.93261 0.816781,3.05541 -0.121193,0.45797 -0.457432,1.0356 -0.805977,1.3845 -0.333016,0.33338 -0.91483,0.67934 -1.34009,0.79684 -0.4502688,0.12445 -0.3942394,0.12289 -4.2354147,0.11981 -1.971615,-0.002 -3.6501125,-0.0125 -3.7299933,-0.0242 z m 7.6451481,-0.82834 c 0.545805,-0.1461 1.0314,-0.47864 1.358863,-0.93055 0.13507,-0.18643 0.302726,-0.55751 0.365299,-0.80853 0.07721,-0.30967 0.07737,-0.79285 3.72e-4,-1.10158 -0.185769,-0.74499 -0.759998,-1.39061 -1.459688,-1.64119 -0.139897,-0.0501 -0.212904,-0.0884 -0.205441,-0.1079 0.230936,-0.60245 0.205529,-1.2656 -0.07086,-1.84969 -0.2420436,-0.51155 -0.6116411,-0.88174 -1.1201832,-1.12201 -0.3646743,-0.1723 -0.5814957,-0.2198 -1.0034057,-0.2198 -0.291794,0 -0.3936692,0.0101 -0.5647214,0.0557 -0.6755653,0.17987 -1.2418071,0.63564 -1.5361388,1.23642 l -0.068336,0.13949 -0.1123702,-0.0569 c -0.2090683,-0.10576 -0.5171066,-0.18751 -0.7629941,-0.20252 -0.8518289,-0.052 -1.6804874,0.51274 -1.9528265,1.33076 -0.1542208,0.46323 -0.1386557,0.90554 0.048962,1.39126 0.012582,0.0324 -0.00809,0.0365 -0.1432276,0.0278 -0.1904101,-0.0122 -0.5000142,0.0466 -0.7431816,0.14113 -0.4999194,0.19422 -0.9533668,0.67125 -1.12307682,1.18155 -0.15734055,0.47307 -0.14715788,0.90357 0.032344,1.36764 0.1940906,0.50172 0.6700544,0.95419 1.1841482,1.12568 0.1034943,0.0344 0.2471302,0.0725 0.3191908,0.0848 0.07415,0.0126 1.6976013,0.0205 3.7403281,0.0181 l 3.6093097,-0.004 z m -5.2655768,-1.2975 C 4.2750097,292.90743 3.844102,292.6375 3.844102,292.62395 c 0,-0.0137 0.4309077,-0.28347 0.9575712,-0.59989 l 0.9575723,-0.5752 0.00674,0.39089 0.00674,0.39094 h 1.5707862 1.5707918 v 0.39326 0.39327 H 7.34349 5.772697 l -0.00674,0.39094 -0.00674,0.39093 z m 2.1483996,-2.17014 v -0.39612 H 5.3786739 3.807272 v -0.39326 -0.39328 h 1.570792 1.5707932 l 0.00674,-0.39093 0.00674,-0.39093 0.9575575,0.57524 c 0.5266596,0.3164 0.9606572,0.58453 0.9644402,0.59591 0.00375,0.0115 -0.4050204,0.26764 -0.908454,0.56956 -0.5034315,0.30187 -0.940193,0.56487 -0.970577,0.58441 l -0.055248,0.0354 z" id="path826" inkscape:connector-curvature="0" /> </g> </g> </svg> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/icon.png���������������������������������������������������������0000664�0000000�0000000�00000017144�15003540030�0020714�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ôxÔú���tEXtSoftware�Adobe ImageReadyqÉe<��IDATxÚìݽväFv�àšÑ›-ýƒ}Q™³2;•ÙѶž`©'”Ù¥'àl挣Ð9‘CR™³éÉìh¨lÉ]KpEQüíàVÕ÷S§¹žs,4€Æ½uë)������������������������������������������������������������������������������������������������������������������������������lÇ §�(ÔÞªí÷ãç«Uëîø÷u]¬Úåø÷rÕ>ŒŸÝñï �Ø¢ý1¨çÏOÇÀÞ;Ƴ1øqL –ã'H��žìs{=ý¾ðïs6&׉¤� �Ààû1à÷ |ßË1 x7&gn$�@+=üè¿h$à?ÅÛ1!ÈŸK§€Z¬ÚѪ½_µŸµÛûñ\IŽ�(6è¯ÚGA}íöq<‡n'�"Ûôw^Øw›A·j‡IyÊv>žó=·�SëWíD0ž½'ó�رÜã\è퇭 ,Ü¢�l;ðÉØ~)s†dx�€ téªÄ,°–¹‚@"�€À/�€»í ü�Ú üC2ÆßJ"pè–`!ð7;YЃ� êÓÕÒ1Á°ívš®æ|�P9ãüÚ]mHæ�T+ý*÷k ô~&�õèÒU©WÓžÒNT�ôúµvW ˜$ ×¯©�ÝB¯_Kæ�4à m×+¨È §�ª°Ÿ®ÊµSÁ]¬Úç«véT”ï¥S�Å[$º0]¢iH� �%Í�kùÄ)€"åñþÿZµr*˜I®t«önÕþêt”Ç�(O.Þ&˳ˆ!Ï ørÕ–NEYÌ€²,&¤çã'1�eÿ<æÿ{§‚`ò=ù/«ö¿cE€€2 �;?súéÆßÏ]¶¶w£Gû‡÷Nñ£¾Zµ7Nƒ�ü·ír êyòÙrlëùu]'ÝØ^ÿÛ°Ì/¾MV �lhòæ>­/7ËãËGéêå4]àëÕÇx4së×íØO`½àßjy#àï~ ¯‚÷’��ÿ»{ù‡©îÝ »ñ;¶vm%�‚sA_2 �üÓÕkŠsIÜšñ_ìç¤öW8K� þyó¢…Ëü¨Åx®$�¨u¶ÿ±ÞþÚUã$ �¨Zúü:—vc]¥÷ÇàÒ‚¿ÀO›‰ÀÂeZµøi<�‚*wrŸ1þé]¿º†U!î ©‡wéK¾òÎv.åìRù» ~T=Z°WAð’ßD»§†Tþ¦Pî) j%¯õ?OʵÑ+K%ß_–NÌ뀩¡÷³Ÿ~ýþöOoô&6yMërl×ÿ>K¿¼’ö9J~­¯×»–#_§oÜg@M¾9F™�õ~<–£1¸÷÷ÿB¯Õ€'µÞ僶å‡@~YÊI*s’Óù¤ OÔ*qÜÿ(—-=i>JeN tßAc=–!Õ½zI`3üëQbzî²Aݽ“Ü;>Nõ¿MÉŸ viCƒËõýZ_|SÃn~J¯uÿþJÛE°wÙ l‚¾Þq Ý—ï%¥Pžë×™*ïÛ˜Iy)÷ç‰Ëå<XJÞŒ¤µÉ~ÆûÛNÒKIÐMJ… ®·"ÕÛü‘X èR}ï(ü‘ �þ*—ùunaî¨â•0t×»T ðkÞ¸F{I€U0áÄ-E5ežÿ[>0¸L0!™Ü'øÓ’æt.ìNŸÊ| &øSpêÁöåÌÚ®}õ´Þ-ÍI€½ ‡I¹ßð‹EŠ=!ØB¯ßkxëjGnk¶d|Ÿº< ×¯Ù0…݉ºü×°†=½~kýáÏ‹¨{ .<Ý^¿ÿðLQW¨ÀÙÐǤ?ؤó  �–ð”üëmÇnqîD¨@a¥;͸?ev&"ÎP€[dõ͸?st*T ÷‰SP}©îßœ†ª}»jÿá40±ÿYµ)ÖN“¿_µÿ[µ3—‡–å,Øk{Û(ýÜ¢ ØæƒôwzkJÿÔ!âPÀÂeAð×lõ »7¨ŒÁü™¸àßF{ŸLv"VÇ#Ú«ÃUÇh*ø[ægØK´ ‚ì‹ñ/œ‚*‚ÿ©ᯜŸïÆÏËU»xæÿ~ü|•®Þ–xÝ"|·Ï]b:M±VüÃøÛG øWh9Äãçrl»>çûc2ðz†ÞçÉ2'â>"¿½jß¹,Ôúck­ìŸ.yòÛA°¤'_‹üZ哤¬IÛŽ“É€°S­Ìöÿ8ÕEaUŽƒ1QÙöĨέOp]2ÿ {ºUj¶‘ èý£ `¹,«5øŸØÓ_§2°î0Þ?ª�vDf]]o¿µ2]~@éés8ôþñ¬Z¿¸”.â;¸7Û’¥‹i¬z<6< ÷*€á3›‰À‰Sƒ*ÀÆÏ(RÄ—müÓ%½S‚gׯÍïˆâì¥ò×ú ükÉû œ: î4Y �EÿxÖi§É\hÝ"Y �ÏVꤿcï ¥8UÌÎ¥ ÑÞ¬õœ^¿±3£cBx%Žûëõ÷‰2ЊÂ+mÜÿ<ëeSë0•7à”g[ïRQ—Ê)ýçã\¸dÀ3žož]ƒKAD¥”þsðWòž+Â0€½5§”ÒþÛÔ(õ9g[`BéR¥ÿSÁØðYáY¦‚I'Éd?  ½ùrжpˆ ü†DØÈ{ þÀÄ"ìrj" ³ 1{Ɇ@ø„žø'ø»a9 ‰€+/‚Y|“âΨ?[µ¯\"`‡Ï˜¹u.sÝxÖù­Š0`p˜ÃqŠ»ÃŸà´Ð òf@ôþ“í}éÍ=ÊJ�ôþ“1€éEx÷ 4ßû7ã˜Z„ šò´ `:ß<¦‹dÆ?0½CóÞ€iäLsì˜.`&K§@ЊÀÇôíX�˜Z„gOï20…h»þ™ ÌÍ^�*�Õ[¤X“M”þÎfþï¿’�°kv<¹ô¿tY€Æu�vi?Åšiš3îï\ �s$�zÿúÚ%‚øiæÿ¾}�܃;uèXÞȸþÎöçìÌ"yÑÀC$Û«�Té‹@Çò}ºšý…gUÚÓûxP¯ P£Hcÿzÿ�H�&¥üŸ¿e�H�&°¨ ÷€`"‘Êÿzÿ�H�&ò:Èq¼Ñû'€÷É[×€FDyóŸM.ˆàú~<Iö^ç×úd�ÙüÏ] ‚%�×KR§ @ †�¶+Òä?ˆ&Oý&�*t¤`ã"V�n7Ã*�*�4ñ°›ª» ô›0, �àfÞR;p)(0)6,Ð/š™9�ÛaÖ}^ö÷Ö¥ @]ºB3,à™‰ 8ÖÿŸ¹ TÐ+Ì«X§ÏK €löé~p¨€ÕmxåPËËìø­mÜ׆ê4÷ª©S�jéý_$[ÿR'Ãuš;©»�° }€c8s¨˜a À¶ý$`"Œe½sh$hX- øoCóS @=7³ �-1,à™¹© —m˜û €ï]‚šêþïê¢ ÉSU�*1÷ì{½ZïM(K„aÓ¥€MEèy|pÀ°@A"ô¾såháR°éCgîRVï2ÔœÃb~î‹§î`[bÖ2¸;—z›¢–�\·£ÔØfj†�ê°t àN†â‰ÚÛ>ïoTåÉN’í,!jÀ°€g溕#[«ó(ûYC €a>¦ø ÀÏãqV] 0P>›YÀó˜Ï~A=ë½1Ylnn�åd³b¨�(Åa!½ÿ»î“êV ¨�l'Kœ“7�Âúºd¡)½.ø>9ÓÃÑ{Áïc{c¾ƒKæ~H&"€&ø†vã Õ‘�T3$`�à׺dX`¾¨ðY¸¬z8*�POÀ°Àn|LõT�LÄF�•'�†¶£¦òÿ]í88/ÀÀæe  ßºaõ}Qù÷[Œ÷GQIÀ ÷åVz8sú,ÅÞ èg·•ÉKo¿OJ¿Ïñ1µ1s~¹j_¦B6hS(Ÿå(0ýoî›dXà9½ãVžS]*èõÂ�€Íö†ö§ÄÓ’C �Àf¼[àá`Ø7ú½Ã/”�lvN Ü'Ï—ÈsÍò¸øwéjŒ¼%ÇâD}A?Òû¬£?l~Ö´›aûåññ£1Yjå~úMnB!h­6›=-8Nõn$ pcJ�4­áfXàé­sI�n§·# €¦¨°ó% `ÝØ“.± u*Ð4ÞËEÝû.oŒ¬ôLó\ i†*¯ÌÖ–Hþ­5 €¦”—¸öÿÆ-r&é!¯i†<Çï¯àÚÎ}‚̱æI%�M3,ИÜñ9­àºÏ6«…�‡ñ‡µ¨ü‡�”ß³õn§ËoÜû<]í2xYð÷èÇ*[tÚÙi*òJ�=;M3,0E•÷¨ðk>y'µÆ @7fÐ-eÑ*�P_@ãéràë±"°,ô;{–oæ0µ±¥ä]-j²£7§iϯèuçMV>JþÖëŸ6þÐX½6èšöôI€ç[ÓÚ!<wéôúŸÛ¢N"ñ`×´§½ÓCÏo7Õ€;‡G.ÝÃ:½þßô$�š¦ÜÏo ÞªA÷8Ðë/fC ×EÓ”û œPâ*€\9Q.»÷bûvÕ>[µ·NÅ¤ÎÆó~QÈññdÔþ®è'¸.š¦Ü-–”4||ØúÛOJþ¥.tM4å~åþˆJÙ~g/ *a`‘¼0á9<h åþ¸¾¯O ‹&· .}kGÃ�®‡¦ÜOüN¦¡€€IùÃ�®‡¦Ü$ ðP@´ào²_=›H¸šÍ|Ôÿâ7Á?P¦(Ð4å~êKª¬0™é_ç»\ M¹I@À ‚¢¬¸~™òÙöüÉ)€1»¿^oÒÕ È•ò­L|!ø‡t6~¾?ó»®/žx.óy|=þï|/Nf®�ÕöÛô»jÃQŠ=óþ¥ß‡­—ýs 1¯vÒÕ6¾ß&�Êý”*òj´¢'¶üÏǬò@Å €Ùý!òÄô¾Äº×HðÏß1¿Ìaá‚Àì~Š´7V€T¶t2k_êw¬\Å&�Êýܹb½üc”÷õô¡ì`ðæÖ"Ô¸½ïñ˜å&�Êý<EÔ÷Ó,œ¸iÇöõ ü@¹Ÿç:Oª�ϲøA”û)_—bÎYØøA”û©GÄù�áª�5,÷;ø¡Š@¹Ÿm:QxXÉ3þO“É}PK0Häi ƒf_€R'ýå zèÞ†*�å~v)âP@ï¤xX@Ë €r?S‰6p¬,¢×­&�CRîg:]À˜7[Gö´°àŸç)ë‡ò�<æ2¤xK\'w˜Ê›á”�(÷A¤M¾$0bä¡’ÿÂý Å'�¹§£ÜOÑæ¾Mš—RúÏÁ_ÉÊN�”û‰(R<™êK—Rú?×[€¢�å~"ëSc“»TFéÿTð‡¢€ÁoU€gµ¯n‹¸¢É~PO ÜOIº«êÝL¹Cð‡z�å~Ju(îlÎ[¤e‚?Ô“� I¹ŸrEêíâ ‚?°å@¹ŸZD™ °õ=¢o÷+øCY €r?ª�… Dßû:D@¹Ÿ¦E&ßÚj€.Yçlž�(÷S»Eªl5@ÔÞÿGÁŠH�”ûiE¤áòãcÔÞ¿í}¡Œ`¨Ó˜(æE­½ÿ…{ B'�Êý´ªOLŽÚû7ãâR?ÖÖû?w_Qq¯á½Ó�URÁË#öþûS£ü[»ù~  Žßu±Ë£d/“¾åfè%Üž1 Ôá<@Ü<yîAGÜõÏf?Ô¤O÷õ$øÅÍX¤x¥ÿνDºôøë´:쉡ϊŸçIévÑxJe ¨G„Õ�O^•%cQú§ý3@="¬¦;*é`wöF#˜P—/÷K� n¥t¤£Mþ³á¥6ø-õØ OµH^ô›èÓæc~@]"Ì«ëò¼|à ¿t¾_µK÷ …È?®\î·ÿ=pÛYgTø2…Þ?¥Òv‡Î€º,ÄÕüœJ¿»ç�#½¼CïŸôéjžŠ?½ðê¡\g¶²Þ?-êvü{ê3÷û{WD*ÿî&ø!õ9MAW,%�zÿDÔ§évôês!¸kÀë 'èM2öO,]2»ØÜ‡ ™ßˆ²ù]ÿˆd˜é·Ô§c“�DÙûÿÜýA êœ/ð�êa®Ýp{ Êò¿ïÝ̬KÊýÀn„ÞŽ03Ñä?æ6¤8Ca@掷Ƿ7꜔7Éä?æ‘ï›ù�-è^ þÙ® Sÿ’r?0­‹¹àf ¬ûÜóë¾`Bêý9v¦õS¤ Âúÿ3÷é“r?Ðð3ðe° €ò?»–¾r?0·‹(eÿeXviHqf÷[�÷Àç~¾ü.Pï?gCfÿ³«šr?À /o< çvær°e9à+÷Üáºð*À±¼s9Ø¢!™Ýðh¡w¤À6ôI¹àQQ†�–Éø?›É_¹à€%R½61¤zËýV´í…SÀ.€+�>¸¬¡OÊý�ky©@rÀW°t)x¢!™Ý°•  �é“r?ÀÖä!€Og>†3—䀯ܰƒ €R*Q I¹¨ÓìϵC�îné“r?P·Ùçß½ p?¹倯Ü0Aç;€�Ɇ¤Ü´ãsÇ^C�Ì­OÊý@{B ÀrÀWî˜ÇòwÎ3’r?ж~æÿþ �SßðÊý@ëBt~ 0…ð•û®DØ‚ÿL€]’r?ÀíN‘ @² °‚?À/> p �Á¡þ À“w>�\‹0pi�SX®Úç«öeòæG€¹€¿uÈ$�Léíª}¶jß:@£ºd^ �sÜxC2,�èýÏåG �sZ&Ã@{^8†‹( À¾û¡i†€–ôA:`K�æ^†g�†€ìéôþ½àm|D‘³RÀÞÿîü½“aàµ{‚[ �5Šï–7�;ñ‘a 6ŽáÝÍàÇ™¦wOðH¶jX�(]—b¼àâfåÄÀC �zÿ›¹¼�D˜(à©7ï �åùS¤Þÿua@ïÞà–ɰ�PŽÜɰüïÝí Bà•ûƒ5�Jpä8Îîú?þ<s{ïþ` öi€{yÛ (ßyäçI„§ÙV¦ý^�±äYrrûÀ®W,œ¤Þ}Â�"ùSãxwß? ²“#÷ [Ö¥ò‡€²} ò,éê}Ï}pçîv¤äa \‹ Ï‘çÙí9Hó�Ø•½ •. �´#JòÑ {„ÒÂýÂŽu©¬a Lûž#ýc{’ÎR„)eX�(ÓqgÈǧìa) [R°�Pž.Ð3äø)Ü9Ø÷3üX£ �zÿ“ÄÔb²؈ÀÞÿ$Uõ(½ «˜K´a@ï’ýu¢<øî!dñb@ïÝö¬7FY¶`S ¢˜{X�ÐûŸ,ŽFÙ¶pß½Ds �eˆ´î?·Ã’3“‰¦KÓ �eˆ¶’h­¹t‹Ò¿�ìØ”Ã@Ï„HÁíô^ /1¸¯jªa þ³ Úòá~“/t’â¬aT ².í¶ôÄ6EG`ÒIô U�x–] �qE›ø·•eô‘†T(Å.†€¸Îƒÿ÷Ûúb'I�ÖÑ¥í �1m;Ù+ª�°‘m �ñD,ýo=N~L,k€m:,�ÄûMG+ýï¤R~ì ÚRui½a –£zÿ˧î= ÷Üa Öï÷çÔ@ïÿZ´RÇ¡{Â=gX�ˆa?ÅŸdŽÜ"à—íÜ‹T ßÇ �1’öˆãþ“¬’‹–õ  & �ó‹´,~òrCÀ/n(€Úzƒ�‰ÿ&ƒ{)fæcU�µéÒ¯‡€ù,ÿ÷Sžˆã€'àÜýI¥¦þ¿q³Ÿ›í`êžIÄ“`ƒ �¶ü#ÎøŸuÜqГ±p¿Ð@ðÿ9Í´.jÀ|��6µW@ðæ<AQ«�^À&Á?êZÿ›ÿfs]à“s. � Âà?ùÄ¿û%“#�(_ cþ¹DÊ–"Ÿ0+�¨%ø‡â‚Ÿ0I��¥ÿ0¥ÿÛÞ'I��eYøC•þoë 8y’��® ÿÙgý?æ$I�ˆm/Å]Æ~_룟Ô.•1Žrš,hQï/a™_˜ žã°jŸ�€¶¤r&ûû¢»ÓBN¬mƒê—;{‘÷¬y(Fu¥ì® ,+çÂï J%–ü‹÷¿Ïaa'Úä@€º …þ¢ÆýïsšÊk1$� ×o½ÿ†Jxâ]C‡~?�EÆœ’{ýÕMP?(ô"äêEç÷PL¬‰¾#m•“þsTðÅP �ˆk?•7ÜÜܪ´’/Ði27� ’ÜS.m7¿‡Ú¢æ‹Uâ|€»V Ø<@àü×(ÕÔP¦$�ÿÚQKqQÉE“�LÓq¬1ð7»ÿÌQEP"�°›Îb “ûÿ;WzAMX¿·”ÊŸ/&ø?Áy¥÷|Ì^U�Ö¥«åÖµÆÁÿ{ \ô|±\j€fƒ¾àßpp=WàDe�hô9Ÿ;B¹¼_ún}‚ÿ–í§úÇ|î&8 ¶þuÀo­—_dð 8m8.WíbÕ~\µ³ñ/=G€à=ûý1à:þ½¯Só+ß­Ú×�IÀ:rRp9~þtãﻜ9]Àü±ûÿ{§íQ_­Ú›ôEã�P²Ë±×ÿ¦”~èX®w~²¦€Ò‚ÿçéþJ­à ö’·ðPŽô¿LÎßz)‹€µ¼cֲăð˜rðY*h€æ|;öü/KýŸ>¶ÒÕÀ?ºÏ�ÔIýç:©Ÿ?¾ÿ\µɶº�Ì/OW3Lý²€cÌYÖg©à2 �ÅûvŒEËZ¾Ð‹‚Ž5ä}õ{÷!�É?oîsVÛû¤ cýëªý%™�À4ò,ÿ<Þÿß5~¹…w?VìÀ¶]޽þ·5É»!�vÑëÿ*50ïìeÁÇ~½iÐ×ÉA�6³LWëú‹^ÛßJà¦.]½G@5�€çÊ3ü¿k­3ùI%ß#_´<A0¿:7Oü½û€Gœ¥«Jr.ûÿµµ/ÿ¢ÂGÉæA�Üm™®†ß¶|^TüÝút5,й×HWÕâëró>©ø»å ïû1Éɯ6,�Ðnàÿ÷Uû×Tá†>*�ËK¿YµC— ©Àÿ}jp‚Ÿà·º1X¸ô�¿ =€ú,ÓÕŠ0_ �hÀÅØããTH�ž+ÏÈóþœ¼_� oÆÿ™S!؆Řì;�!{ûƒ¿2¿`'öÇDà@U�`V—7zûN‡`êªÀÉî‚�Sý¼Sß©ñû$�1ìI€d�`û–7‚þ™Ó!ˆž ¼N† �Ö•þ»ñsétH�J´#!è€ßÈeý‹1àŸéåK�jÕIÁëñ³sJ€ÆœÿÇñÓ> @“öÆD '¯Æ„`?:�êô—7ýR°—�ðôjÁÍÏOo$’`.é—µö9¨¸ðoÿ;�������������������������������������������������������������������������������������������������������������������������������ú�¡’6A“__P����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/preferences.ui���������������������������������������������������0000664�0000000�0000000�00000245623�15003540030�0022123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>PreferencesDialog</class> <widget class="QDialog" name="PreferencesDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>626</width> <height>534</height> </rect> </property> <property name="windowTitle"> <string>Preferences</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="2" column="0"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPushButton" name="helpButton"> <property name="mouseTracking"> <bool>true</bool> </property> <property name="toolTip"> <string/> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="help-browser"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <spacer name="horizontalSpacer_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="cancelButton"> <property name="text"> <string>Close</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="applyButton"> <property name="text"> <string>Apply</string> </property> <property name="icon"> <iconset theme="document-save"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="acceptButton"> <property name="text"> <string>Save</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> </layout> </item> <item row="0" column="0"> <widget class="QTabWidget" name="tabWidget"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="tabPosition"> <enum>QTabWidget::North</enum> </property> <property name="currentIndex"> <number>0</number> </property> <widget class="QWidget" name="tab"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <attribute name="title"> <string>Pop-ups</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> <item row="1" column="0" colspan="3"> <widget class="QLabel" name="label"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></string> </property> <property name="text"> <string>Default timeout</string> </property> </widget> </item> <item row="1" column="3"> <layout class="QHBoxLayout" name="horizontalLayout_4"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QPushButton" name="cmdTimeoutUp"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-add"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QSpinBox" name="spinUITimeout"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="buttonSymbols"> <enum>QAbstractSpinBox::NoButtons</enum> </property> <property name="accelerated"> <bool>true</bool> </property> <property name="maximum"> <number>100</number> </property> <property name="value"> <number>30</number> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdTimeoutDown"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-remove"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item row="0" column="3"> <widget class="QCheckBox" name="popupsCheck"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> </widget> </item> <item row="0" column="0" colspan="3"> <widget class="QLabel" name="label_16"> <property name="text"> <string>Disable pop-ups, only display a notification</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="2" column="0" colspan="4"> <widget class="QToolBox" name="toolBox"> <widget class="QWidget" name="page_7"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>586</width> <height>306</height> </rect> </property> <attribute name="label"> <string>Default options</string> </attribute> <layout class="QGridLayout" name="gridLayout_5" rowstretch="0,0,0,0" columnstretch="1,0"> <item row="1" column="1"> <widget class="QComboBox" name="comboUIDuration"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>once</string> </property> </item> <item> <property name="text"> <string>30s</string> </property> </item> <item> <property name="text"> <string>5m</string> </property> </item> <item> <property name="text"> <string>15m</string> </property> </item> <item> <property name="text"> <string>30m</string> </property> </item> <item> <property name="text"> <string>1h</string> </property> </item> <item> <property name="text"> <string>until reboot</string> </property> </item> <item> <property name="text"> <string>forever</string> </property> </item> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_2"> <property name="toolTip"> <string><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. the daemon's default action will be applied (see Nodes tab).</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></string> </property> <property name="text"> <string>Action</string> </property> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Default position on screen</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_3"> <property name="toolTip"> <string>Pop-up default duration</string> </property> <property name="text"> <string>Duration</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QComboBox" name="comboUITarget"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>by executable</string> </property> </item> <item> <property name="text"> <string>by command line</string> </property> </item> <item> <property name="text"> <string>by destination port</string> </property> </item> <item> <property name="text"> <string>by destination ip</string> </property> </item> <item> <property name="text"> <string>by user id</string> </property> </item> <item> <property name="text"> <string>by PID</string> </property> </item> </widget> </item> <item row="3" column="1"> <widget class="QComboBox" name="comboUIDialogPos"> <property name="enabled"> <bool>true</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>center</string> </property> </item> <item> <property name="text"> <string>top right</string> </property> </item> <item> <property name="text"> <string>bottom right</string> </property> </item> <item> <property name="text"> <string>top left</string> </property> </item> <item> <property name="text"> <string>bottom left</string> </property> </item> </widget> </item> <item row="0" column="1"> <widget class="QComboBox" name="comboUIAction"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>deny</string> </property> <property name="icon"> <iconset theme="emblem-important"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>allow</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>reject</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>.</normaloff>.</iconset> </property> </item> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_5"> <property name="text"> <string>Default target</string> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="page_8"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>586</width> <height>306</height> </rect> </property> <attribute name="label"> <string>More</string> </attribute> <layout class="QGridLayout" name="gridLayout_14"> <item row="2" column="0" colspan="2"> <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0"> <item> <widget class="QCheckBox" name="uidCheck"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>If checked, this field will be selected when a pop-up is displayed</string> </property> <property name="text"> <string>User ID</string> </property> </widget> </item> <item> <widget class="QCheckBox" name="dstPortCheck"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>If checked, this field will be selected when a pop-up is displayed</string> </property> <property name="text"> <string>Destination port</string> </property> </widget> </item> <item> <widget class="QCheckBox" name="dstIPCheck"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>If checked, this field will be selected when a pop-up is displayed</string> </property> <property name="text"> <string>Destination IP</string> </property> </widget> </item> </layout> </item> <item row="1" column="0"> <widget class="QLabel" name="label_18"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></string> </property> <property name="text"> <string>Filter connections also by:</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_17"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>The advanced view allows you to easily select multiple fields to filter connections</string> </property> <property name="text"> <string>Show advanced view by default</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="0" column="1"> <widget class="QCheckBox" name="showAdvancedCheck"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></string> </property> <property name="text"> <string/> </property> </widget> </item> </layout> </widget> </widget> </item> </layout> </widget> <widget class="QWidget" name="tab_4"> <attribute name="title"> <string>UI</string> </attribute> <layout class="QGridLayout" name="gridLayout_6"> <item row="0" column="0"> <widget class="QToolBox" name="toolBox_2"> <property name="currentIndex"> <number>0</number> </property> <widget class="QWidget" name="page_5"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>586</width> <height>301</height> </rect> </property> <attribute name="label"> <string>General</string> </attribute> <layout class="QGridLayout" name="gridLayout_16"> <item row="10" column="2" colspan="2"> <widget class="QLineEdit" name="lineUIScreenFactor"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Use numbers to define a global scale factor for the whole application: 1, 1.2, 1.5, 2, etc ... Use ; to define multiple screens: 1;1.5 etc...</string> </property> <property name="placeholderText"> <string>ex: 1, 1.25, 1.5, 2, ...</string> </property> </widget> </item> <item row="0" column="2" colspan="2"> <layout class="QHBoxLayout" name="horizontalLayout_8"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QPushButton" name="cmdRefreshUIUp"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-add"> <normaloff>../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QSpinBox" name="spinUIRefresh"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="buttonSymbols"> <enum>QAbstractSpinBox::NoButtons</enum> </property> <property name="accelerated"> <bool>true</bool> </property> <property name="maximum"> <number>100</number> </property> <property name="value"> <number>1</number> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdRefreshUIDown"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-remove"> <normaloff>../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item row="3" column="2" colspan="2"> <widget class="QComboBox" name="comboUITheme"> <item> <property name="text"> <string>System</string> </property> </item> </widget> </item> <item row="12" column="0" colspan="2"> <widget class="QCheckBox" name="checkAutostart"> <property name="toolTip"> <string>By default the GUI is started when login</string> </property> <property name="text"> <string>Autostart the GUI upon login</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="0" column="0" colspan="2"> <widget class="QLabel" name="label_27"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Refresh interval (seconds)</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="11" column="0" colspan="2"> <widget class="QLabel" name="labelUIDensity"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Theme density scale</string> </property> </widget> </item> <item row="7" column="0" colspan="4"> <widget class="QCheckBox" name="checkUIAutoScreen"> <property name="text"> <string>Auto screen scale factor</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="label_21"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Theme</string> </property> </widget> </item> <item row="1" column="2" colspan="2"> <widget class="QComboBox" name="comboUILang"/> </item> <item row="10" column="0"> <widget class="QLabel" name="labelUIScreenFactor"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string><html><head/><body><p>Scale factor (use ; for multiple displays) <a href="https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#gui-size-problems-on-4k-monitors"><span style=" text-decoration: underline; color:#0000ff;">More information</span></a></p></body></html></string> </property> </widget> </item> <item row="11" column="2" colspan="2"> <layout class="QHBoxLayout" name="horizontalLayout_9"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QPushButton" name="cmdUIDensityUp"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-add"> <normaloff>../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QSpinBox" name="spinUIDensity"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="buttonSymbols"> <enum>QAbstractSpinBox::NoButtons</enum> </property> <property name="accelerated"> <bool>true</bool> </property> <property name="minimum"> <number>-20</number> </property> <property name="maximum"> <number>20</number> </property> <property name="value"> <number>0</number> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdUIDensityDown"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-remove"> <normaloff>../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item row="1" column="0" colspan="2"> <widget class="QLabel" name="label_19"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Language</string> </property> </widget> </item> <item row="4" column="0"> <widget class="QLabel" name="labelThemeError"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="5" column="2" colspan="2"> <widget class="QComboBox" name="comboUIQtPlatform"> <property name="editable"> <bool>true</bool> </property> <item> <property name="text"> <string/> </property> </item> <item> <property name="text"> <string notr="true">xcb</string> </property> </item> <item> <property name="text"> <string notr="true">wayland</string> </property> </item> </widget> </item> <item row="5" column="0" colspan="2"> <widget class="QLabel" name="label_29"> <property name="toolTip"> <string>This option will set QT_QPA_PLATFORM when launching the GUI. xcb - X11 compatibility. If you experience issues with wayland, use this plugin. wayland</string> </property> <property name="text"> <string>Qt platform plugin</string> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="page_6"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>259</width> <height>178</height> </rect> </property> <attribute name="label"> <string>Server</string> </attribute> <layout class="QGridLayout" name="gridLayout_13"> <item row="0" column="1"> <widget class="QComboBox" name="comboGrpcMsgSize"> <property name="editable"> <bool>true</bool> </property> <property name="currentText"> <string notr="true">4MiB</string> </property> <item> <property name="text"> <string>4MiB</string> </property> </item> <item> <property name="text"> <string>8MiB</string> </property> </item> <item> <property name="text"> <string>16MiB</string> </property> </item> <item> <property name="text"> <string>32MiB</string> </property> </item> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_24"> <property name="toolTip"> <string><p>Simple: no authentication</p> <p>TLS simple/mutual: use SSL certificates to authenticate nodes.</p> <p>Visit the wiki for more information.</p></string> </property> <property name="text"> <string>Authentication type</string> </property> </widget> </item> <item row="3" column="0" colspan="2"> <widget class="QLineEdit" name="lineCertFile"> <property name="placeholderText"> <string>Absolute path to the cert file</string> </property> </widget> </item> <item row="4" column="0" colspan="2"> <widget class="QLineEdit" name="lineCertKeyFile"> <property name="placeholderText"> <string>Absolute path to the cert key file</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QComboBox" name="comboAuthType"> <item> <property name="text"> <string>Simple</string> </property> </item> <item> <property name="text"> <string>Simple TLS</string> </property> </item> <item> <property name="text"> <string>Mutual TLS</string> </property> </item> </widget> </item> <item row="2" column="0" colspan="2"> <widget class="QLineEdit" name="lineCACertFile"> <property name="placeholderText"> <string>Absolute path to the CA cert file</string> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_20"> <property name="toolTip"> <string>Maximum size of each message from nodes. Default 4MB</string> </property> <property name="text"> <string>Max gRPC channel size</string> </property> </widget> </item> <item row="5" column="0" colspan="2"> <widget class="QLabel" name="label_28"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string><a href="https://github.com/evilsocket/opensnitch/wiki/Nodes-authentication#nodes-authentication-added-in-v161">More information</a></string> </property> <property name="openExternalLinks"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="page_3"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>321</width> <height>112</height> </rect> </property> <attribute name="label"> <string>Desktop notifications</string> </attribute> <layout class="QFormLayout" name="formLayout"> <item row="0" column="0" colspan="2"> <widget class="QGroupBox" name="groupNotifs"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="title"> <string>Enable</string> </property> <property name="flat"> <bool>true</bool> </property> <property name="checkable"> <bool>true</bool> </property> <layout class="QGridLayout" name="gridLayout_9"> <item row="0" column="0"> <widget class="QRadioButton" name="radioSysNotifs"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Use system notifications</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="1" column="0"> <widget class="QRadioButton" name="radioQtNotifs"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Use Qt notifications</string> </property> </widget> </item> <item row="0" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QLabel" name="labelNotifsWarning"> <property name="text"> <string/> </property> </widget> </item> <item> <spacer name="horizontalSpacer_5"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="cmdTestNotifs"> <property name="text"> <string>Test</string> </property> </widget> </item> </layout> </item> </layout> </widget> </item> </layout> </widget> <widget class="QWidget" name="page_4"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>586</width> <height>301</height> </rect> </property> <attribute name="label"> <string>Events tab columns</string> </attribute> <layout class="QFormLayout" name="formLayout_2"> <item row="0" column="0" colspan="2"> <widget class="QGroupBox" name="groupBox_2"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <layout class="QGridLayout" name="gridLayout_7"> <property name="leftMargin"> <number>4</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>4</number> </property> <property name="bottomMargin"> <number>4</number> </property> <property name="spacing"> <number>0</number> </property> <item row="2" column="1"> <widget class="QCheckBox" name="checkHideNode"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Node</string> </property> <property name="checked"> <bool>false</bool> </property> </widget> </item> <item row="8" column="1"> <widget class="QCheckBox" name="checkHideCmdline"> <property name="text"> <string>Command line</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="checkHideTime"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Time</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="2" column="2"> <widget class="QCheckBox" name="checkHideAction"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Action</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="4" column="0"> <widget class="QCheckBox" name="checkHideSrcPort"> <property name="text"> <string>Source port</string> </property> </widget> </item> <item row="4" column="1"> <widget class="QCheckBox" name="checkHideSrcIP"> <property name="text"> <string>Source IP</string> </property> </widget> </item> <item row="8" column="2"> <widget class="QCheckBox" name="checkHideRule"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Rule</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="8" column="0"> <widget class="QCheckBox" name="checkHideProc"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Process</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="5" column="1"> <widget class="QCheckBox" name="checkHideDstIP"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Dest IP</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="5" column="0"> <widget class="QCheckBox" name="checkHideDstPort"> <property name="text"> <string>Dest port</string> </property> </widget> </item> <item row="5" column="2"> <widget class="QCheckBox" name="checkHideDstHost"> <property name="text"> <string>Dest host</string> </property> </widget> </item> <item row="7" column="0"> <widget class="QCheckBox" name="checkHideProto"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Protocol</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="7" column="1"> <widget class="QCheckBox" name="checkHidePID"> <property name="text"> <string>PID</string> </property> </widget> </item> <item row="7" column="2"> <widget class="QCheckBox" name="checkHideUID"> <property name="text"> <string>UID</string> </property> </widget> </item> </layout> </widget> </item> </layout> </widget> </widget> </item> </layout> </widget> <widget class="QWidget" name="tab_5"> <attribute name="title"> <string>Rules</string> </attribute> <layout class="QGridLayout" name="gridLayout_11"> <item row="0" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_5"> <item> <widget class="QCheckBox" name="checkUIRules"> <property name="toolTip"> <string>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</string> </property> <property name="text"> <string>Don't save/Delete rules of duration</string> </property> </widget> </item> <item> <widget class="QComboBox" name="comboUIRules"> <item> <property name="text"> <string>any temporary rules</string> </property> </item> <item> <property name="text"> <string>once</string> </property> </item> <item> <property name="text"> <string>30s or less</string> </property> </item> <item> <property name="text"> <string>5m or less</string> </property> </item> <item> <property name="text"> <string>15m or less</string> </property> </item> <item> <property name="text"> <string>30m or less</string> </property> </item> <item> <property name="text"> <string>1h or less</string> </property> </item> </widget> </item> </layout> </item> <item row="1" column="0"> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <widget class="QWidget" name="tab_3"> <attribute name="title"> <string>Nodes</string> </attribute> <layout class="QGridLayout" name="gridLayout_4" rowstretch="0,0,0,0"> <item row="0" column="2"> <widget class="QCheckBox" name="checkApplyToNodes"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Apply configuration to all nodes</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_9"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Ignored"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Version</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> </widget> </item> <item row="0" column="0"> <widget class="QComboBox" name="comboNodes"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> </widget> </item> <item row="2" column="2"> <widget class="QLabel" name="labelNodeVersion"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> </widget> </item> <item row="0" column="1"> <spacer name="horizontalSpacer_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Preferred</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item row="1" column="2"> <widget class="QLabel" name="labelNodeName"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> </widget> </item> <item row="3" column="0" colspan="3"> <widget class="QToolBox" name="toolBox"> <property name="currentIndex"> <number>0</number> </property> <widget class="QWidget" name="page"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>586</width> <height>260</height> </rect> </property> <attribute name="label"> <string>General</string> </attribute> <layout class="QGridLayout" name="gridLayout_10"> <item row="1" column="0"> <widget class="QLabel" name="label_10"> <property name="toolTip"> <string><html><head/><body><p>The default action will be applied to new outbound connections in two scenarios:</p><p>when the daemon is not connected to the UI, or when there's a pop-up running.</p></body></html></string> </property> <property name="text"> <string>Default action when the GUI is disconnected</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_11"> <property name="toolTip"> <string><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></string> </property> <property name="text"> <string>Default duration</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QComboBox" name="comboNodeDuration"> <item> <property name="text"> <string>once</string> </property> </item> <item> <property name="text"> <string>until restart</string> </property> </item> <item> <property name="text"> <string>always</string> </property> </item> </widget> </item> <item row="1" column="1"> <widget class="QComboBox" name="comboNodeAction"> <property name="editable"> <bool>false</bool> </property> <item> <property name="text"> <string>deny</string> </property> <property name="icon"> <iconset theme="emblem-important"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>allow</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>reject</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>.</normaloff>.</iconset> </property> </item> </widget> </item> <item row="4" column="1"> <widget class="QComboBox" name="comboNodeMonitorMethod"> <property name="accessibleDescription"> <string notr="true"/> </property> <item> <property name="text"> <string notr="true">proc</string> </property> </item> <item> <property name="text"> <string notr="true">ebpf</string> </property> </item> <item> <property name="text"> <string notr="true">audit</string> </property> </item> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="label_12"> <property name="toolTip"> <string><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></string> </property> <property name="text"> <string>Debug invalid connections</string> </property> </widget> </item> <item row="4" column="0"> <widget class="QLabel" name="label_13"> <property name="text"> <string>Process monitor method</string> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_15"> <property name="toolTip"> <string><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></string> </property> <property name="text"> <string>Address</string> </property> </widget> </item> <item row="3" column="1"> <widget class="QCheckBox" name="checkInterceptUnknown"> <property name="text"> <string/> </property> </widget> </item> <item row="0" column="1"> <widget class="QComboBox" name="comboNodeAddress"> <property name="editable"> <bool>true</bool> </property> <item> <property name="text"> <string>unix:///tmp/osui.sock</string> </property> </item> </widget> </item> </layout> </widget> <widget class="QWidget" name="page_2"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>376</width> <height>118</height> </rect> </property> <attribute name="label"> <string>Logging</string> </attribute> <layout class="QGridLayout" name="gridLayout_12"> <item row="2" column="1"> <widget class="QCheckBox" name="checkNodeLogUTC"> <property name="text"> <string/> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_7"> <property name="toolTip"> <string><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></string> </property> <property name="text"> <string>Log file</string> </property> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="label_23"> <property name="toolTip"> <string><html><head/><body><p>If checked, OpenSnitch will log timestamp microseconds.</p></body></html></string> </property> <property name="text"> <string>Log timestamp microseconds</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QComboBox" name="comboNodeLogLevel"> <property name="accessibleDescription"> <string notr="true"/> </property> <item> <property name="text"> <string notr="true">DEBUG</string> </property> </item> <item> <property name="text"> <string notr="true">INFO</string> </property> </item> <item> <property name="text"> <string notr="true">IMPORTANT</string> </property> </item> <item> <property name="text"> <string notr="true">WARNING</string> </property> </item> <item> <property name="text"> <string notr="true">ERROR</string> </property> </item> <item> <property name="text"> <string notr="true">FATAL</string> </property> </item> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_22"> <property name="toolTip"> <string><html><head/><body><p>If checked, OpenSnitch will use the UTC timezone for timestamps.</p></body></html></string> </property> <property name="text"> <string>Log UTC timestamps</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_14"> <property name="text"> <string>Default log level</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QComboBox" name="comboNodeLogFile"> <property name="editable"> <bool>true</bool> </property> <item> <property name="text"> <string>/var/log/opensnitchd.log</string> </property> </item> <item> <property name="text"> <string>/dev/stdout</string> </property> </item> </widget> </item> <item row="3" column="1"> <widget class="QCheckBox" name="checkNodeLogMicro"> <property name="text"> <string/> </property> </widget> </item> <item row="4" column="1"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Maximum</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <widget class="QWidget" name="page_7"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>296</width> <height>211</height> </rect> </property> <attribute name="label"> <string>Authentication</string> </attribute> <layout class="QGridLayout" name="gridLayout_15"> <item row="5" column="0"> <widget class="QLineEdit" name="lineNodeCertFile"> <property name="placeholderText"> <string>Absolute path to the cert file</string> </property> </widget> </item> <item row="2" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_7"> <item> <widget class="QLabel" name="label_25"> <property name="toolTip"> <string><p>Simple: no authentication, TLS simple/mutual: use SSL certificates to authenticate nodes.</p><p>Visit the wiki for more information.</p></string> </property> <property name="text"> <string>Authentication type</string> </property> </widget> </item> <item> <widget class="QComboBox" name="comboNodeAuthType"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>Simple</string> </property> </item> <item> <property name="text"> <string>Simple TLS</string> </property> </item> <item> <property name="text"> <string>Mutual TLS</string> </property> </item> </widget> </item> </layout> </item> <item row="3" column="0"> <widget class="QLineEdit" name="lineNodeCACertFile"> <property name="placeholderText"> <string>Absolute path to the CA cert file</string> </property> </widget> </item> <item row="8" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_6"> <item> <widget class="QCheckBox" name="checkNodeAuthSkipVerify"> <property name="text"> <string>Don't verify certs</string> </property> </widget> </item> <item> <widget class="QComboBox" name="comboNodeAuthVerifyType"> <item> <property name="text"> <string>no-client-cert</string> </property> </item> <item> <property name="text"> <string>req-cert</string> </property> </item> <item> <property name="text"> <string>req-any-cert</string> </property> </item> <item> <property name="text"> <string>verify-cert</string> </property> </item> <item> <property name="text"> <string>req-and-verify-cert</string> </property> </item> </widget> </item> </layout> </item> <item row="6" column="0"> <widget class="QLineEdit" name="lineNodeCertKeyFile"> <property name="placeholderText"> <string>Absolute path to the cert key file</string> </property> </widget> </item> <item row="4" column="0"> <widget class="QLineEdit" name="lineNodeServerCertFile"> <property name="placeholderText"> <string>Absolute path to the server cert file</string> </property> </widget> </item> <item row="9" column="0"> <widget class="QLabel" name="label_26"> <property name="text"> <string><a href="https://github.com/evilsocket/opensnitch/wiki/Nodes-authentication#nodes-authentication-added-in-v161">More information</a></string> </property> <property name="openExternalLinks"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </widget> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_8"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>HostName</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="tab_2"> <attribute name="title"> <string>Database</string> </attribute> <layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0,0"> <property name="sizeConstraint"> <enum>QLayout::SetDefaultConstraint</enum> </property> <property name="leftMargin"> <number>9</number> </property> <property name="verticalSpacing"> <number>15</number> </property> <item row="1" column="1" colspan="3"> <widget class="QLabel" name="dbLabel"> <property name="text"> <string/> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="0" column="3"> <widget class="QComboBox" name="comboDBType"> <property name="enabled"> <bool>true</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>In memory</string> </property> </item> <item> <property name="text"> <string>File</string> </property> </item> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_6"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Database type</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QPushButton" name="dbFileButton"> <property name="text"> <string>Select</string> </property> <property name="icon"> <iconset theme="document-open"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item row="5" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item row="0" column="1"> <spacer name="horizontalSpacer_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item row="2" column="0" colspan="4"> <layout class="QGridLayout" name="gridLayout_8"> <property name="verticalSpacing"> <number>10</number> </property> <item row="0" column="3"> <widget class="QSpinBox" name="spinDBMaxDays"> <property name="enabled"> <bool>false</bool> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="buttonSymbols"> <enum>QAbstractSpinBox::NoButtons</enum> </property> <property name="minimum"> <number>1</number> </property> <property name="maximum"> <number>99999</number> </property> </widget> </item> <item row="0" column="2"> <widget class="QPushButton" name="cmdDBMaxDaysUp"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-add"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item row="1" column="5"> <widget class="QLabel" name="labelDBPurgeMinutes"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>minutes</string> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item row="1" column="3"> <widget class="QSpinBox" name="spinDBPurgeInterval"> <property name="enabled"> <bool>false</bool> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="buttonSymbols"> <enum>QAbstractSpinBox::NoButtons</enum> </property> <property name="minimum"> <number>5</number> </property> <property name="maximum"> <number>1440</number> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="labelDBPurgeInterval"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Minutes between events purges</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="0" column="1"> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item row="0" column="5"> <widget class="QLabel" name="labelDBPurgeDays"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>days</string> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item row="0" column="0"> <widget class="QCheckBox" name="checkDBMaxDays"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Maximum days of events to keep</string> </property> </widget> </item> <item row="0" column="4"> <widget class="QPushButton" name="cmdDBMaxDaysDown"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-remove"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item row="1" column="2"> <widget class="QPushButton" name="cmdDBPurgesUp"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-add"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item row="1" column="4"> <widget class="QPushButton" name="cmdDBPurgesDown"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="list-remove"> <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="checkDBJrnlWal"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Enable DB Write-Ahead Logging (WAL)</string> </property> </widget> </item> </layout> </item> </layout> </widget> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="statusLabel"> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> </layout> </widget> <resources/> <connections/> </ui> �������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/process_details.ui�����������������������������������������������0000664�0000000�0000000�00000016637�15003540030�0023006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>ProcessDetailsDialog</class> <widget class="QDialog" name="ProcessDetailsDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>731</width> <height>478</height> </rect> </property> <property name="windowTitle"> <string>Process details</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="labelProcIcon"> <property name="minimumSize"> <size> <width>48</width> <height>48</height> </size> </property> <property name="maximumSize"> <size> <width>64</width> <height>64</height> </size> </property> <property name="text"> <string/> </property> </widget> </item> <item> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <widget class="QLabel" name="labelProcName"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="text"> <string>loading...</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QLabel" name="labelProcArgs"> <property name="text"> <string>loading...</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> </layout> </item> </layout> </item> <item> <widget class="QLabel" name="labelCwd"> <property name="text"> <string>CWD: loading...</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QLabel" name="labelStatm"> <property name="text"> <string>mem stats: loading...</string> </property> </widget> </item> </layout> </item> </layout> </item> <item> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item> <widget class="QTabWidget" name="tabWidget"> <property name="tabPosition"> <enum>QTabWidget::South</enum> </property> <property name="currentIndex"> <number>0</number> </property> <property name="documentMode"> <bool>true</bool> </property> <widget class="QWidget" name="tab_2"> <attribute name="title"> <string>Status</string> </attribute> <layout class="QGridLayout" name="gridLayout_3"> <item row="0" column="1"> <widget class="QPlainTextEdit" name="textStatus"> <property name="undoRedoEnabled"> <bool>false</bool> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="tab_3"> <attribute name="title"> <string>Open files</string> </attribute> <layout class="QGridLayout" name="gridLayout_4"> <item row="1" column="0"> <widget class="QPlainTextEdit" name="textOpenedFiles"> <property name="undoRedoEnabled"> <bool>false</bool> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="tabWidgetPage1"> <attribute name="title"> <string>I/O Statistics</string> </attribute> <layout class="QGridLayout" name="gridLayout"> <item row="1" column="0"> <widget class="QPlainTextEdit" name="textIOStats"> <property name="undoRedoEnabled"> <bool>false</bool> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="tabWidgetPage2"> <attribute name="title"> <string>Memory mapped files</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> <item row="1" column="0"> <widget class="QPlainTextEdit" name="textMappedFiles"> <property name="undoRedoEnabled"> <bool>false</bool> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="tab"> <attribute name="title"> <string>Stack</string> </attribute> <layout class="QGridLayout" name="gridLayout_5"> <item row="0" column="0"> <widget class="QPlainTextEdit" name="textStack"> <property name="undoRedoEnabled"> <bool>false</bool> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="tab_4"> <attribute name="title"> <string>Environment variables</string> </attribute> <layout class="QGridLayout" name="gridLayout_6"> <item row="0" column="0"> <widget class="QPlainTextEdit" name="textEnv"> <property name="undoRedoEnabled"> <bool>false</bool> </property> </widget> </item> </layout> </widget> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QLabel" name="label"> <property name="text"> <string>Application pids</string> </property> </widget> </item> <item> <widget class="QComboBox" name="comboPids"> <property name="maxVisibleItems"> <number>100</number> </property> <property name="sizeAdjustPolicy"> <enum>QComboBox::AdjustToContents</enum> </property> </widget> </item> <item> <spacer name="horizontalSpacer_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="cmdAction"> <property name="toolTip"> <string>Start or stop monitoring this process</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="media-playback-start"/> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdClose"> <property name="text"> <string>Close</string> </property> <property name="icon"> <iconset theme="window-close"/> </property> </widget> </item> </layout> </item> </layout> </widget> <resources/> <connections/> </ui> �������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/prompt.ui��������������������������������������������������������0000664�0000000�0000000�00000065420�15003540030�0021136�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Dialog</class> <widget class="QDialog" name="Dialog"> <property name="windowModality"> <enum>Qt::NonModal</enum> </property> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>515</width> <height>315</height> </rect> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>0</width> <height>200</height> </size> </property> <property name="font"> <font> <kerning>true</kerning> </font> </property> <property name="windowTitle"> <string>opensnitch-qt</string> </property> <property name="windowIcon"> <iconset resource="resources.qrc"> <normaloff>:/pics/icon-white.png</normaloff> <normalon>:/pics/icon.png</normalon>:/pics/icon-white.png</iconset> </property> <property name="sizeGripEnabled"> <bool>false</bool> </property> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,0,0,1"> <property name="spacing"> <number>2</number> </property> <property name="sizeConstraint"> <enum>QLayout::SetMinAndMaxSize</enum> </property> <property name="leftMargin"> <number>5</number> </property> <property name="topMargin"> <number>5</number> </property> <property name="rightMargin"> <number>5</number> </property> <property name="bottomMargin"> <number>5</number> </property> <item> <layout class="QFormLayout" name="formLayout"> <property name="fieldGrowthPolicy"> <enum>QFormLayout::ExpandingFieldsGrow</enum> </property> <property name="labelAlignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="verticalSpacing"> <number>0</number> </property> <item row="0" column="0"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLabel" name="iconLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>64</width> <height>64</height> </size> </property> <property name="maximumSize"> <size> <width>64</width> <height>64</height> </size> </property> <property name="text"> <string/> </property> <property name="pixmap"> <pixmap resource="resources.qrc">:/pics/icon.png</pixmap> </property> <property name="scaledContents"> <bool>true</bool> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> </widget> </item> <item> <widget class="QLabel" name="label_4"> <property name="text"> <string/> </property> </widget> </item> </layout> </item> <item row="0" column="1"> <layout class="QVBoxLayout" name="verticalLayout_3"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QLabel" name="appNameLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>16</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string notr="true">Chromium Web Browser</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item> <widget class="QLabel" name="appDescriptionLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>50</weight> <italic>true</italic> <bold>false</bold> <underline>true</underline> <kerning>true</kerning> </font> </property> <property name="text"> <string notr="true"><html><head/><body><p>/opt/google/chrome/bin/chrome --something abc --more-long def --for-word-wrapping</p></body></html></string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item> <widget class="QLabel" name="appPathLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string notr="true">(/path/to/bin/chromium)</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item> <widget class="QLabel" name="argsLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <family>DejaVu Sans</family> <pointsize>9</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string notr="true">(/path/to/bin/chromium)</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> </layout> </item> <item> <widget class="Line" name="line"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item> <layout class="QVBoxLayout" name="verticalLayout_5"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QLabel" name="messageLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string notr="true">Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="margin"> <number>2</number> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> <item> <layout class="QGridLayout" name="gridLayout"> <property name="topMargin"> <number>6</number> </property> <property name="verticalSpacing"> <number>3</number> </property> <item row="1" column="1"> <widget class="QLabel" name="label"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Source IP</string> </property> </widget> </item> <item row="0" column="3"> <widget class="QLabel" name="cwdLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string/> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="7" column="3"> <widget class="QLabel" name="pidLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="text"> <string>TextLabel</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="6" column="1"> <widget class="QLabel" name="label_6"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>User ID</string> </property> </widget> </item> <item row="6" column="0"> <widget class="QCheckBox" name="checkUserID"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> </widget> </item> <item row="7" column="1"> <widget class="QLabel" name="pidLabelWidget"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Process ID</string> </property> </widget> </item> <item row="5" column="1"> <widget class="QLabel" name="destPortLabel_1"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Dst Port</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QLabel" name="label_3"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Destination IP</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QLabel" name="label_2"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></string> </property> </widget> </item> <item row="6" column="3"> <widget class="QLabel" name="uidLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="text"> <string>TextLabel</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="2" column="3"> <widget class="QLabel" name="destIPLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="text"> <string>TextLabel</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="0" column="2"> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item row="2" column="0"> <widget class="QCheckBox" name="checkDstIP"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> </widget> </item> <item row="2" column="2"> <widget class="QComboBox" name="whatIPCombo"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumContentsLength"> <number>0</number> </property> </widget> </item> <item row="5" column="0"> <widget class="QCheckBox" name="checkDstPort"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> </widget> </item> <item row="5" column="3"> <widget class="QLabel" name="destPortLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="text"> <string>TextLabel</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="1" column="3"> <widget class="QLabel" name="sourceIPLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="text"> <string>TextLabel</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> <item> <widget class="QLabel" name="label_5"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>0</width> <height>0</height> </size> </property> <property name="text"> <string notr="true"/> </property> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout" stretch="3,2,1,0,0"> <property name="sizeConstraint"> <enum>QLayout::SetMinimumSize</enum> </property> <item> <widget class="QComboBox" name="whatCombo"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>97</width> <height>26</height> </size> </property> <item> <property name="text"> <string>from this executable</string> </property> </item> <item> <property name="text"> <string>from this command line</string> </property> </item> <item> <property name="text"> <string>this destination port</string> </property> </item> <item> <property name="text"> <string>this user</string> </property> </item> <item> <property name="text"> <string>this destination ip</string> </property> </item> <item> <property name="text"> <string>from this PID</string> </property> </item> </widget> </item> <item> <widget class="QComboBox" name="durationCombo"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>97</width> <height>26</height> </size> </property> <property name="currentText"> <string>once</string> </property> <item> <property name="text"> <string>once</string> </property> </item> <item> <property name="text"> <string>30s</string> </property> </item> <item> <property name="text"> <string>5m</string> </property> </item> <item> <property name="text"> <string>15m</string> </property> </item> <item> <property name="text"> <string>30m</string> </property> </item> <item> <property name="text"> <string>1h</string> </property> </item> <item> <property name="text"> <string>until reboot</string> </property> </item> <item> <property name="text"> <string>forever</string> </property> </item> </widget> </item> <item> <widget class="QToolButton" name="actionButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>97</width> <height>26</height> </size> </property> <property name="styleSheet"> <string notr="true"/> </property> <property name="text"> <string>action</string> </property> <property name="icon"> <iconset theme="emblem-important"> <normaloff>.</normaloff>.</iconset> </property> <property name="popupMode"> <enum>QToolButton::MenuButtonPopup</enum> </property> <property name="toolButtonStyle"> <enum>Qt::ToolButtonTextBesideIcon</enum> </property> </widget> </item> <item> <widget class="QPushButton" name="allowButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>97</width> <height>26</height> </size> </property> <property name="text"> <string>Allow</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="checkAdvanced"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>26</width> <height>26</height> </size> </property> <property name="maximumSize"> <size> <width>40</width> <height>30</height> </size> </property> <property name="text"> <string>+</string> </property> <property name="checkable"> <bool>true</bool> </property> <property name="checked"> <bool>false</bool> </property> </widget> </item> </layout> </item> </layout> </widget> <tabstops> <tabstop>checkAdvanced</tabstop> <tabstop>allowButton</tabstop> <tabstop>actionButton</tabstop> <tabstop>durationCombo</tabstop> <tabstop>whatCombo</tabstop> <tabstop>checkDstPort</tabstop> <tabstop>checkDstIP</tabstop> <tabstop>checkUserID</tabstop> <tabstop>whatIPCombo</tabstop> </tabstops> <resources> <include location="resources.qrc"/> <include location="../../../../../../../../.designer/backup/resources.qrc"/> </resources> <connections/> </ui> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/resources.qrc����������������������������������������������������0000664�0000000�0000000�00000000260�15003540030�0021766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<RCC> <qresource prefix="pics"> <file>icon-white.svg</file> <file>icon-white.png</file> <file>icon-red.png</file> <file>icon.png</file> </qresource> </RCC> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/ruleseditor.ui���������������������������������������������������0000664�0000000�0000000�00000113715�15003540030�0022157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>RulesDialog</class> <widget class="QDialog" name="RulesDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>552</width> <height>559</height> </rect> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="windowTitle"> <string>Rule</string> </property> <layout class="QGridLayout" name="gridLayout_4"> <item row="6" column="0"> <widget class="QGroupBox" name="enableGroup"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="flat"> <bool>false</bool> </property> <property name="checkable"> <bool>false</bool> </property> <property name="checked"> <bool>false</bool> </property> <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0,0,0"> <property name="sizeConstraint"> <enum>QLayout::SetDefaultConstraint</enum> </property> <property name="topMargin"> <number>4</number> </property> <property name="bottomMargin"> <number>4</number> </property> <property name="verticalSpacing"> <number>12</number> </property> <item row="3" column="1"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Action</string> </property> </widget> </item> <item row="4" column="4"> <spacer name="horizontalSpacer_5"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item row="3" column="4"> <spacer name="horizontalSpacer_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item row="4" column="1"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Duration</string> </property> </widget> </item> <item row="4" column="6"> <widget class="QComboBox" name="durationCombo"> <item> <property name="text"> <string>once</string> </property> </item> <item> <property name="text"> <string notr="true">30s</string> </property> </item> <item> <property name="text"> <string notr="true">5m</string> </property> </item> <item> <property name="text"> <string notr="true">15m</string> </property> </item> <item> <property name="text"> <string notr="true">30m</string> </property> </item> <item> <property name="text"> <string notr="true">1h</string> </property> </item> <item> <property name="text"> <string>until reboot</string> </property> </item> <item> <property name="text"> <string>always</string> </property> </item> </widget> </item> <item row="3" column="6"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QRadioButton" name="actionDenyRadio"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Deny will just discard the connection</string> </property> <property name="text"> <string>Deny</string> </property> <property name="icon"> <iconset theme="emblem-important"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QRadioButton" name="actionRejectRadio"> <property name="toolTip"> <string>Reject will drop the connection, and kill the socket that initiated it</string> </property> <property name="text"> <string>Reject</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QRadioButton" name="actionAllowRadio"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Allow will allow the connection</string> </property> <property name="layoutDirection"> <enum>Qt::LeftToRight</enum> </property> <property name="text"> <string>Allow</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> </layout> </item> </layout> </widget> </item> <item row="4" column="0"> <widget class="QCheckBox" name="enableCheck"> <property name="text"> <string>Enable</string> </property> </widget> </item> <item row="5" column="0"> <widget class="QCheckBox" name="precedenceCheck"> <property name="toolTip"> <string>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: [x] Priority - 000-priority-rule [ ] Priority - 001-less-priority-rule</string> </property> <property name="text"> <string>Priority rule</string> </property> </widget> </item> <item row="0" column="0"> <widget class="QLineEdit" name="ruleNameEdit"> <property name="enabled"> <bool>true</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. 000-allow-localhost 001-deny-broadcast ...</string> </property> <property name="placeholderText"> <string>Name</string> </property> <property name="clearButtonEnabled"> <bool>true</bool> </property> </widget> </item> <item row="8" column="0"> <widget class="QLabel" name="statusLabel"> <property name="enabled"> <bool>true</bool> </property> <property name="text"> <string/> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="10" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <spacer name="horizontalSpacer_6"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="standardButtons"> <set>QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Reset</set> </property> </widget> </item> </layout> </item> <item row="2" column="0"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="label_3"> <property name="text"> <string>Node</string> </property> </widget> </item> <item> <widget class="QComboBox" name="nodesCombo"/> </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QCheckBox" name="nodeApplyAllCheck"> <property name="text"> <string>Apply rule to all nodes</string> </property> </widget> </item> </layout> </item> <item row="7" column="0"> <widget class="QTabWidget" name="tabWidget"> <property name="currentIndex"> <number>0</number> </property> <property name="elideMode"> <enum>Qt::ElideRight</enum> </property> <property name="documentMode"> <bool>true</bool> </property> <widget class="QWidget" name="tabWidgetPage1"> <attribute name="icon"> <iconset theme="system-run"> <normaloff>.</normaloff>.</iconset> </attribute> <attribute name="title"> <string>Applications</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> <item row="0" column="1" colspan="2"> <widget class="QLineEdit" name="procLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></string> </property> <property name="placeholderText"> <string notr="true">/path/to/executable, .*/bin/executable[0-9\.]+$, ...</string> </property> </widget> </item> <item row="3" column="1"> <widget class="QCheckBox" name="checkCmdlineRegexp"> <property name="text"> <string>Is regular expression</string> </property> </widget> </item> <item row="4" column="0"> <widget class="QCheckBox" name="uidCheck"> <property name="text"> <string>From this user ID</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="cmdlineCheck"> <property name="text"> <string>From this command line</string> </property> </widget> </item> <item row="2" column="1" colspan="2"> <widget class="QLineEdit" name="cmdlineLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></string> </property> <property name="placeholderText"> <string notr="true">curl -L https://www.domain.com</string> </property> </widget> </item> <item row="5" column="0"> <widget class="QCheckBox" name="pidCheck"> <property name="text"> <string>From this PID</string> </property> </widget> </item> <item row="5" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_11"> <item> <spacer name="horizontalSpacer_11"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QLineEdit" name="pidLine"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> </widget> </item> </layout> </item> <item row="4" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_8"> <item> <spacer name="horizontalSpacer_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> <enum>QSizePolicy::MinimumExpanding</enum> </property> </spacer> </item> <item> <widget class="QComboBox" name="uidCombo"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="editable"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item row="0" column="0"> <widget class="QCheckBox" name="procCheck"> <property name="text"> <string>From this executable</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QCheckBox" name="checkProcRegexp"> <property name="text"> <string>is regular expression</string> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="tabWidgetPage2"> <attribute name="icon"> <iconset theme="preferences-system-network"> <normaloff>.</normaloff>.</iconset> </attribute> <attribute name="title"> <string>Network</string> </attribute> <layout class="QGridLayout" name="gridLayout_3"> <item row="6" column="1"> <spacer name="horizontalSpacer_9"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Maximum</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>60</width> <height>20</height> </size> </property> </spacer> </item> <item row="7" column="4" colspan="2"> <widget class="QComboBox" name="ifaceCombo"> <property name="enabled"> <bool>false</bool> </property> <property name="editable"> <bool>true</bool> </property> </widget> </item> <item row="0" column="5"> <widget class="QComboBox" name="protoCombo"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></string> </property> <property name="editable"> <bool>true</bool> </property> <property name="currentText"> <string>TCP</string> </property> <item> <property name="text"> <string notr="true">TCP</string> </property> </item> <item> <property name="text"> <string notr="true">UDP</string> </property> </item> <item> <property name="text"> <string notr="true">UDPLITE</string> </property> </item> <item> <property name="text"> <string notr="true">TCP6</string> </property> </item> <item> <property name="text"> <string notr="true">UDP6</string> </property> </item> <item> <property name="text"> <string notr="true">UDPLITE6</string> </property> </item> <item> <property name="text"> <string>ICMP</string> </property> </item> <item> <property name="text"> <string>ICMP6</string> </property> </item> <item> <property name="text"> <string>SCTP</string> </property> </item> <item> <property name="text"> <string>SCTP6</string> </property> </item> </widget> </item> <item row="5" column="2" colspan="4"> <widget class="QLineEdit" name="dstHostLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string>Commas or spaces are not allowed to specify multiple domains. Use regular expressions instead: .*(opensnitch|duckduckgo).com .*\.google.com or a single domain: www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</string> </property> <property name="placeholderText"> <string>www.domain.org, .*\.domain.org</string> </property> </widget> </item> <item row="6" column="0"> <widget class="QCheckBox" name="dstIPCheck"> <property name="text"> <string>To this IP / Network</string> </property> </widget> </item> <item row="5" column="1"> <spacer name="horizontalSpacer_10"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Maximum</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>60</width> <height>20</height> </size> </property> </spacer> </item> <item row="0" column="0"> <widget class="QCheckBox" name="protoCheck"> <property name="text"> <string>Protocol</string> </property> </widget> </item> <item row="4" column="2" colspan="4"> <widget class="QComboBox" name="srcIPCombo"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</string> </property> <property name="editable"> <bool>true</bool> </property> <property name="currentText"> <string notr="true">LAN</string> </property> <item> <property name="text"> <string>LAN</string> </property> </item> <item> <property name="text"> <string>MULTICAST</string> </property> </item> <item> <property name="text"> <string>127.0.0.0/8</string> </property> </item> <item> <property name="text"> <string>192.168.0.0/24</string> </property> </item> <item> <property name="text"> <string>192.168.1.0/24</string> </property> </item> <item> <property name="text"> <string>192.168.2.0/24</string> </property> </item> <item> <property name="text"> <string>192.168.0.0/16</string> </property> </item> <item> <property name="text"> <string>169.254.0.0/16</string> </property> </item> <item> <property name="text"> <string>172.16.0.0/12</string> </property> </item> <item> <property name="text"> <string>10.0.0.0/8</string> </property> </item> <item> <property name="text"> <string>::1/128</string> </property> </item> <item> <property name="text"> <string>fc00::/7</string> </property> </item> <item> <property name="text"> <string>ff00::/8</string> </property> </item> <item> <property name="text"> <string>fe80::/10</string> </property> </item> <item> <property name="text"> <string>fd00::/8</string> </property> </item> </widget> </item> <item row="4" column="0"> <widget class="QCheckBox" name="srcIPCheck"> <property name="text"> <string>From this IP / Network</string> </property> </widget> </item> <item row="5" column="0"> <widget class="QCheckBox" name="dstHostCheck"> <property name="text"> <string>To this host</string> </property> </widget> </item> <item row="6" column="2" colspan="4"> <widget class="QComboBox" name="dstIPCombo"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string>You can specify a single IP: - 192.168.1.1 or a regular expression: - 192\.168\.1\.[0-9]+ multiple IPs: - ^(192\.168\.1\.1|172\.16\.0\.1)$ You can also specify a subnet: - 192.168.1.0/24 Note: Commas or spaces are not allowed to separate IPs or networks.</string> </property> <property name="statusTip"> <string/> </property> <property name="editable"> <bool>true</bool> </property> <property name="currentText"> <string notr="true">LAN</string> </property> <item> <property name="text"> <string notr="true">LAN</string> </property> </item> <item> <property name="text"> <string notr="true">MULTICAST</string> </property> </item> <item> <property name="text"> <string notr="true">127.0.0.0/8</string> </property> </item> <item> <property name="text"> <string notr="true">192.168.0.0/24</string> </property> </item> <item> <property name="text"> <string notr="true">192.168.1.0/24</string> </property> </item> <item> <property name="text"> <string notr="true">192.168.2.0/24</string> </property> </item> <item> <property name="text"> <string notr="true">192.168.0.0/16</string> </property> </item> <item> <property name="text"> <string notr="true">169.254.0.0/16</string> </property> </item> <item> <property name="text"> <string notr="true">172.16.0.0/12</string> </property> </item> <item> <property name="text"> <string notr="true">10.0.0.0/8</string> </property> </item> <item> <property name="text"> <string notr="true">::1/128</string> </property> </item> <item> <property name="text"> <string notr="true">fc00::/7</string> </property> </item> <item> <property name="text"> <string notr="true">ff00::/8</string> </property> </item> <item> <property name="text"> <string notr="true">fe80::/10</string> </property> </item> <item> <property name="text"> <string notr="true">fd00::/8</string> </property> </item> </widget> </item> <item row="7" column="0"> <widget class="QCheckBox" name="ifaceCheck"> <property name="text"> <string>Network interface</string> </property> </widget> </item> <item row="1" column="0" colspan="6"> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> <widget class="QCheckBox" name="srcPortCheck"> <property name="text"> <string>From this port</string> </property> </widget> </item> <item> <widget class="QLineEdit" name="srcPortLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></string> </property> </widget> </item> <item> <spacer name="horizontalSpacer_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QCheckBox" name="dstPortCheck"> <property name="text"> <string>To this port</string> </property> </widget> </item> <item> <widget class="QLineEdit" name="dstPortLine"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></string> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tabWidgetPage3"> <attribute name="icon"> <iconset theme="document-properties"> <normaloff>.</normaloff>.</iconset> </attribute> <attribute name="title"> <string>List of domains/IPs</string> </attribute> <layout class="QGridLayout" name="gridLayout_5"> <item row="3" column="0"> <widget class="QCheckBox" name="dstListNetsCheck"> <property name="text"> <string>To this list of network ranges</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="dstListIPsCheck"> <property name="text"> <string>To this list of IPs</string> </property> </widget> </item> <item row="2" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_7"> <item> <widget class="QPushButton" name="selectIPsListButton"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="system-search"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QLineEdit" name="dstListIPsLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></string> </property> </widget> </item> </layout> </item> <item row="0" column="0"> <widget class="QCheckBox" name="dstListsCheck"> <property name="text"> <string>To this list of domains</string> </property> </widget> </item> <item row="3" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_9"> <item> <widget class="QPushButton" name="selectNetsListButton"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="system-search"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QLineEdit" name="dstListNetsLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></string> </property> </widget> </item> </layout> </item> <item row="0" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_6"> <item> <widget class="QPushButton" name="selectListButton"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="system-search"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QLineEdit" name="dstListsLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></string> </property> </widget> </item> </layout> </item> <item row="1" column="0"> <widget class="QCheckBox" name="dstListRegexpCheck"> <property name="sizePolicy"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>To this list of domains (regular expressions)</string> </property> </widget> </item> <item row="1" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_10"> <item> <widget class="QPushButton" name="selectListRegexpButton"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="system-search"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QLineEdit" name="dstRegexpListsLine"> <property name="enabled"> <bool>false</bool> </property> <property name="toolTip"> <string><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></string> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab"> <attribute name="title"> <string>More</string> </attribute> <layout class="QGridLayout" name="gridLayout_6"> <item row="0" column="0"> <widget class="QCheckBox" name="sensitiveCheck"> <property name="toolTip"> <string><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></string> </property> <property name="text"> <string>Case-sensitive</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QCheckBox" name="nologCheck"> <property name="statusTip"> <string>Don't log connections that match this rule</string> </property> <property name="text"> <string>Don't log connections</string> </property> </widget> </item> <item row="2" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Maximum</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> </widget> </item> <item row="9" column="0"> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item row="1" column="0"> <widget class="QPlainTextEdit" name="ruleDescEdit"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="maximumSize"> <size> <width>16777215</width> <height>60</height> </size> </property> <property name="placeholderText"> <string>Description...</string> </property> </widget> </item> </layout> </widget> <resources/> <connections/> </ui> ���������������������������������������������������opensnitch-1.6.9/ui/opensnitch/res/stats.ui���������������������������������������������������������0000664�0000000�0000000�00000177671�15003540030�0020767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>StatsDialog</class> <widget class="QDialog" name="StatsDialog"> <property name="windowModality"> <enum>Qt::NonModal</enum> </property> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>863</width> <height>597</height> </rect> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>300</width> <height>220</height> </size> </property> <property name="font"> <font> <kerning>true</kerning> </font> </property> <property name="windowTitle"> <string>OpenSnitch Network Statistics</string> </property> <property name="windowIcon"> <iconset resource="resources.qrc"> <normaloff>:/pics/icon-white.svg</normaloff> <normalon>:/pics/icon-white.svg</normalon> <disabledoff>:/pics/icon-white.svg</disabledoff> <disabledon>:/pics/icon-white.svg</disabledon> <activeoff>:/pics/icon-white.svg</activeoff> <activeon>:/pics/icon-white.svg</activeon>:/pics/icon-white.svg</iconset> </property> <property name="sizeGripEnabled"> <bool>false</bool> </property> <property name="modal"> <bool>false</bool> </property> <layout class="QGridLayout" name="gridLayout_4"> <property name="leftMargin"> <number>4</number> </property> <property name="topMargin"> <number>2</number> </property> <property name="rightMargin"> <number>4</number> </property> <property name="bottomMargin"> <number>4</number> </property> <property name="horizontalSpacing"> <number>2</number> </property> <property name="verticalSpacing"> <number>4</number> </property> <item row="3" column="0"> <widget class="QFrame" name="navToolBar"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QHBoxLayout" name="horizontalLayout_17"> <property name="leftMargin"> <number>4</number> </property> <property name="topMargin"> <number>2</number> </property> <property name="rightMargin"> <number>4</number> </property> <property name="bottomMargin"> <number>4</number> </property> <item> <widget class="QLabel" name="label_4"> <property name="text"> <string>Filter</string> </property> </widget> </item> <item> <widget class="QComboBox" name="comboAction"> <item> <property name="text"> <string>-</string> </property> </item> <item> <property name="text"> <string>Allow</string> </property> <property name="icon"> <iconset theme="emblem-default"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>Deny</string> </property> <property name="icon"> <iconset theme="emblem-important"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>Reject</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> </widget> </item> <item> <widget class="QLineEdit" name="filterLine"> <property name="text"> <string notr="true"/> </property> <property name="frame"> <bool>true</bool> </property> <property name="placeholderText"> <string>Ex.: firefox</string> </property> <property name="clearButtonEnabled"> <bool>true</bool> </property> </widget> </item> <item> <spacer name="horizontalSpacer_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Minimum</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="prevButton"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QLabel" name="labelRowsCount"> <property name="text"> <string>0</string> </property> </widget> </item> <item> <widget class="QPushButton" name="nextButton"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-next"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QComboBox" name="limitCombo"> <property name="currentIndex"> <number>0</number> </property> <property name="sizeAdjustPolicy"> <enum>QComboBox::AdjustToContents</enum> </property> <item> <property name="text"> <string>50</string> </property> </item> <item> <property name="text"> <string>100</string> </property> </item> <item> <property name="text"> <string>200</string> </property> </item> <item> <property name="text"> <string>300</string> </property> </item> <item> <property name="text"> <string/> </property> </item> </widget> </item> <item> <widget class="QPushButton" name="cmdCleanSql"> <property name="toolTip"> <string>Delete all intercepted events</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="edit-clear-all"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="helpButton"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="help-browser"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </widget> </item> <item row="0" column="0"> <widget class="QFrame" name="frame"> <property name="frameShape"> <enum>QFrame::StyledPanel</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <property name="spacing"> <number>0</number> </property> <item row="0" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_10"> <item> <widget class="QPushButton" name="actionsButton"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="format-justify-fill"> <normaloff>.</normaloff>.</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="prefsButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="preferences-system"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="newRuleButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>32</horstretch> <verstretch>32</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Create a new rule</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="document-new"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="fwButton"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="security-high"/> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <spacer name="horizontalSpacer_9"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>60</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="nodeLabel"> <property name="text"> <string><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></string> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="label_2"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>11</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Status</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="statusLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>11</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>-</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QPushButton" name="startButton"> <property name="toolTip"> <string>Start or Stop interception</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="media-playback-start"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="checkable"> <bool>true</bool> </property> <property name="checked"> <bool>false</bool> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> </layout> </widget> </item> <item row="1" column="0"> <widget class="QTabWidget" name="tabWidget"> <property name="tabPosition"> <enum>QTabWidget::North</enum> </property> <property name="tabShape"> <enum>QTabWidget::Rounded</enum> </property> <property name="currentIndex"> <number>0</number> </property> <property name="documentMode"> <bool>true</bool> </property> <widget class="QWidget" name="tab"> <attribute name="icon"> <iconset theme="view-sort-ascending"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Events</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_8"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>4</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="horizontalLayout_16"> <property name="spacing"> <number>0</number> </property> <item> <widget class="GenericTableView" name="eventsTable"> <property name="styleSheet"> <string notr="true"/> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Plain</enum> </property> <property name="autoScroll"> <bool>false</bool> </property> <property name="editTriggers"> <set>QAbstractItemView::NoEditTriggers</set> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <attribute name="verticalHeaderVisible"> <bool>false</bool> </attribute> </widget> </item> <item> <widget class="QScrollBar" name="connectionsTableScrollBar"> <property name="styleSheet"> <string notr="true"/> </property> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab_8"> <attribute name="icon"> <iconset theme="network-workgroup"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Nodes</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>9</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="horizontalLayout_12"> <item> <widget class="QPushButton" name="cmdNodesBack"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QLabel" name="nodesLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item> <spacer name="horizontalSpacer_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="nodeActionsButton"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="format-justify-fill"/> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="nodeDeleteButton"> <property name="toolTip"> <string>Delete this node</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="edit-delete"/> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="nodePrefsButton"> <property name="toolTip"> <string>Show the preferences of this node</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="preferences-system"/> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="nodeStartButton"> <property name="toolTip"> <string>Start or stop interception of this node</string> </property> <property name="text"> <string notr="true"/> </property> <property name="icon"> <iconset theme="media-playback-start"/> </property> <property name="checkable"> <bool>true</bool> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <property name="spacing"> <number>0</number> </property> <item> <widget class="GenericTableView" name="nodesTable"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Plain</enum> </property> <property name="autoScroll"> <bool>false</bool> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <property name="sortingEnabled"> <bool>true</bool> </property> <attribute name="horizontalHeaderStretchLastSection"> <bool>true</bool> </attribute> <attribute name="verticalHeaderVisible"> <bool>false</bool> </attribute> <attribute name="verticalHeaderStretchLastSection"> <bool>false</bool> </attribute> </widget> </item> <item> <widget class="QScrollBar" name="verticalScrollBar"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab_3"> <attribute name="icon"> <iconset theme="address-book-new"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Rules</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> <item row="2" column="0"> <widget class="QSplitter" name="rulesSplitter"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <widget class="QTreeWidget" name="rulesTreePanel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="animated"> <bool>true</bool> </property> <property name="headerHidden"> <bool>true</bool> </property> <attribute name="headerStretchLastSection"> <bool>false</bool> </attribute> <column> <property name="text"> <string notr="true">1</string> </property> </column> <column> <property name="text"> <string>2</string> </property> </column> <item> <property name="text"> <string>Application rules</string> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> </font> </property> <property name="icon"> <iconset theme="system-run"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> </property> <item> <property name="text"> <string>Permanent</string> </property> <property name="font"> <font> <pointsize>10</pointsize> </font> </property> <property name="icon"> <iconset theme="security-medium"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> </property> </item> <item> <property name="text"> <string>Temporary</string> </property> <property name="font"> <font> <pointsize>10</pointsize> </font> </property> <property name="icon"> <iconset theme="edit-clear"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> </property> </item> </item> <item> <property name="text"> <string>Nodes</string> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> </font> </property> <property name="icon"> <iconset theme="network-workgroup"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> </property> </item> <item> <property name="text"> <string>System rules</string> </property> <property name="font"> <font> <weight>75</weight> <bold>true</bold> </font> </property> <property name="icon"> <iconset theme="security-high"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> </widget> <widget class="QWidget" name="horizontalLayoutWidget"> <layout class="QHBoxLayout" name="horizontalLayout_19"> <property name="spacing"> <number>0</number> </property> <item> <widget class="FirewallTableView" name="fwTable"> <property name="autoScroll"> <bool>false</bool> </property> <property name="editTriggers"> <set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed</set> </property> <property name="alternatingRowColors"> <bool>true</bool> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <property name="sortingEnabled"> <bool>true</bool> </property> <attribute name="horizontalHeaderStretchLastSection"> <bool>true</bool> </attribute> <attribute name="verticalHeaderMinimumSectionSize"> <number>25</number> </attribute> <attribute name="verticalHeaderDefaultSectionSize"> <number>42</number> </attribute> </widget> </item> <item> <widget class="GenericTableView" name="rulesTable"> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> <property name="showGrid"> <bool>false</bool> </property> </widget> </item> <item> <widget class="QScrollBar" name="rulesScrollBar"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </widget> </widget> </item> <item row="1" column="0"> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <layout class="QHBoxLayout" name="horizontalLayout_8"> <item> <widget class="QComboBox" name="comboRulesFilter"> <item> <property name="text"> <string>All applications</string> </property> <property name="icon"> <iconset theme="system-run"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>Permanent</string> </property> <property name="icon"> <iconset theme="security-medium"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>Temporary</string> </property> <property name="icon"> <iconset theme="edit-clear"> <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </item> <item> <property name="text"> <string>System rules</string> </property> <property name="icon"> <iconset theme="security-high"/> </property> </item> </widget> </item> <item> <spacer name="horizontalSpacer_11"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>30</width> <height>20</height> </size> </property> </spacer> </item> </layout> </item> </layout> </item> <item row="0" column="0"> <layout class="QHBoxLayout" name="rulesToolbarLayout"> <item> <widget class="QPushButton" name="cmdRulesBack"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QCheckBox" name="enableRuleCheck"> <property name="text"> <string>enable</string> </property> </widget> </item> <item> <widget class="QLabel" name="nodeRuleLabel"> <property name="text"> <string/> </property> </widget> </item> <item> <widget class="QLabel" name="ruleLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item> <widget class="QPushButton" name="editRuleButton"> <property name="toolTip"> <string>Edit rule</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="accessories-text-editor"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="delRuleButton"> <property name="toolTip"> <string>Delete rule</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="edit-delete"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab_4"> <attribute name="icon"> <iconset theme="computer"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Hosts</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_3"> <property name="leftMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QPushButton" name="cmdHostsBack"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QLabel" name="hostsLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_7"> <property name="spacing"> <number>0</number> </property> <item> <widget class="GenericTableView" name="hostsTable"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <property name="sortingEnabled"> <bool>false</bool> </property> <property name="cornerButtonEnabled"> <bool>false</bool> </property> <attribute name="horizontalHeaderStretchLastSection"> <bool>true</bool> </attribute> <attribute name="verticalHeaderStretchLastSection"> <bool>false</bool> </attribute> </widget> </item> <item> <widget class="QScrollBar" name="hostsScrollBar"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab_7"> <attribute name="icon"> <iconset theme="system-run"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Applications</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_4"> <property name="leftMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QPushButton" name="cmdProcsBack"> <property name="enabled"> <bool>true</bool> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QLabel" name="procsLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true"/> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item> <widget class="QPushButton" name="cmdProcDetails"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="system-search"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="flat"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_11"> <property name="spacing"> <number>0</number> </property> <item> <widget class="GenericTableView" name="procsTable"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Plain</enum> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <property name="sortingEnabled"> <bool>false</bool> </property> <property name="cornerButtonEnabled"> <bool>true</bool> </property> <attribute name="horizontalHeaderStretchLastSection"> <bool>true</bool> </attribute> <attribute name="verticalHeaderStretchLastSection"> <bool>false</bool> </attribute> </widget> </item> <item> <widget class="QScrollBar" name="procsScrollBar"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab_2"> <attribute name="icon"> <iconset theme="network-server"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Addresses</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_5"> <property name="leftMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> <widget class="QPushButton" name="cmdAddrsBack"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QLabel" name="addrsLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_13"> <property name="spacing"> <number>0</number> </property> <item> <widget class="GenericTableView" name="addrTable"> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <property name="sortingEnabled"> <bool>false</bool> </property> <property name="cornerButtonEnabled"> <bool>false</bool> </property> <attribute name="horizontalHeaderStretchLastSection"> <bool>true</bool> </attribute> <attribute name="verticalHeaderStretchLastSection"> <bool>false</bool> </attribute> </widget> </item> <item> <widget class="QScrollBar" name="addrsScrollBar"> <property name="maximum"> <number>50</number> </property> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab_5"> <attribute name="icon"> <iconset theme="network-wired"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Ports</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_6"> <property name="leftMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="horizontalLayout_5"> <item> <widget class="QPushButton" name="cmdPortsBack"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QLabel" name="portsLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_14"> <property name="spacing"> <number>0</number> </property> <item> <widget class="GenericTableView" name="portsTable"> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <property name="sortingEnabled"> <bool>false</bool> </property> <property name="cornerButtonEnabled"> <bool>false</bool> </property> <attribute name="horizontalHeaderStretchLastSection"> <bool>true</bool> </attribute> <attribute name="verticalHeaderStretchLastSection"> <bool>false</bool> </attribute> </widget> </item> <item> <widget class="QScrollBar" name="portsScrollBar"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </item> </layout> </widget> <widget class="QWidget" name="tab_6"> <attribute name="icon"> <iconset theme="system-users"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </attribute> <attribute name="title"> <string>Users</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_7"> <property name="leftMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <layout class="QHBoxLayout" name="horizontalLayout_6"> <item> <widget class="QPushButton" name="cmdUsersBack"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="go-previous"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QLabel" name="usersLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_15"> <property name="spacing"> <number>0</number> </property> <item> <widget class="GenericTableView" name="usersTable"> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="showGrid"> <bool>false</bool> </property> <property name="sortingEnabled"> <bool>false</bool> </property> <property name="cornerButtonEnabled"> <bool>false</bool> </property> <attribute name="horizontalHeaderStretchLastSection"> <bool>true</bool> </attribute> <attribute name="verticalHeaderStretchLastSection"> <bool>false</bool> </attribute> </widget> </item> <item> <widget class="QScrollBar" name="usersScrollBar"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> </layout> </item> </layout> </widget> </widget> </item> <item row="4" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_9"> <property name="spacing"> <number>6</number> </property> <item> <layout class="QHBoxLayout" name="statsLayout"> <item> <widget class="QLabel" name="label_5"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Connections</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="consLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>8</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>-</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="label_7"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Dropped</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="droppedLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>8</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>-</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="label"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Uptime</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="uptimeLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>8</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>-</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="label_3"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Rules</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="rulesLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>8</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>-</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> </layout> </item> <item> <spacer name="horizontalSpacer_10"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="label_6"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <pointsize>10</pointsize> <weight>75</weight> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Version</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> <item> <widget class="QLabel" name="daemonVerLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <family>DejaVu Sans</family> <pointsize>8</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>-</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="margin"> <number>5</number> </property> </widget> </item> </layout> </item> </layout> </widget> <customwidgets> <customwidget> <class>GenericTableView</class> <extends>QTableView</extends> <header>customwidgets.generictableview</header> </customwidget> <customwidget> <class>FirewallTableView</class> <extends>QTableView</extends> <header>customwidgets.firewalltableview</header> </customwidget> </customwidgets> <resources> <include location="resources.qrc"/> </resources> <connections/> </ui> �����������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/rules.py�������������������������������������������������������������0000664�0000000�0000000�00000025451�15003540030�0020171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtCore import QObject, pyqtSignal from opensnitch.database import Database from opensnitch.database.enums import RuleFields from opensnitch.config import Config import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() import os import json from slugify import slugify from datetime import datetime from google.protobuf.json_format import MessageToJson, Parse DefaultRulesPath = "/etc/opensnitchd/rules" # date format displayed on the GUI (created column) DBDateFieldFormat = "%Y-%m-%d %H:%M:%S" class Rule(): def __init__(self): pass @staticmethod def to_bool(s): return s == 'True' @staticmethod def new_empty(): pass @staticmethod def new_from_records(records): """Creates a new protobuf Rule from DB records. Fields of the record are in the order defined on the DB. """ rule = ui_pb2.Rule(name=records.value(RuleFields.Name)) rule.enabled = Rule.to_bool(records.value(RuleFields.Enabled)) rule.precedence = Rule.to_bool(records.value(RuleFields.Precedence)) rule.action = records.value(RuleFields.Action) rule.duration = records.value(RuleFields.Duration) rule.operator.type = records.value(RuleFields.OpType) rule.operator.sensitive = Rule.to_bool(records.value(RuleFields.OpSensitive)) rule.operator.operand = records.value(RuleFields.OpOperand) rule.operator.data = "" if records.value(RuleFields.OpData) == None else str(records.value(RuleFields.OpData)) rule.description = records.value(RuleFields.Description) rule.nolog = Rule.to_bool(records.value(RuleFields.NoLog)) created = int(datetime.now().timestamp()) if records.value(RuleFields.Created) != "": created = int(datetime.strptime( records.value(RuleFields.Created), DBDateFieldFormat ).timestamp()) rule.created = created try: # Operator list is always saved as json string to the db, # so we need to load the json string. if rule.operator.type == Config.RULE_TYPE_LIST: operators = json.loads(rule.operator.data) for op in operators: rule.operator.list.extend([ ui_pb2.Operator( type=op['type'], operand=op['operand'], sensitive=False if op.get('sensitive') == None else op['sensitive'], data="" if op.get('data') == None else op['data'] ) ]) rule.operator.data = "" except Exception as e: print("new_from_records exception parsing operartor list:", e) return rule class Rules(QObject): __instance = None updated = pyqtSignal(int) LOG_TAG = "[Rules]: " @staticmethod def instance(): if Rules.__instance == None: Rules.__instance = Rules() return Rules.__instance def __init__(self): QObject.__init__(self) self._db = Database.instance() def add(self, time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created): # don't add rule if the user has selected to exclude temporary # rules if duration in Config.RULES_DURATION_FILTER: return self._db.insert("rules", "(time, node, name, description, enabled, precedence, nolog, action, duration, operator_type, operator_sensitive, operator_operand, operator_data, created)", (time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created), action_on_conflict="REPLACE") def add_rules(self, addr, rules): try: for _,r in enumerate(rules): # Operator list is always saved as json string to the db. rjson = json.loads(MessageToJson(r)) if r.operator.type == Config.RULE_TYPE_LIST and rjson.get('operator') != None and rjson.get('operator').get('list') != None: r.operator.data = json.dumps(rjson.get('operator').get('list')) self.add(datetime.now().strftime(DBDateFieldFormat), addr, r.name, r.description, str(r.enabled), str(r.precedence), str(r.nolog), r.action, r.duration, r.operator.type, str(r.operator.sensitive), r.operator.operand, r.operator.data, str(datetime.fromtimestamp(r.created).strftime(DBDateFieldFormat))) return True except Exception as e: print(self.LOG_TAG + " exception adding node rules to db: ", e) return False def delete(self, name, addr, callback): rule = ui_pb2.Rule(name=name) rule.enabled = False rule.action = "" rule.duration = "" rule.operator.type = "" rule.operator.operand = "" rule.operator.data = "" if not self._db.delete_rule(rule.name, addr): return None return rule def delete_by_field(self, field, values): return self._db.delete_rules_by_field(field, values) def exists(self, rule, node_addr): return self._db.rule_exists(rule, node_addr) def new_unique_name(self, rule_name, node_addr, prefix): """generate a new name, if the supplied one already exists """ if self._db.get_rule(rule_name, node_addr).next() == False: return rule_name for idx in range(0, 100): new_rule_name = "{0}-{1}".format(rule_name, idx) if self._db.get_rule(new_rule_name, node_addr).next() == False: return new_rule_name return rule_name def update_time(self, time, name, addr): """Updates the time of a rule, whenever a new connection matched a rule. """ self._db.update("rules", "time=?", (time, name, addr), "name=? AND node=?", action_on_conflict="OR REPLACE" ) def _timestamp_to_rfc3339(self, time): """converts timestamp to rfc3339 format""" return "{0}Z".format( datetime.fromtimestamp(time).isoformat(timespec='microseconds') ) def rule_to_json(self, node, rule_name): try: records = self._db.get_rule(rule_name, node) if records == None or records == -1: return None if not records.next(): return None rule = Rule.new_from_records(records) # exclude this field when exporting to json tempRule = MessageToJson(rule) jRule = json.loads(tempRule) jRule['created'] = self._timestamp_to_rfc3339(rule.created) return json.dumps(jRule, indent=" ") except Exception as e: print("rule_to_json() exception:", e) return None def _export_rule_common(self, node, records, outdir): try: rule = Rule.new_from_records(records) rulename = rule.name if ".json" not in rulename: rulename = rulename + ".json" with open(outdir + "/" + rulename, 'w') as jsfile: actual_json_text = MessageToJson(rule) jRule = json.loads(actual_json_text) jRule['created'] = self._timestamp_to_rfc3339(rule.created) actual_json_text = json.dumps(jRule, indent=" ") jsfile.write( actual_json_text ) return True except Exception as e: print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e) return False def export_rule(self, node, rule_name, outdir): """Gets the rule from the DB and writes it out to a directory. A new directory per node will be created. """ try: records = self._db.get_rule(rule_name, node) if records.next() == False: print("export_rule() get_error 2:", records) return False rulesdir = outdir + "/" + slugify(node) try: os.makedirs(rulesdir, 0o700) except Exception as e: print("exception creating dirs:", e) return self._export_rule_common(node, records, rulesdir) except Exception as e: print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e) return False def export_rules(self, node, outdir): """Gets the rules from the DB and writes them out to a directory. A new directory per node will be created. """ records = self._db.get_rules(node) if records == None: return False rulesdir = outdir + "/" + slugify(node) try: os.makedirs(rulesdir, 0o700) except Exception as e: print("exception creating dirs:", e) try: while records.next() != False: self._export_rule_common(node, records, rulesdir) except Exception as e: print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e) return False return True def import_rules(self, rulesdir): """Read a directory with rules in json format, and parse them out to protobuf Returns a list of rules on success, or None on error. """ try: rules = [] for rulename in os.listdir(rulesdir): with open(rulesdir + "/" + rulename, 'r') as f: jsrule = f.read() # up until v1.6.5/v1.7.0, 'created' field was exported as timestamp. # since > v1.6.5 it's exported in rfc3339 format, so if we fail to # parse the rule, we'll try to convert the 'created' value from # timestamp to rfc3339. try: pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True) except: jRule = json.loads(jsrule) created = int(datetime.strptime( jRule['created'], "%Y-%m-%dT%H:%M:%S.%fZ" ).timestamp()) jRule['created'] = created jsrule = json.dumps(jRule) pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True) rules.append(pb_rule) return rules except Exception as e: print(self.LOG_TAG, "import_rules() exception:", e) return None �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/service.py�����������������������������������������������������������0000664�0000000�0000000�00000113003�15003540030�0020466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import QCoreApplication as QC from datetime import datetime, timedelta from threading import Thread, Lock, Event import grpc import os import sys import json import copy path = os.path.abspath(os.path.dirname(__file__)) sys.path.append(path) import opensnitch.proto as proto ui_pb2, ui_pb2_grpc = proto.import_() from opensnitch.dialogs.prompt import PromptDialog from opensnitch.dialogs.stats import StatsDialog from opensnitch.notifications import DesktopNotifications from opensnitch.firewall import Rules as FwRules from opensnitch.nodes import Nodes from opensnitch.config import Config from opensnitch.version import version from opensnitch.database import Database from opensnitch.utils import Utils, CleanerTask, Themes from opensnitch.utils import Message, languages from opensnitch.utils.xdg import Autostart class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject): _new_remote_trigger = QtCore.pyqtSignal(str, ui_pb2.PingRequest) _node_actions_trigger = QtCore.pyqtSignal(dict) _update_stats_trigger = QtCore.pyqtSignal(str, str, ui_pb2.PingRequest) _add_alert_trigger = QtCore.pyqtSignal(str, str, ui_pb2.Alert) _version_warning_trigger = QtCore.pyqtSignal(str, str) _status_change_trigger = QtCore.pyqtSignal(bool) _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) _show_message_trigger = QtCore.pyqtSignal(str, str, int, int) # .desktop filename located under /usr/share/applications/ DESKTOP_FILENAME = "opensnitch_ui.desktop" def __init__(self, app, on_exit, start_in_bg=False): super(UIService, self).__init__() self.MENU_ENTRY_STATS = QtCore.QCoreApplication.translate("contextual_menu", "Open main window") self.MENU_ENTRY_FW_ENABLE = QtCore.QCoreApplication.translate("contextual_menu", "Enable") self.MENU_ENTRY_FW_DISABLE = QtCore.QCoreApplication.translate("contextual_menu", "Disable") self.MENU_ENTRY_HELP = QtCore.QCoreApplication.translate("contextual_menu", "Help") self.MENU_ENTRY_CLOSE = QtCore.QCoreApplication.translate("contextual_menu", "Close") # set of actions that must be performed on the main thread self.NODE_ADD = 0 self.NODE_UPDATE = 1 self.NODE_DELETE = 2 self.ADD_RULE = 3 self.DELETE_RULE = 4 self._cfg = Config.init() self._db = Database.instance() db_file=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY) db_jrnl_wal=self._cfg.getBool(Config.DEFAULT_DB_JRNL_WAL) db_status, db_error = self._db.initialize( dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY), dbfile=db_file, dbjrnl_wal=db_jrnl_wal ) if db_status is False: Message.ok( QtCore.QCoreApplication.translate("preferences", "Warning"), QtCore.QCoreApplication.translate("preferences", "The DB is corrupted and it's not safe to continue.<br>\ Remove, backup or recover the file before continuing.<br><br>\ Corrupted database file: {0}".format(db_file)), QtWidgets.QMessageBox.Warning) sys.exit(-1) self._db_sqlite = self._db.get_db() self._last_ping = None self._version_warning_shown = False self._asking = False self._connected = False self._fw_enabled = False self._path = os.path.abspath(os.path.dirname(__file__)) self._app = app self._on_exit = on_exit self._exit = False self._msg = QtWidgets.QMessageBox() self._remote_lock = Lock() self._remote_stats = {} self._autostart = Autostart() self.translator = None self._init_translation() self._themes = Themes() self._desktop_notifications = DesktopNotifications() self._setup_interfaces() self._setup_icons() self._prompt_dialog = PromptDialog(appicon=self.white_icon) self._stats_dialog = StatsDialog(dbname="general", db=self._db, appicon=self.white_icon) self._setup_tray() self._setup_slots() self._nodes = Nodes.instance() self._nodes.reset_status() self._last_stats = {} self._last_items = { 'hosts':{}, 'procs':{}, 'addrs':{}, 'ports':{}, 'users':{} } if not start_in_bg: self._show_gui_if_tray_not_available() self._cleaner = None if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST): self._start_db_cleaner() self._cfg.setRulesDurationFilter( self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES), self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) ) if self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES): self._nodes.delete_rule_by_field(Config.DURATION_FIELD, Config.RULES_DURATION_FILTER) # https://gist.github.com/pklaus/289646 def _setup_interfaces(self): namestr, outbytes = Utils.get_interfaces() self._interfaces = {} for i in range(0, outbytes, 40): name = namestr[i:i+16].split(b'\0', 1)[0] addr = namestr[i+20:i+24] self._interfaces[name] = "%d.%d.%d.%d" % (int(addr[0]), int(addr[1]), int(addr[2]), int(addr[3])) def _setup_slots(self): # https://stackoverflow.com/questions/40288921/pyqt-after-messagebox-application-quits-why self._app.setQuitOnLastWindowClosed(False) self._version_warning_trigger.connect(self._on_diff_versions) self._new_remote_trigger.connect(self._on_new_remote) self._node_actions_trigger.connect(self._on_node_actions) self._update_stats_trigger.connect(self._on_update_stats) self._add_alert_trigger.connect(self._on_new_alert) self._status_change_trigger.connect(self._on_status_changed) self._stats_dialog._shown_trigger.connect(self._on_stats_dialog_shown) self._stats_dialog._status_changed_trigger.connect(self._on_stats_status_changed) self._stats_dialog.settings_saved.connect(self._on_settings_saved) self._stats_dialog.close_trigger.connect(self._on_close) self._show_message_trigger.connect(self._show_systray_message) def _setup_icons(self): self.off_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-off.png")) self.off_icon = QtGui.QIcon() self.off_icon.addPixmap(self.off_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) self.white_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-white.svg")) self.white_icon = QtGui.QIcon() self.white_icon.addPixmap(self.white_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) self.red_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-red.png")) self.red_icon = QtGui.QIcon() self.red_icon.addPixmap(self.red_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) self.pause_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-pause.png")) self.pause_icon = QtGui.QIcon() self.pause_icon.addPixmap(self.pause_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) self.alert_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-alert.png")) self.alert_icon = QtGui.QIcon() self.alert_icon.addPixmap(self.alert_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) self._app.setWindowIcon(self.white_icon) # NOTE: only available since pyqt 5.7 if hasattr(self._app, "setDesktopFileName"): self._app.setDesktopFileName(self.DESKTOP_FILENAME) def _setup_tray(self): self._tray = QtWidgets.QSystemTrayIcon(self.off_icon) self._tray.show() self._menu = QtWidgets.QMenu() self._tray.setContextMenu(self._menu) self._tray.activated.connect(self._on_tray_icon_activated) self._menu.addAction(self.MENU_ENTRY_STATS).triggered.connect(self._show_stats_dialog) self._menu_enable_fw = self._menu.addAction(self.MENU_ENTRY_FW_DISABLE) self._menu_enable_fw.setEnabled(False) self._menu_enable_fw.triggered.connect(self._on_enable_interception_clicked) self._menu.addSeparator() self._menu_autostart = self._menu.addAction("Autostart") self._menu_autostart.setCheckable(True) self._menu_autostart.setChecked(self._autostart.isEnabled()) self._menu_autostart.triggered.connect(self._on_switch_autostart) self._menu.addSeparator() self._menu.addAction(self.MENU_ENTRY_HELP).triggered.connect( lambda: QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_CONFIG_URL)) ) self._menu.addAction(self.MENU_ENTRY_CLOSE).triggered.connect(self._on_close) self._menu.aboutToShow.connect(self._on_show_menu) def _on_switch_autostart(self): try: self._autostart.enable(self._menu_autostart.isChecked()) except Exception as e: self._desktop_notifications.show( QC.translate("stats", "Warning"), QC.translate("stats", str(e)) ) def _on_show_menu(self): self._menu_autostart.setChecked(self._autostart.isEnabled()) def _show_gui_if_tray_not_available(self): """If the system tray is not available or ready, show the GUI after 10s. This delay helps to skip showing up the GUI when DEs' autologin is on. """ tray = self._tray gui = self._stats_dialog def __show_gui(): if not tray.isSystemTrayAvailable(): self._show_systray_msg_error() gui.show() QtCore.QTimer.singleShot(10000, __show_gui) def _show_systray_msg_error(self): print("") print("WARNING: system tray not available. On GNOME you need the extension gnome-shell-extension-appindicator.") print("\tRead more:", Config.HELP_SYSTRAY_WARN) print("\tIf you want to start OpenSnitch GUI in background even if tray not available, use --background argument.") print("") hide_msg = self._cfg.getBool(Config.DEFAULT_HIDE_SYSTRAY_WARN) if hide_msg: return self._desktop_notifications.show( QC.translate("stats", "WARNING"), QC.translate("stats", """System tray not available. Read more: {0} """.format(Config.HELP_SYSTRAY_WARN)), os.path.join(self._path, "res/icon-white.svg") ) self._cfg.setSettings(Config.DEFAULT_HIDE_SYSTRAY_WARN, True) def _on_tray_icon_activated(self, reason): if reason == QtWidgets.QSystemTrayIcon.Trigger or reason == QtWidgets.QSystemTrayIcon.MiddleClick: if self._stats_dialog.isVisible() and not self._stats_dialog.isMinimized(): self._stats_dialog.hide() elif self._stats_dialog.isVisible() and self._stats_dialog.isMinimized() and not self._stats_dialog.isMaximized(): self._stats_dialog.hide() self._stats_dialog.showNormal() elif self._stats_dialog.isVisible() and self._stats_dialog.isMinimized() and self._stats_dialog.isMaximized(): self._stats_dialog.hide() self._stats_dialog.showMaximized() else: self._stats_dialog.show() def _on_close(self): self._exit = True self._tray.setIcon(self.off_icon) self._app.processEvents() self._nodes.stop_notifications() self._nodes.update_all(Nodes.OFFLINE) self._db.vacuum() self._db.optimize() self._db.close() self._stop_db_cleaner() self._on_exit() def _show_stats_dialog(self): if self._connected and self._fw_enabled: self._tray.setIcon(self.white_icon) self._stats_dialog.show() @QtCore.pyqtSlot(bool) def _on_stats_status_changed(self, enabled): self._update_fw_status(enabled) @QtCore.pyqtSlot(bool) def _on_status_changed(self, enabled): self._set_daemon_connected(enabled) @QtCore.pyqtSlot(str, str) def _on_diff_versions(self, daemon_ver, ui_ver): if self._version_warning_shown == False: self._msg.setIcon(QtWidgets.QMessageBox.Warning) self._msg.setWindowTitle("OpenSnitch version mismatch!") self._msg.setText(("You are running version <b>%s</b> of the daemon, while the UI is at version " + \ "<b>%s</b>, they might not be fully compatible.") % (daemon_ver, ui_ver)) self._msg.setStandardButtons(QtWidgets.QMessageBox.Ok) self._msg.show() self._version_warning_shown = True @QtCore.pyqtSlot(str, str, ui_pb2.PingRequest) def _on_update_stats(self, proto, addr, request): main_need_refresh, details_need_refresh = self._populate_stats(self._db, proto, addr, request.stats) is_local_request = self._is_local_request(proto, addr) self._stats_dialog.update(is_local_request, request.stats, main_need_refresh or details_need_refresh) @QtCore.pyqtSlot(str, str, ui_pb2.Alert) def _on_new_alert(self, proto, addr, alert): try: is_local = self._is_local_request(proto, addr) icon = QtWidgets.QSystemTrayIcon.Information _title = QtCore.QCoreApplication.translate("messages", "Info") atype = "INFO" if alert.type == ui_pb2.Alert.ERROR: atype = "ERROR" _title = QtCore.QCoreApplication.translate("messages", "Error") icon = QtWidgets.QSystemTrayIcon.Critical if alert.type == ui_pb2.Alert.WARNING: atype = "WARNING" _title = QtCore.QCoreApplication.translate("messages", "Warning") icon = QtWidgets.QSystemTrayIcon.Warning body = "" what = "GENERIC" if alert.what == ui_pb2.Alert.GENERIC: body = alert.text elif alert.what == ui_pb2.Alert.KERNEL_EVENT: body = "%s\n%s" % (alert.text, alert.proc.path) what = "KERNEL EVENT" if is_local is False: body = "node: {0}:{1}\n\n{2}\n{3}".format(proto, addr, alert.text, alert.proc.path) if alert.action == ui_pb2.Alert.SHOW_ALERT: urgency = DesktopNotifications.URGENCY_NORMAL if alert.priority == ui_pb2.Alert.LOW: urgency = DesktopNotifications.URGENCY_LOW elif alert.priority == ui_pb2.Alert.HIGH: urgency = DesktopNotifications.URGENCY_CRITICAL self._show_message_trigger.emit(_title, body, icon, urgency) else: print("PostAlert() unknown alert action:", alert.action) except Exception as e: print("PostAlert() exception:", e) return ui_pb2.MsgResponse(id=1) @QtCore.pyqtSlot(str, ui_pb2.PingRequest) def _on_new_remote(self, addr, request): self._remote_stats[addr] = { 'last_ping': datetime.now(), 'dialog': StatsDialog(address=addr, dbname=addr, db=self._db) } self._remote_stats[addr]['dialog'].daemon_connected = True self._remote_stats[addr]['dialog'].update(addr, request.stats) self._remote_stats[addr]['dialog'].show() @QtCore.pyqtSlot() def _on_stats_dialog_shown(self): if self._connected: if self._fw_enabled: self._tray.setIcon(self.white_icon) else: self._tray.setIcon(self.pause_icon) else: self._tray.setIcon(self.off_icon) @QtCore.pyqtSlot(ui_pb2.NotificationReply) def _on_notification_reply(self, reply): if reply.code == ui_pb2.ERROR: self._tray.showMessage("Error", reply.data, QtWidgets.QSystemTrayIcon.Information, 5000) def _on_remote_stats_menu(self, address): self._remote_stats[address]['dialog'].show() @QtCore.pyqtSlot(str, str, int, int) def _show_systray_message(self, title, body, icon, urgency): def callback_open_clicked(notifObject, action): if action == DesktopNotifications.ACTION_ID_OPEN: self._stats_dialog.show() #self._stats_dialog.raise() self._stats_dialog.activateWindow() if self._desktop_notifications.are_enabled(): timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15) if self._desktop_notifications.is_available() and self._cfg.getInt(Config.NOTIFICATIONS_TYPE, 1) == Config.NOTIFICATION_TYPE_SYSTEM: try: self._desktop_notifications.show( title, body, os.path.join(self._path, "res/icon-white.svg"), callback=callback_open_clicked ) except: self._tray.showMessage(title, body, icon, timeout * 1000) else: self._tray.showMessage(title, body, icon, timeout * 1000) if icon == QtWidgets.QSystemTrayIcon.NoIcon: self._tray.setIcon(self.alert_icon) def _on_enable_interception_clicked(self): self._enable_interception(self._fw_enabled) @QtCore.pyqtSlot() def _on_settings_saved(self): if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST): if self._cleaner != None: self._stop_db_cleaner() self._start_db_cleaner() elif self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST) == False and self._cleaner != None: self._stop_db_cleaner() theme_idx, theme_name, theme_density = self._themes.get_saved_theme() if theme_idx > 0: self._themes.load_theme(self._app) def _init_translation(self): if self.translator: self._app.removeTranslator(self.translator) saved_lang = self._cfg.getSettings(Config.DEFAULT_LANGUAGE) self.translator = languages.init(saved_lang) self._app.installTranslator(self.translator) def _stop_db_cleaner(self): if self._cleaner != None: self._cleaner.stop() self._cleaner = None def _start_db_cleaner(self): def _cleaner_task(db): oldest = self._cfg.getInt(self._cfg.DEFAULT_DB_MAX_DAYS, 1) db.purge_oldest(oldest) interval = self._cfg.getInt(self._cfg.DEFAULT_DB_PURGE_INTERVAL, 5) self._cleaner = CleanerTask(interval, _cleaner_task) self._cleaner.start() def _update_fw_status(self, enabled): """_update_fw_status updates the status of the menu entry to disable or enable the firewall of the daemon. """ self._fw_enabled = enabled if self._connected == False: return self._stats_dialog.update_interception_status(enabled) if enabled: self._tray.setIcon(self.white_icon) self._menu_enable_fw.setText(self.MENU_ENTRY_FW_DISABLE) else: self._tray.setIcon(self.pause_icon) self._menu_enable_fw.setText(self.MENU_ENTRY_FW_ENABLE) def _set_daemon_connected(self, connected): """_set_daemon_connected only updates the connection status of the daemon(s), regardless if the fw is enabled or not. There're 3 states: - daemon connected - daemon not connected - daemon connected and firewall enabled/disabled """ self._stats_dialog.daemon_connected = connected self._connected = connected # if there're more than 1 node, override connection status if self._nodes.count() >= 1: self._connected = True self._stats_dialog.daemon_connected = True if self._nodes.count() == 1: self._menu_enable_fw.setEnabled(True) if self._nodes.count() == 0 or self._nodes.count() > 1: self._menu_enable_fw.setEnabled(False) self._stats_dialog.update_status() if self._connected: self._tray.setIcon(self.white_icon) else: self._fw_enabled = False self._tray.setIcon(self.off_icon) def _enable_interception(self, enable): if self._connected == False: return if self._nodes.count() == 0: self._tray.showMessage("No nodes connected", "", QtWidgets.QSystemTrayIcon.Information, 5000) return if self._nodes.count() > 1: print("enable interception for all nodes not supported yet") return if enable: nid, noti = self._nodes.stop_interception(_callback=self._notification_callback) else: nid, noti = self._nodes.start_interception(_callback=self._notification_callback) self._fw_enabled = not enable self._stats_dialog._status_changed_trigger.emit(not enable) def _is_local_request(self, proto, addr): if proto == "unix" or proto == "unix-abstract": return True elif proto == "ipv4" or proto == "ipv6": for name, ip in self._interfaces.items(): if addr == ip: return True return False def _get_peer(self, peer): """ server -> client 127.0.0.1:50051 -> ipv4:127.0.0.1:52032 [::]:50051 -> ipv6:[::1]:59680 0.0.0.0:50051 -> ipv6:[::1]:59654 """ return self._nodes.get_addr(peer) def _delete_node(self, peer): try: proto, addr = self._get_peer(peer) if addr in self._last_stats: del self._last_stats[addr] for table in self._last_items: if addr in self._last_items[table]: del self._last_items[table][addr] self._nodes.update(peer, Nodes.OFFLINE) self._nodes.delete(peer) self._stats_dialog.update(True, None, True) except Exception as e: print("_delete_node() exception:", e) def _populate_stats(self, db, proto, addr, stats): main_need_refresh = False details_need_refresh = False try: if db == None: print("populate_stats() db None") return main_need_refresh, details_need_refresh peer = proto+":"+addr _node = self._nodes.get_node(peer) if _node == None: return main_need_refresh, details_need_refresh # TODO: move to nodes.add_node() version = _node['data'].version if _node != None else "" hostname = _node['data'].name if _node != None else "" db.insert("nodes", "(addr, status, hostname, daemon_version, daemon_uptime, " \ "daemon_rules, cons, cons_dropped, version, last_connection)", (peer, Nodes.ONLINE, hostname, stats.daemon_version, str(timedelta(seconds=stats.uptime)), stats.rules, stats.connections, stats.dropped, version, datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) if addr not in self._last_stats: self._last_stats[addr] = [] db.transaction() for event in stats.events: if event.unixnano in self._last_stats[addr]: continue main_need_refresh=True db.insert("connections", "(time, node, action, protocol, src_ip, src_port, dst_ip, dst_host, dst_port, uid, pid, process, process_args, process_cwd, rule)", (str(datetime.fromtimestamp(event.unixnano/1000000000)), peer, event.rule.action, event.connection.protocol, event.connection.src_ip, str(event.connection.src_port), event.connection.dst_ip, event.connection.dst_host, str(event.connection.dst_port), str(event.connection.user_id), str(event.connection.process_id), event.connection.process_path, " ".join(event.connection.process_args), event.connection.process_cwd, event.rule.name), action_on_conflict="IGNORE" ) self._nodes.update_rule_time( str(datetime.fromtimestamp(event.unixnano/1000000000)), event.rule.name, peer ) db.commit() details_need_refresh = self._populate_stats_details(db, addr, stats) self._last_stats[addr] = [] for event in stats.events: self._last_stats[addr].append(event.unixnano) except Exception as e: print("_populate_stats() exception: ", e) return main_need_refresh, details_need_refresh def _populate_stats_details(self, db, addr, stats): need_refresh = False changed = self._populate_stats_events(db, addr, stats, "hosts", ("what", "hits"), (1,2), stats.by_host.items()) if changed: need_refresh = True changed = self._populate_stats_events(db, addr, stats, "procs", ("what", "hits"), (1,2), stats.by_executable.items()) if changed: need_refresh = True changed = self._populate_stats_events(db, addr, stats, "addrs", ("what", "hits"), (1,2), stats.by_address.items()) if changed: need_refresh = True changed = self._populate_stats_events(db, addr, stats, "ports", ("what", "hits"), (1,2), stats.by_port.items()) if changed: need_refresh = True changed = self._populate_stats_events(db, addr, stats, "users", ("what", "hits"), (1,2), stats.by_uid.items()) if changed: need_refresh = True return need_refresh def _populate_stats_events(self, db, addr, stats, table, colnames, cols, items): fields = [] values = [] need_refresh = False try: if addr not in self._last_items[table].keys(): self._last_items[table][addr] = {} if items == self._last_items[table][addr]: return need_refresh for row, event in enumerate(items): if event in self._last_items[table][addr]: continue need_refresh = True what, hits = event # FIXME: this is suboptimal # BUG: there can be users with same id on different machines but with different names if table == "users": what = Utils.get_user_id(what) fields.append(what) values.append(int(hits)) # FIXME: default action on conflict is to replace. If there're multiple nodes connected, # stats are painted once per node on each update. if need_refresh: db.insert_batch(table, colnames, cols, fields, values) self._last_items[table][addr] = items except Exception as e: print("details exception: ", e) return need_refresh def _overwrite_nodes_config(self, node_config): """Overwrite daemon's DefaultAction value, with the one defined by the GUI. It'll only be valid while the daemon is connected to the GUI (it's not saved to disk). """ newconf = copy.deepcopy(node_config) _default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) try: temp_cfg = json.loads(newconf.config) if _default_action == Config.ACTION_ALLOW_IDX: temp_cfg['DefaultAction'] = Config.ACTION_ALLOW else: temp_cfg['DefaultAction'] = Config.ACTION_DENY print("Setting daemon DefaultAction to:", temp_cfg['DefaultAction']) newconf.config = json.dumps(temp_cfg) except Exception as e: print("error parsing node's configuration:", e) return node_config return newconf @QtCore.pyqtSlot(dict) def _on_node_actions(self, kwargs): if kwargs['action'] == self.NODE_ADD: n, addr = self._nodes.add(kwargs['peer'], kwargs['node_config']) if n != None: self._nodes.add_fw_rules( addr, FwRules.to_dict(kwargs['node_config'].systemFirewall.SystemRules) ) self._status_change_trigger.emit(True) # if there're more than one node, we can't update the status # based on the fw status, only if the daemon is running or not if self._nodes.count() <= 1: self._update_fw_status(kwargs['node_config'].isFirewallRunning) else: self._update_fw_status(True) elif kwargs['action'] == self.ADD_RULE: rule = kwargs['rule'] proto, addr = self._get_peer(kwargs['peer']) self._nodes.add_rule((datetime.now().strftime("%Y-%m-%d %H:%M:%S")), "{0}:{1}".format(proto, addr), rule.name, rule.description, str(rule.enabled), str(rule.precedence), str(rule.nolog), rule.action, rule.duration, rule.operator.type, str(rule.operator.sensitive), rule.operator.operand, rule.operator.data, str(datetime.fromtimestamp(rule.created).strftime("%Y-%m-%d %H:%M:%S")) ) if rule.operator.type == Config.RULE_TYPE_LIST: # reset list operator data before sending it back to the # daemon. rule.operator.data = "" elif kwargs['action'] == self.DELETE_RULE: self._db.delete_rule(kwargs['name'], kwargs['addr']) elif kwargs['action'] == self.NODE_DELETE: self._delete_node(kwargs['peer']) def OpenWindow(self): self._stats_dialog.show() def close(self): self._on_close() def PostAlert(self, alert, context): proto, addr = self._get_peer(context.peer()) self._add_alert_trigger.emit(proto, addr, alert) return ui_pb2.MsgResponse(id=0) def Ping(self, request, context): try: self._last_ping = datetime.now() if Utils.check_versions(request.stats.daemon_version): self._version_warning_trigger.emit(request.stats.daemon_version, version) proto, addr = self._get_peer(context.peer()) # do not update db here, do it on the main thread self._update_stats_trigger.emit(proto, addr, request) #else: # with self._remote_lock: # # XXX: disable this option for now # # opening several dialogs only updates one of them. # if addr not in self._remote_stats: # self._new_remote_trigger.emit(addr, request) # else: # self._populate_stats(self._remote_stats[addr]['dialog'].get_db(), proto, addr, request.stats) # self._remote_stats[addr]['dialog'].update(addr, request.stats) except Exception as e: print("Ping exception: ", e) return ui_pb2.PingReply(id=request.id) def AskRule(self, request, context): #def callback(ntf, action, connection): # TODO #if self._desktop_notifications.support_actions(): # self._desktop_notifications.ask(request, callback) # TODO: allow connections originated from ourselves: os.getpid() == request.pid) self._asking = True peer = context.peer() proto, addr = self._get_peer(peer) rule, timeout_triggered = self._prompt_dialog.promptUser(request, self._is_local_request(proto, addr), peer) self._last_ping = datetime.now() self._asking = False if rule == None: return None if timeout_triggered: _title = request.process_path if _title == "": _title = "%s:%d (%s)" % (request.dst_host if request.dst_host != "" else request.dst_ip, request.dst_port, request.protocol) node_text = "" if self._is_local_request(proto, addr) else "on node {0}:{1}".format(proto, addr) self._show_message_trigger.emit(_title, "{0} action applied {1}\nCommand line: {2}" .format(rule.action, node_text, " ".join(request.process_args)), QtWidgets.QSystemTrayIcon.NoIcon, DesktopNotifications.URGENCY_NORMAL) if rule.duration in Config.RULES_DURATION_FILTER: self._node_actions_trigger.emit( { 'action': self.DELETE_RULE, 'name': rule.name, 'addr': peer } ) else: self._node_actions_trigger.emit( { 'action': self.ADD_RULE, 'peer': peer, 'rule': rule } ) return rule def Subscribe(self, node_config, context): """ Accept and collect nodes. It keeps a connection open with each client, in order to send them notifications. @doc: https://grpc.github.io/grpc/python/grpc.html#service-side-context """ # if the exit mark is set, don't accept new connections. # db vacuum operation may take a lot of time to complete. if self._exit: context.cancel() return try: self._node_actions_trigger.emit({ 'action': self.NODE_ADD, 'peer': context.peer(), 'node_config': node_config }) # force events processing, to add the node ^ before the # Notifications() call arrives. self._app.processEvents() proto, addr = self._get_peer(context.peer()) if self._is_local_request(proto, addr) == False: self._show_message_trigger.emit( QtCore.QCoreApplication.translate("stats", "New node connected"), "({0})".format(context.peer()), QtWidgets.QSystemTrayIcon.Information, DesktopNotifications.URGENCY_LOW ) except Exception as e: print("[Notifications] exception adding new node:", e) context.cancel() newconf = self._overwrite_nodes_config(node_config) return newconf def Notifications(self, node_iter, context): """ Accept and collect nodes. It keeps a connection open with each client, in order to send them notifications. @doc: https://grpc.github.io/grpc/python/grpc.html#service-side-context @doc: https://grpc.io/docs/what-is-grpc/core-concepts/ """ proto, addr = self._get_peer(context.peer()) _node = self._nodes.get_node("%s:%s" % (proto, addr)) if _node == None: return stop_event = Event() def _on_client_closed(): stop_event.set() self._node_actions_trigger.emit( {'action': self.NODE_DELETE, 'peer': context.peer(), }) self._status_change_trigger.emit(False) # TODO: handle the situation when a node disconnects, and the # remaining node has the fw disabled. #if self._nodes.count() == 1: # nd = self._nodes.get_nodes() # if nd[0].get_config().isFirewallRunning: if self._is_local_request(proto, addr) == False: self._show_message_trigger.emit("node exited", "({0})".format(context.peer()), QtWidgets.QSystemTrayIcon.Information, DesktopNotifications.URGENCY_LOW) context.add_callback(_on_client_closed) # TODO: move to notifications.py def new_node_message(): print("new node connected, listening for client responses...", addr) while self._exit == False: try: if stop_event.is_set(): break in_message = next(node_iter) if in_message == None: continue self._nodes.reply_notification(addr, in_message) except StopIteration: print("[Notifications] Node {0} exited".format(addr)) break except grpc.RpcError as e: print("[Notifications] grpc exception new_node_message(): ", addr, in_message) except Exception as e: print("[Notifications] unexpected exception new_node_message(): ", addr, e, in_message) read_thread = Thread(target=new_node_message) read_thread.daemon = True read_thread.start() while self._exit == False: if stop_event.is_set(): break try: noti = _node['notifications'].get() if noti != None: _node['notifications'].task_done() yield noti except Exception as e: print("[Notifications] exception getting notification from queue:", addr, e) context.cancel() return node_iter �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/utils/���������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017616�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/utils/__init__.py����������������������������������������������������0000664�0000000�0000000�00000041171�15003540030�0021733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5 import QtCore, QtWidgets, QtGui from opensnitch.version import version as gui_version from opensnitch.database import Database from opensnitch.config import Config from threading import Thread, Event import pwd import socket import fcntl import struct import array import os, sys, glob import enum import re class AsnDB(): __instance = None asndb = None @staticmethod def instance(): if AsnDB.__instance == None: AsnDB.__instance = AsnDB() return AsnDB.__instance def __init__(self): self.ASN_AVAILABLE = True self.load() def is_available(self): return self.ASN_AVAILABLE def load(self): """Load the ASN DB from disk. It'll try to load it from user's opensnitch directory if these file exist: - ~/.config/opensnitch/ipasn_db.dat.gz - ~/.config/opensnitch/asnames.json Otherwise it'll try to load it from python3-pyasn package. """ try: if self.asndb != None: return import pyasn IPASN_DB_PATH = os.path.expanduser('~/.config/opensnitch/ipasn_db.dat.gz') # .gz not supported for asnames AS_NAMES_FILE_PATH = os.path.expanduser('~/.config/opensnitch/asnames.json') # if the user hasn't downloaded an updated ipasn db, use the one # shipped with the python3-pyasn package if os.path.isfile(IPASN_DB_PATH) == False: IPASN_DB_PATH = '/usr/lib/python3/dist-packages/data/ipasn_20140513_v12.dat.gz' if os.path.isfile(AS_NAMES_FILE_PATH) == False: AS_NAMES_FILE_PATH = '/usr/lib/python3/dist-packages/data/asnames.json' print("using IPASN DB:", IPASN_DB_PATH) self.asndb = pyasn.pyasn(IPASN_DB_PATH, as_names_file=AS_NAMES_FILE_PATH) except Exception as e: self.ASN_AVAILABLE = False print("exception loading ipasn db:", e) print("Install python3-pyasn to display IP's network name.") def lookup(self, ip): """Lookup the IP in the ASN DB. Return the net range and the prefix if found, otherwise nothing. """ try: return self.asndb.lookup(ip) except Exception: return "", "" def get_as_name(self, asn): """Get the ASN name given a network range. Return the name of the network if found, otherwise nothing. """ try: asname = self.asndb.get_as_name(asn) if asname == None: asname = "" return asname except Exception: return "" def get_asn(self, ip): try: asn, prefix = self.lookup(ip) return self.get_as_name(asn) except Exception: return "" class Themes(): """Change GUI's appearance using qt-material lib. https://github.com/UN-GCPDS/qt-material """ THEMES_PATH = [ os.path.expanduser("~/.config/opensnitch/"), os.path.dirname(sys.modules[__name__].__file__) ] __instance = None AVAILABLE = False try: from qt_material import apply_stylesheet as qtmaterial_apply_stylesheet from qt_material import list_themes as qtmaterial_themes AVAILABLE = True except Exception: print("Themes not available. Install qt-material if you want to change GUI's appearance: pip3 install qt-material.") @staticmethod def instance(): if Themes.__instance == None: Themes.__instance = Themes() return Themes.__instance def __init__(self): self._cfg = Config.get() theme = self._cfg.getInt(self._cfg.DEFAULT_THEME, 0) def available(self): return Themes.AVAILABLE def get_saved_theme(self): theme = self._cfg.getSettings(self._cfg.DEFAULT_THEME) theme_density = self._cfg.getSettings(self._cfg.DEFAULT_THEME_DENSITY_SCALE) if theme_density == "" or theme_density == None: theme_density = '0' if not Themes.AVAILABLE: return 0, "", theme_density if theme != "" and theme != None: # 0 == System return self.list_themes().index(theme)+1, theme, theme_density return 0, "", theme_density def save_theme(self, theme_idx, theme, density_scale): if not Themes.AVAILABLE: return self._cfg.setSettings(self._cfg.DEFAULT_THEME_DENSITY_SCALE, density_scale) if theme_idx == 0: self._cfg.setSettings(self._cfg.DEFAULT_THEME, "") else: self._cfg.setSettings(self._cfg.DEFAULT_THEME, theme) def load_theme(self, app): if not Themes.AVAILABLE: return try: theme_idx, theme_name, theme_density = self.get_saved_theme() if theme_name != "": invert = "light" in theme_name print("Using theme:", theme_idx, theme_name, "inverted:", invert) # TODO: load {theme}.xml.extra and .xml.css for further # customizations. extra_opts = { 'density_scale': theme_density } Themes.qtmaterial_apply_stylesheet(app, theme=theme_name, invert_secondary=invert, extra=extra_opts) except Exception as e: print("Themes.load_theme() exception:", e) def change_theme(self, window, theme_name, extra={}): try: invert = "light" in theme_name Themes.qtmaterial_apply_stylesheet(window, theme=theme_name, invert_secondary=invert, extra=extra) except Exception as e: print("Themes.change_theme() exception:", e, " - ", window, theme_name) def list_local_themes(self): themes = [] if not Themes.AVAILABLE: return themes try: for tdir in self.THEMES_PATH: themes += glob.glob(tdir + "/themes/*.xml") except Exception: pass finally: return themes def list_themes(self): themes = self.list_local_themes() if not Themes.AVAILABLE: return themes themes += Themes.qtmaterial_themes() return themes class GenericTimer(Thread): interval = 1 stop_flag = None callback = None def __init__(self, _interval, _callback, _args=()): Thread.__init__(self, name="generic_timer_thread") self.interval = _interval self.stop_flag = Event() self.callback = _callback self.args = _args def run(self): while self.stop_flag.wait(self.interval): if self.stop_flag.is_set(): self.callback(self.args) break def stop(self): self.stop_flag.set() class OneshotTimer(GenericTimer): def __init__(self, _interval, _callback, _args=()): GenericTimer.__init__(self, _interval, _callback, _args) def run(self): self.stop_flag.wait(self.interval) self.callback(self.args) class CleanerTask(Thread): interval = 1 stop_flag = None callback = None def __init__(self, _interval, _callback): Thread.__init__(self, name="cleaner_db_thread") self.interval = _interval * 60 self.stop_flag = Event() self.callback = _callback self._cfg = Config.init() # We need to instantiate a new QsqlDatabase object with a unique name, # because it's not thread safe: # "A connection can only be used from within the thread that created it." # https://doc.qt.io/qt-5/threads-modules.html#threads-and-the-sql-module # The filename and type is the same, the one chosen by the user. self.db = Database("db-cleaner-connection") self.db_status, db_error = self.db.initialize( dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY), dbfile=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY), dbjrnl_wal=self._cfg.getBool(self._cfg.DEFAULT_DB_JRNL_WAL) ) def run(self): if self.db_status == False: return while not self.stop_flag.is_set(): self.stop_flag.wait(self.interval) self.callback(self.db) def stop(self): self.stop_flag.set() self.db.close() class QuickHelp(): @staticmethod def show(help_str): QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), help_str) class Utils(): @staticmethod def check_versions(daemon_version): lMayor, lMinor, lPatch = gui_version.split(".", 2) rMayor, rMinor, rPatch = daemon_version.split(".", 2) return lMayor != rMayor or (lMayor == rMayor and lMinor != rMinor) @staticmethod def get_user_id(uid): pw_name = uid try: pw_name = pwd.getpwuid(int(uid)).pw_name + " (" + uid + ")" except Exception: #pw_name += " (error)" pass return pw_name @staticmethod def get_interfaces(): max_possible = 128 # arbitrary. raise if needed. bytes = max_possible * 32 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', b'\0' * bytes) outbytes = struct.unpack('iL', fcntl.ioctl( s.fileno(), 0x8912, # SIOCGIFCONF struct.pack('iL', bytes, names.buffer_info()[0]) ))[0] return names.tobytes(), outbytes @staticmethod def create_socket_dirs(): """https://www.linuxbase.org/betaspecs/fhs/fhs.html#runRuntimeVariableData """ run_path = "/run/user/{0}".format(os.getuid()) var_run_path = "/var{0}".format(run_path) try: if os.path.exists(run_path): os.makedirs(run_path + "/opensnitch/", 0o700) if os.path.exists(var_run_path): os.makedirs(var_run_path + "/opensnitch/", 0o700) except: pass class Message(): @staticmethod def ok(title, message, icon): msgBox = QtWidgets.QMessageBox() msgBox.setWindowFlags(msgBox.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) msgBox.setText("<b>{0}</b><br><br>{1}".format(title, message)) msgBox.setIcon(icon) msgBox.setModal(True) msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() @staticmethod def yes_no(title, message, icon): msgBox = QtWidgets.QMessageBox() msgBox.setWindowFlags(msgBox.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) msgBox.setText(title) msgBox.setIcon(icon) msgBox.setModal(True) msgBox.setInformativeText(message) msgBox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Yes) msgBox.setDefaultButton(QtWidgets.QMessageBox.Cancel) return msgBox.exec_() class FileDialog(): @staticmethod def save(parent): options = QtWidgets.QFileDialog.Options() fileName, _ = QtWidgets.QFileDialog.getSaveFileName(parent, "", "","All Files (*)", options=options) return fileName @staticmethod def select(parent): options = QtWidgets.QFileDialog.Options() fileName, _ = QtWidgets.QFileDialog.getOpenFileName(parent, "", "","All Files (*)", options=options) return fileName @staticmethod def select_dir(parent, current_dir): options = QtWidgets.QFileDialog.Options() fileName = QtWidgets.QFileDialog.getExistingDirectory(parent, "", current_dir, options) return fileName # https://stackoverflow.com/questions/29503339/how-to-get-all-values-from-python-enum-class class Enums(enum.Enum): @classmethod def to_dict(cls): return {e.name: e.value for e in cls} @classmethod def keys(cls): return cls._member_names_ @classmethod def values(cls): return [str(v.value) for v in cls] class NetworkInterfaces(): # https://gist.github.com/pklaus/289646 @staticmethod def list(): namestr, outbytes = Utils.get_interfaces() _interfaces = {} for i in range(0, outbytes, 40): try: name = namestr[i:i+16].split(b'\0', 1)[0] addr = namestr[i+20:i+24] _interfaces[name.decode()] = "%d.%d.%d.%d" % (int(addr[0]), int(addr[1]), int(addr[2]), int(addr[3])) except Exception as e: print("utils.NetworkInterfaces() exception:", e) return _interfaces class NetworkServices(): """Get a list of known ports. /etc/services """ __instance = None @staticmethod def instance(): if NetworkServices.__instance == None: NetworkServices.__instance = NetworkServices() return NetworkServices.__instance srv_array = [] ports_list = [] def __init__(self): etcServicesPath = "/etc/services" if not os.path.isfile(etcServicesPath) and os.path.isfile("/usr/etc/services"): etcServicesPath = "/usr/etc/services" try: etcServices = open(etcServicesPath) for line in etcServices: if line[0] == "#": continue g = re.search(r'([a-zA-Z0-9\-]+)( |\t)+([0-9]+)\/([a-zA-Z0-9\-]+)(.*)\n', line) if g: self.srv_array.append("{0}/{1} {2}".format( g.group(1), g.group(3), "" if len(g.groups())>3 and g.group(4) == "" else "({0})".format(g.group(4).replace("\t", "")) ) ) self.ports_list.append(g.group(3)) # extra ports that don't exist in /etc/services self.srv_array.append("wireguard/51820 WireGuard VPN") self.ports_list.append("51820") except Exception as e: print("Error loading {0}: {1}".format(etcServicesPath, e)) def to_array(self): return self.srv_array def service_by_index(self, idx): return self.srv_array[idx] def service_by_name(self, name): return self.srv_array.index(name) def port_by_index(self, idx): return self.ports_list[idx] def index_by_port(self, port): return self.ports_list.index(str(port)) class Icons(): """Util to display Qt's built-in icons when the system is not configured as we expect. More information: https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#no-icons-on-the-gui https://www.pythonguis.com/faq/built-in-qicons-pyqt/icons-builtin.png """ defaults = { 'document-new': "SP_FileIcon", 'document-save': "SP_DialogSaveButton", 'document-open': "SP_DirOpenIcon", 'format-justify-fill': "SP_FileDialogDetailedView", 'preferences-system': "SP_FileDialogListView", 'preferences-desktop': "SP_FileDialogListView", 'security-high': "SP_VistaShield", 'go-previous': "SP_ArrowLeft", 'go-jump': "SP_CommandLink", 'go-down': "SP_TitleBarUnshadeButton", 'go-up': "SP_TitleBarShadeButton", 'help-browser': "SP_DialogHelpButton", 'emblem-important': "SP_DialogCancelButton", 'emblem-default': "SP_DialogApplyButton", 'window-close': "SP_DialogCloseButton", 'system-run': "", 'preferences-system-network': "", 'document-properties': "", 'edit-delete': "SP_DialogCancelButton", 'list-add': "SP_ArrowUp", 'list-remove': "SP_ArrowDown", 'system-search': "SP_FileDialogContentsView", 'application-exit': "SP_TitleBarCloseButton", 'view-sort-ascending': "SP_ToolBarVerticalExtensionButton", 'address-book-new': "", 'media-playback-start': "SP_MediaPlay", 'media-playback-pause': "SP_MediaPause", 'system-search': "SP_FileDialogContentsView", 'accessories-text-editor': "SP_DialogOpenButton", 'edit-clear-all': "SP_DialogResetButton", 'reload': "SP_DialogResetButton", 'dialog-information': "SP_MessageBoxInformation", 'dialog-warning': "SP_MessageBoxWarning" } @staticmethod def new(widget, icon_name): icon = QtGui.QIcon.fromTheme(icon_name, QtGui.QIcon.fromTheme(icon_name + "-symbolic")) if icon.isNull(): try: return widget.style().standardIcon(getattr(QtWidgets.QStyle, Icons.defaults[icon_name])) except Exception as e: print("Qt standardIcon exception:", icon_name, ",", e) return icon class Versions(): @staticmethod def get(): try: from google.protobuf import __version__ as proto_version from grpc import _grpcio_metadata as grpcmeta return gui_version, grpcmeta.__version__, proto_version except: return "none", "none", "none" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/utils/infowindow.py��������������������������������������������������0000664�0000000�0000000�00000003156�15003540030�0022360�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from opensnitch.config import Config from PyQt5 import QtCore, QtWidgets, QtGui class InfoWindow(QtWidgets.QDialog): """Display a text on a small dialog. """ def __init__(self, parent): QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.Tool) self.setContentsMargins(0, 0, 0, 0) self._cfg = Config.get() self.layout = QtWidgets.QVBoxLayout(self) self._textedit = QtWidgets.QTextEdit() # hide cursor self._textedit.setCursorWidth(0) self._textedit.setMinimumSize(300, 325) self._textedit.setReadOnly(True) self._textedit.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.TextSelectableByKeyboard) self.layout.addWidget(self._textedit) self._load_settings() def closeEvent(self, ev): self._save_settings() ev.accept() self.hide() def _load_settings(self): saved_geometry = self._cfg.getSettings(Config.INFOWIN_GEOMETRY) if saved_geometry is not None: self.restoreGeometry(saved_geometry) def _save_settings(self): self._cfg.setSettings(Config.INFOWIN_GEOMETRY, self.saveGeometry()) def showText(self, text): self._load_settings() self._textedit.setText(text) pos = QtGui.QCursor.pos() win_size = self.size() # center dialog on cursor, relative to the parent widget. x_off = (int(win_size.width()/2)) y_off = (int(win_size.height()/2)) point = QtCore.QPoint( pos.x()-x_off, pos.y()-y_off ) self.move(point.x(), point.y()) self.show() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/utils/languages.py���������������������������������������������������0000664�0000000�0000000�00000001760�15003540030�0022142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import QtCore import os from opensnitch.config import Config def __get_i18n_path(): return os.path.dirname(os.path.realpath(__file__)) + "/../i18n" def init(saved_lang): locale = QtCore.QLocale.system() lang = locale.name() if saved_lang: lang = saved_lang i18n_path = __get_i18n_path() print("Loading translations:", i18n_path, "locale:", lang) translator = QtCore.QTranslator() translator.load(i18n_path + "/" + lang + "/opensnitch-" + lang + ".qm") return translator def save(cfg, lang): q = QtCore.QLocale(lang) cfg.setSettings(Config.DEFAULT_LANGUAGE, lang) cfg.setSettings(Config.DEFAULT_LANGNAME, q.nativeLanguageName().capitalize()) def get_all(): langs = [] names = [] i18n_path = __get_i18n_path() lang_dirs = os.listdir(i18n_path) lang_dirs.sort() for lang in lang_dirs: q = QtCore.QLocale(lang) langs.append(lang) names.append(q.nativeLanguageName()) return langs, names ����������������opensnitch-1.6.9/ui/opensnitch/utils/qvalidator.py��������������������������������������������������0000664�0000000�0000000�00000001624�15003540030�0022341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5 import QtCore, QtGui class RestrictChars(QtGui.QValidator): result = QtCore.pyqtSignal(object) def __init__(self, restricted_chars, *args, **kwargs): QtGui.QValidator.__init__(self, *args, **kwargs) self._restricted_chars = restricted_chars def validate(self, value, pos): # allow to delete all characters if len(value) == 0: return QtGui.QValidator.Intermediate, value, pos # user can type characters or paste them. # pos value when pasting can be any number, depending on where did the # user paste the characters. for char in self._restricted_chars: if char in value: self.result.emit(QtGui.QValidator.Invalid) return QtGui.QValidator.Invalid, value, pos self.result.emit(QtGui.QValidator.Acceptable) return QtGui.QValidator.Acceptable, value, pos ������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/utils/xdg.py���������������������������������������������������������0000664�0000000�0000000�00000007637�15003540030�0020767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ import os import re import shutil import stat # https://github.com/takluyver/pyxdg/blob/1d23e483ae869ee9532aca43b133cc43f63626a3/xdg/BaseDirectory.py def get_runtime_dir(strict=True): try: return os.environ['XDG_RUNTIME_DIR'] except KeyError: if strict: raise import getpass fallback = '/tmp/opensnitch-' + getpass.getuser() create = False try: # This must be a real directory, not a symlink, so attackers can't # point it elsewhere. So we use lstat to check it. st = os.lstat(fallback) except OSError as e: import errno if e.errno == errno.ENOENT: create = True else: raise else: # The fallback must be a directory if not stat.S_ISDIR(st.st_mode): os.unlink(fallback) create = True # Must be owned by the user and not accessible by anyone else elif (st.st_uid != os.getuid()) \ or (st.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): os.rmdir(fallback) create = True if create: os.mkdir(fallback, 0o700) return fallback def get_run_opensnitch_dir(): rdir = get_runtime_dir(False) if 'opensnitch' not in rdir: rdir = os.path.join(rdir, 'opensnitch') try: os.makedirs(rdir, 0o700) except: pass return rdir class Autostart(): def __init__(self): desktopFile = 'opensnitch_ui.desktop' self.systemDesktop = os.path.join('/usr/share/applications', desktopFile) self.systemAutostart = os.path.join('/etc/xdg/autostart', desktopFile) if not os.path.isfile(self.systemAutostart) and os.path.isfile('/usr' + self.systemAutostart): self.systemAutostart = '/usr' + self.systemAutostart self.userAutostart = os.path.join(xdg_config_home, 'autostart', desktopFile) def _copyfile(self, src, dst): """copy file (.desktop) to dst. Ignore exception if src and dst are equal""" try: shutil.copyfile(src, dst) except shutil.SameFileError: pass def createUserDir(self): if not os.path.isdir(xdg_config_home): os.makedirs(xdg_config_home, 0o700) if not os.path.isdir(os.path.dirname(self.userAutostart)): os.makedirs(os.path.dirname(self.userAutostart), 0o755) def isEnabled(self): ret = False if os.path.isfile(self.userAutostart): ret = True lines = open(self.userAutostart, 'r').readlines() for line in lines: if re.search("^Hidden=true", line, re.IGNORECASE): ret = False break elif os.path.isfile(self.systemAutostart): ret = True return ret def enable(self, mode=True): self.createUserDir() if mode == True: if os.path.isfile(self.systemAutostart) and os.path.isfile(self.userAutostart): os.remove(self.userAutostart) elif os.path.isfile(self.systemDesktop): self._copyfile(self.systemDesktop, self.userAutostart) else: if os.path.isfile(self.systemAutostart): self._copyfile(self.systemAutostart, self.userAutostart) with open(self.userAutostart, 'a') as f: f.write('Hidden=true\n') elif os.path.isfile(self.userAutostart): os.remove(self.userAutostart) def disable(self): self.enable(False) _home = os.path.expanduser('~') xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or os.path.join(_home, '.config') xdg_runtime_dir = get_runtime_dir(False) xdg_current_desktop = os.environ.get('XDG_CURRENT_DESKTOP') xdg_current_session = os.environ.get('XDG_SESSION_TYPE') xdg_opensnitch_dir = get_run_opensnitch_dir() �������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/opensnitch/version.py�����������������������������������������������������������0000664�0000000�0000000�00000000022�15003540030�0020507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������version = '1.6.9' ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/requirements.txt����������������������������������������������������������������0000664�0000000�0000000�00000000121�15003540030�0017562�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������grpcio-tools>=1.10.1 pyinotify==0.9.6 unicode_slugify==0.1.5 pyqt5>=5.6 protobuf �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016316�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/icons/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017431�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/icons/48x48/����������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020230�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/icons/48x48/opensnitch-ui.png�����������������������������������������0000664�0000000�0000000�00000003452�15003540030�0023527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���0���0���Wù‡��� pHYs��Ã��ÃÇo¨d���tEXtSoftware�www.inkscape.org›î<��·IDAThí™{PT×Ç?w÷²»Æµ™)¶HÉð´°˜Dìt¦]ì°q‚‚‰bmjµ“Œ™PÉô•IóB:Í£* B&ÑÔÆWh” 4&‘Õ$¢VSícF&‚I)q‚\T`ïÝÓ?–»Þö è8ãwæÎÜßïüÎï|{ïÞó=çHBnfn4ÑâV77}òuoÂ�¤ 0xàœ þ D›Pº_¡o¸áa –�?�b†´_NH°ËµDZŒb¼.i@ˆ_¸…¸àB„y}¡1/’qÆë Ltà ~ªwªª*𛛥îîKX,fbccEbb"’$ICú—Ëð4 †h< ¸M:`–æho?ïÞ¶íO1ÎutuwûÇÅÅQ° @-z|¹{’ÕjÑüöÆÀϽ·m’v`ð=àv¼ïn/ðm K üså[”––qõêÕ  ­V+kÖ® èŸÆZ^ÓÈ6�3BÅ–—WðúÖ?F”¿¸øY–=ºL3ݘa‚ÏÅG2È*¼" òuuΈÉ””lâäÉSšc€’`ñá>£*?…�\ºÔÃþýû9þ×´ý· Ù(“’’LŽ#‡G‹ZLgçň �HMMå@Ý{Â`0H€P!Ý _F]€«€—4Ûé<È‹/¼„Ëå1Þ`0àñx¢"¯¡¦¦úü]wß7h>#æÇ •¨¾ ¬Õì7߬äwO><0jò�ï8 O23P\ÈdxŽÁÙóÓ3gúÖ¯Û0jrá ñLÓ·tfJ ¸P˜üD3Ö½¼Þí¯kË´EïêréuÚmâ‚à¾öÇuñbOCCCD$ôXµr;wí mjZXñ“&MÒ›gä À7Ú?þù/K°ØPcd²³í¼ûî;?‹Õj Ÿ‘‘¡7§*ð<0Tr—Ó¾£Ýw¹ºüTä¢E INIÍ| Þe™e.ãþy÷³~ݜ΃#Ƨ¥¥êÍàEfÈðpåÉž[‡[ˆÜB¨šR¬ûàC‘–žé»Ž?®Â zûøobÖì<¿Üié™bjÆ4QôË_‹Æææ¡} !Lס¯¬@)p ˜îËÊšÎpÑ8:¨ªÊZ[éru kBpøp=>°ˆ7ÞØ®oÊU¡B3ô™U÷Ñ©HEQh8ÝÀéÓ ttvP³¯Ö'ÊŽ¦ÄLJM¶hy! wÜá³›ššX»æ÷466†Õ¿°ð1ž~æ)_}@® Gµ¤Aòó´§ó ¥›Ê8wî\Ø$ƒ¡ººŠéYÓéêüªß®FUCÊ}$Ibëë[˜5ëÍuR†IBܰ\Ÿò*)ÙÈŽí;Ç„¸†ªª=œ={–²²ò ³x0L™Gý‘ÃF�<0]BXïÂ:¼Rá/¯‹šè„ ìv;GóóÇÇOáë¯Û£Î«áµ-¯’›{¯f®6¨PÀ ùÿutPº©,êäùùyÔ9ëX²tɰ¶± P__¯7¿/{`¾ömÙW½þþþˆ“¦¤¤°zÍóÌœ™ @kk먉B»ÿ‘ K0M³Nú{DÉÌf3E/§°ð1L&“Ïo³Ù(¯ØvžO>ù”;ÂûÏõööêM‹ ˜5+’Çœ9-“ŠòÍ$&%k‹ý&sçæ‡+’ùeòäÉz³Ë�øä¥ÙlÖ!Z¾há­={¹|ùrØ}Æ©©×$†-2Щ92ÒÓùê˯ÂJ¤ª*»wíæƒC‡X¹j%yys|mŸö97ޏ€‘,=çÌ÷Ý{à” | Ü ?7? ¸ „ööóüö7Oàpä°rÕ ’““鼨©_˜Žl6ŸJí‹¿<PÅàæÑœ9³Éœ–UòãÇOP0ÿÇ”•n¦§§7t‡aµZ)^ñœÞµ pIBxx� ­­Ÿ-^õl `2™ˆzÃy&NœÈk[^Ånÿ¡æêáNà‚@õîCö�$%%QY¹ÛïÏ)Æ’¼-ÓFÕÛ{ôä…EÀЩQ°0ôõõQ[SKuu ---\ÏÃ@£Ñˆ=ÛÎÂ…rß}s1üT±ìÝį��– Øø-¯\¹‚[QÂ&ÐßßÏ@__D¤Mf3f‹wX³É„Å2lÛ#Á¯ŒP©wÛØrC–[숌P;¸ÝÞ6´1ÐΜ¤@®€‡{$Hg’Cá«x`· šŽö|ÀLM‚!èÃ{ä6®ÇÙ¸â¦?f½UÀÆ­n4þ¡x_êãŠ]����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/icons/64x64/����������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020224�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/icons/64x64/opensnitch-ui.png�����������������������������������������0000664�0000000�0000000�00000004514�15003540030�0023523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���@���@���ªiqÞ��� pHYs��Ã��ÃÇo¨d���tEXtSoftware�www.inkscape.org›î<��ÙIDATxœí›{PÔ×Ç?¿ýýveµF£v">°%¦2ŠÖ(™øªŠFÒ°hj5ŠQ“Ñ´“Ä*ëL5FƒFÑØ™b qÒ8>P@±N4 ñ>ÚQ›h°u|@5" ¬°~ýcEûÛ]ö‰›4|ÿ»çÜsî¹g÷Þsν+ȲÌš`l´: Ø­¶ÁF«‚m@°!Û�7A„�á@£ õ:ø7` ô@Â÷$’¬ß¿F ÐÃE¿o8,C®Å8ØКa®�©ÀS^ÊþC€¥"äûc@Ð`†AlžñSU‘æµ+¾Åf˜)À Ä¿º¦†×®SU]MXh(:u䉎Ðëv¸$KpÔ[[»,0X§¦×ÕÕÉ……ž=yœ9sÆANEbbú“œlhL|1QEQTu1 ðv{cOK8@h€Þ"ôl"ÈP-C­Æ造ÈÏ/`õ»«¹s§Ò£"ºG°hQ*cÆŒV³Œ2ŒÔB‰ÇÆÊf¢WdHºx"c2™X¸ •¢¢C>9í•©¼óÎbTÃM ~T{¢Ão˜ ŸÞÃöëzŒ††~÷ÆïùòKÿN³ Ƴn}:‚`÷QeHð¦'òþD‚‚ÞÔØ>7¯&–¶ÒïÉ`Ë–?«ÉóL¶¯À-|ý+dÊ0˳²²’‹/R~³Q”èÞ£;QQQ´k×€'N23åUµü$Ibßþ½DFF>2¶Š0ÇíD|1 ›exCM?òÙ²²¶SRRBcc£OE†}ž$C7dpåŠOǶKÄÅaÓæ %É(Ag ®99¯`…)2ä(iUUU,J]ÌçŸá•®@B’$N–œ¸ªo¢ à.RôvxR¶0Q^^Á09¨“°X,|qô¨Ý9*Ãpwr^9àAó“¦v}}½<{ön\¿îšéÒÓá*R”;o æ) 7nʾ-óBEË¢ìrY{é§îd<v€FíšÚµµµ 9Ù9ÍHxú%o4:” ´îd¼ùÆ*y{󤆆/ÄÝã“ìí¬M_KçÎ}’o:fp»Ã{ì�YµžŽ;®NFü‚(Šh4â9p°€Y³_E’¼+Xuéb ÐÛQèÞ|½”ÿ\½ê…¨{('ÆÂ… Ø·/Ï=뱎ÁCÛµeèmóár\/l|BÙ¸ï¸ÞXºôètn—SˆN~íÈÈH>ÊÚF~~kß[Ë­Û·›ÕÝ×ùIàæja›šé.jk±íü3§•ŒdÃdΟ?o×ùì¹ÓèõzZµµµdlÜDvvV«ÕiŸÐÐPH™9ƒž={:ðex[ ”4—KÀ Ó-ð-¶Lïi5?2²—£P "<<œ?,˜Ïرq.ûFrsÿFüÄDÖ¥¯Çb±ØñXg…ÉJš3èÍ+ÃÇ4³ŒérYµ¾*þŠ„„9pà Û¾‹…Ì̭̘žÂ½{÷”, ™(ªÎê%f"àyµR“ÉDiéiN—žæ¿·nQQ^Aq±}:7F]œð:޵ï¯q ßº}›5«×PPPè“Þ~ýû±}{!!võă҃^é�ÉûPåöF£‘¬¬ùh[–Ú›…^ÂÙsjV«•œìddlò{\ƒ!‰Uï®T“‡IPüpëµÀT“¿Rv…×^ŸËõk×ü2À[”œ*aÅŠU\ºt) úvíÚM|B¼úH] ²,S½$øЦ‰{ñÂERRfRSÓr¿ºz}E‡òþÚtòó V,i€Ø‘›­$™$è*ȲŒþ"+ª'UUU’&qãÆ€¡Óé˜4yÙŸd;ðDQ$$$„ººfë~aßþ½DE= hxY´•aš²czúú€O~øðaìÏÏcþü·œò­Vk‹NàÈgGíÚ2üR²B<¶T°8òöælÐn,Y²˜Q£~@}}}Àt{‹Ë—/«IQªªÉ¡¢"Ìf³ßƒét:æÌ™Ík¯Ï¡M›‡[ Z­o¡r PYépñÒA’¡·’rê”Ç—*.;„eËÿä4E‘]»wú¬»ên³f¹-ö:…ÅâBk%;?@yE…o–:u$uQ*‰‰ ÍöëÛ×iÒân÷ϲmÛª+fÜÕ  ‡ýY£}ú<àAƒ|–oitïaÿîB€›À¤$vñ±P\\ÌÄ Ù¼ùÀì#ưaCíÚpF’¡By«Íñã'|äþýz6oúÃ?ÌòåËèÓߎoµZ7n‚Ïúe©°;tíÚ…ØØ!öºà˜œ~ÛD=z™™[}6° _ý S¦LÅl`þü·èС`sÀã­f̘ŽFc·Ú/èàŸ-ìÞcÅ ˆáÙÁÏdÐÆÆFv~º“ÆŽ'çA!C£?t‹ˆ`ê´©v´¦  p8¬d¦¦.ôº ÙªkjHK[‰!)™“'NL¯'Ðëõ|ðÁ:t:ÝCš WµðWx›!V€ãJÁ=»÷°dÉÒ€'%z}6n`Äû2 MOi4�Z8!ƒ]†òRÒK¤¥-³óÜ OuëÆŽ9“—!GùŽHYio…³²ªü}áÂV¬XʳçZÜè@ ,,ŒÙ³g‘23ÅÙ«²3’-ô˜uÙ•ÄLÐGc{jæðƧ´¤”Â)-)Åxÿ¾×†Ý«öèÉŽO´111ŒÿqcF»ªLŸ“`P®$:”ÅM-Bž ‘üŸ@†\­­ÞQ«æ9T…up^„A2|úX¬kY\À …)8™<¸¾¸«…—±])F'ü @™ oKðsw'=z"S=EHÀv\þ ð¤öÝF€PÏLö24ðèé¼ TÊP)Àyàˆd;Ò=:¿ƒýZ<èøÑÿc¤ÕÁ6 Øhu@° 6Zl‚ÿÇ1ñM€z¬­����IEND®B`‚������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/icons/opensnitch-ui.svg�����������������������������������������������0000664�0000000�0000000�00000013700�15003540030�0022740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="64" height="64" viewBox="0 0 12.698413 12.698413" version="1.1" id="svg8" inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" sodipodi:docname="opensnitch-ui.svg" inkscape:export-filename="64x64/opensnitch-ui.png" inkscape:export-xdpi="96" inkscape:export-ydpi="96" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs2" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="9.75" inkscape:cx="21.74359" inkscape:cy="31.794872" inkscape:document-units="px" inkscape:current-layer="g4133" showgrid="true" inkscape:window-width="1600" inkscape:window-height="839" inkscape:window-x="0" inkscape:window-y="24" inkscape:window-maximized="1" units="px" inkscape:pagecheckerboard="0" inkscape:showpageshadow="2" inkscape:deskcolor="#d1d1d1" showguides="true"> <sodipodi:guide position="13.436869,10.958536" orientation="0,-1" id="guide291" inkscape:locked="false" /> <sodipodi:guide position="13.233163,1.6160318" orientation="0,-1" id="guide293" inkscape:locked="false" /> <inkscape:grid type="xygrid" id="grid295" /> </sodipodi:namedview> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-284.30001)"> <g id="g4133" transform="matrix(0.9182611,0,0,0.9182611,-19.582232,24.4642)"> <path style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.287462px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 23.93778,289.55608 0.922529,-2.47895 2.563056,-0.57549 2.395943,-1.27803 2.491924,1.17578 0.401143,2.50266 1.620734,1.26491 0.381256,1.78538 -0.666013,1.62872 -1.326357,0.85434 -9.105041,0.0948 -1.530218,-1.13976 -0.31061,-1.71767 0.780558,-1.56852 z" id="path1481" sodipodi:nodetypes="ccccccccccccccc" inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" inkscape:export-xdpi="96" inkscape:export-ydpi="96" /> <path style="fill:#fbffff;fill-opacity:1;stroke:none;stroke-width:0.0267051;stroke-opacity:1" d="m 23.924347,295.01517 c -1.190403,-0.17151 -2.160364,-1.04049 -2.48066,-2.22233 -0.06228,-0.22986 -0.06956,-0.30524 -0.06956,-0.7212 0,-0.41596 0.0072,-0.49134 0.06956,-0.72117 0.147237,-0.54329 0.408851,-0.99654 0.795944,-1.37896 0.324557,-0.32066 0.785409,-0.60618 1.154881,-0.71557 l 0.103224,-0.0303 0.01629,-0.29557 c 0.07143,-1.29674 0.972433,-2.37494 2.27078,-2.71736 0.229851,-0.0607 0.312438,-0.0688 0.716539,-0.0696 0.257818,-6.1e-4 0.509198,0.0123 0.577176,0.0292 0.120416,0.0303 0.120441,0.0303 0.198793,-0.0679 0.155946,-0.19524 0.509186,-0.51209 0.74373,-0.66706 0.551463,-0.36443 1.11147,-0.54697 1.774575,-0.57842 0.589067,-0.0277 1.121425,0.0806 1.650413,0.33595 0.72776,0.35149 1.243577,0.8611 1.599369,1.58011 0.255831,0.51699 0.367014,1.04428 0.341349,1.61884 l -0.01332,0.2989 0.217937,0.14177 c 0.119863,0.078 0.346565,0.26788 0.503782,0.42196 0.88975,0.87202 1.218873,2.08704 0.893766,3.29955 -0.132616,0.49457 -0.500547,1.11835 -0.881945,1.49513 -0.364404,0.36002 -1.001057,0.73363 -1.4664,0.86052 -0.492709,0.13439 -0.431399,0.13271 -4.634625,0.12938 -2.15745,-0.002 -3.994154,-0.0135 -4.081564,-0.0261 z m 8.365743,-0.89453 c 0.59725,-0.15778 1.128615,-0.51689 1.486943,-1.00491 0.147801,-0.20133 0.33126,-0.60206 0.39973,-0.87313 0.08448,-0.33442 0.08466,-0.85621 4.07e-4,-1.18961 -0.203279,-0.80452 -0.831632,-1.50173 -1.597272,-1.77233 -0.153083,-0.0541 -0.232971,-0.0955 -0.224805,-0.11652 0.252703,-0.65059 0.224901,-1.36673 -0.07754,-1.99749 -0.264858,-0.55243 -0.669292,-0.9522 -1.225766,-1.21167 -0.399047,-0.18607 -0.636305,-0.23736 -1.097982,-0.23736 -0.319298,0 -0.430775,0.0109 -0.61795,0.0601 -0.739241,0.19424 -1.358854,0.68643 -1.680928,1.33522 l -0.07478,0.15063 -0.122961,-0.0614 c -0.228775,-0.11421 -0.565847,-0.2025 -0.834911,-0.21871 -0.932118,-0.0562 -1.838882,0.55372 -2.13689,1.4371 -0.168757,0.50024 -0.151725,0.9779 0.05358,1.50243 0.01377,0.035 -0.0089,0.0394 -0.156728,0.03 -0.208357,-0.0132 -0.547143,0.0503 -0.81323,0.15241 -0.54704,0.20974 -1.043227,0.72489 -1.228933,1.27596 -0.172171,0.51087 -0.161028,0.97577 0.03539,1.47692 0.212384,0.54181 0.73321,1.03044 1.29576,1.21563 0.113249,0.0371 0.270424,0.0783 0.349276,0.0916 0.08114,0.0136 1.857609,0.0221 4.092874,0.0195 l 3.949506,-0.004 z m -5.761885,-1.40118 c -0.576304,-0.34169 -1.047827,-0.63319 -1.047827,-0.64782 0,-0.0148 0.471523,-0.30613 1.047827,-0.64783 l 1.047829,-0.62116 0.0074,0.42212 0.0074,0.42218 h 1.718841 1.718847 v 0.42469 0.42469 h -1.718876 -1.718849 l -0.0074,0.42218 -0.0074,0.42217 z m 2.350898,-2.34355 v -0.42777 h -1.719512 -1.719514 v -0.42468 -0.42471 h 1.718847 1.718849 l 0.0074,-0.42217 0.0074,-0.42216 1.047812,0.6212 c 0.5763,0.34168 1.051205,0.63124 1.055344,0.64353 0.0041,0.0124 -0.443196,0.28902 -0.99408,0.61507 -0.550883,0.32599 -1.028812,0.61 -1.062059,0.63111 l -0.06046,0.0382 z" id="path826-6" inkscape:connector-curvature="0" /> </g> </g> </svg> ����������������������������������������������������������������opensnitch-1.6.9/ui/resources/io.github.evilsocket.opensnitch.appdata.xml���������������������������0000664�0000000�0000000�00000004224�15003540030�0026663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <component type="desktop-application"> <id>io.github.evilsocket.opensnitch</id> <name>OpenSnitch</name> <summary>GNU/Linux interactive application firewall</summary> <metadata_license>FTL</metadata_license> <project_license>GPL-3.0-or-later</project_license> <supports> <control>pointing</control> <control>keyboard</control> <control>touch</control> </supports> <description> <p> Whenever a program tries to establish a new connection, it'll prompt the user to allow or deny it. </p> <p> The user can decide if block the outgoing connection based on properties of the connection: by port, by uid, by dst ip, by program or a combination of them. These rules can last forever, until the app restart or just one time. </p> <p> The GUI allows the user to view live outgoing connections, as well as search by process, user, host or port. </p> <p> OpenSnitch can also work as a system-wide domains blocker, by using lists of domains, list of IPs or list of regular expressions. </p> </description> <categories> <category>System</category> <category>Security</category> <category>Monitor</category> <category>Network</category> </categories> <icon type="stock">opensnitch-ui</icon> <url type="homepage">https://github.com/evilsocket/opensnitch</url> <url type="bugtracker">https://github.com/evilsocket/opensnitch/issues</url> <url type="help">https://github.com/evilsocket/opensnitch/wiki</url> <launchable type="desktop-id">opensnitch_ui.desktop</launchable> <screenshots> <screenshot type="default"> <image>https://user-images.githubusercontent.com/2742953/85205382-6ba9cb00-b31b-11ea-8e9a-bd4b8b05a236.png</image> </screenshot> <screenshot> <image>https://user-images.githubusercontent.com/2742953/217039798-3477c6c2-d64f-4eea-89af-cd94ee77cff4.png</image> </screenshot> <screenshot> <image>https://user-images.githubusercontent.com/2742953/99863173-3987e800-2b9d-11eb-93f2-fe3121b18c51.png</image> </screenshot> </screenshots> <content_rating type="oars-1.0" /> </component> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/kcm_opensnitch.desktop������������������������������������������������0000664�0000000�0000000�00000000436�15003540030�0022720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Desktop Entry] Exec=opensnitch-ui Icon=opensnitch-ui Type=Service X-KDE-ServiceTypes=SystemSettingsExternalApp Name=OpenSnitch Firewall Comment=OpenSnitch Firewall Graphical Interface X-KDE-Keywords=system,firewall,policies,security,polkit,policykit,douane X-KDE-Autostart-after=panel ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/resources/opensnitch_ui.desktop�������������������������������������������������0000664�0000000�0000000�00000001025�15003540030�0022556�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Desktop Entry] Type=Application Name=OpenSnitch Exec=opensnitch-ui Icon=opensnitch-ui GenericName=OpenSnitch Firewall GenericName[hu]=OpenSnitch-tűzfal GenericName[nb]=OpenSnitch brannmur Comment=Interactive application firewall Comment[es]=Firewall de aplicaciones Comment[hu]=Alkalmazási tűzfal Comment[nb]=Interaktiv programbrannmur Terminal=false NoDisplay=false Categories=System;Security;Monitor;Network; Keywords=system;firewall;policies;security;polkit;policykit; X-GNOME-Autostart-Delay=3 X-GNOME-Autostart-enabled=true �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/setup.py������������������������������������������������������������������������0000664�0000000�0000000�00000003271�15003540030�0016021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from setuptools import setup, find_packages import os import sys path = os.path.abspath(os.path.dirname(__file__)) sys.path.append(path) from opensnitch.version import version setup(name='opensnitch-ui', version=version, description='Prompt service and UI for the opensnitch interactive firewall application.', long_description='GUI for the opensnitch interactive firewall application\n\ opensnitch-ui is a GUI for opensnitch written in Python.\n\ It allows the user to view live outgoing connections, as well as search\n\ to make connections.\n\ .\n\ The user can decide if block the outgoing connection based on properties of\n\ the connection: by port, by uid, by dst ip, by program or a combination\n\ of them.\n\ .\n\ These rules can last forever, until the app restart or just one time.', url='https://github.com/evilsocket/opensnitch', author='Simone "evilsocket" Margaritelli', author_email='evilsocket@protonmail.com', license='GPL-3.0', packages=find_packages(), include_package_data = True, package_data={'': ['*.*']}, data_files=[('/usr/share/applications', ['resources/opensnitch_ui.desktop']), ('/usr/share/kservices5', ['resources/kcm_opensnitch.desktop']), ('/usr/share/icons/hicolor/scalable/apps', ['resources/icons/opensnitch-ui.svg']), ('/usr/share/icons/hicolor/48x48/apps', ['resources/icons/48x48/opensnitch-ui.png']), ('/usr/share/icons/hicolor/64x64/apps', ['resources/icons/64x64/opensnitch-ui.png']), ('/usr/share/metainfo', ['resources/io.github.evilsocket.opensnitch.appdata.xml'])], scripts = [ 'bin/opensnitch-ui' ], zip_safe=False) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/tests/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015446�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/tests/README.md�����������������������������������������������������������������0000664�0000000�0000000�00000001674�15003540030�0016735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������GUI unit tests. We use pytest [0] to pytest-qt [1] to test GUI code. To run the tests: `cd tests; pytest -v` TODO: - test service class (Service.py) - test events window (stats.py): - The size of the window must be saved on close, and restored when opening it again. - Columns width of every view must be saved and restored properly. - On the Events tab, clicking on the Node, Process or Rule column must jump to the detailed view of the selected item. - When entering into a detail view: - the results limit configured must be respected (that little button on the bottom right of every tab). - must apply the proper SQL query for every detailed view. - When going back from a detail view: - The SQL query must be restored. - Test rules context menu actions. - Test select rows and copy them to the clipboard (ctrl+c). 0. https://docs.pytest.org/en/6.2.x/ 1. https://pytest-qt.readthedocs.io/en/latest/intro.html ��������������������������������������������������������������������opensnitch-1.6.9/ui/tests/__init__.py���������������������������������������������������������������0000664�0000000�0000000�00000000000�15003540030�0017545�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/tests/dialogs/������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017070�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/tests/dialogs/__init__.py�������������������������������������������������������0000664�0000000�0000000�00000002176�15003540030�0021207�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from opensnitch.database import Database from opensnitch.config import Config from opensnitch.nodes import Nodes # grpc object class ClientConfig: version = "1.2.3" name = "bla" logLevel = 0 isFirewallRunning = False rules = [] config = '''{ "Server":{ "Address": "unix:///tmp/osui.sock", "LogFile": "/var/log/opensnitchd.log" }, "DefaultAction": "deny", "DefaultDuration": "once", "InterceptUnknown": false, "ProcMonitorMethod": "ebpf", "LogLevel": 0, "LogUTC": true, "LogMicro": false, "Firewall": "iptables", "Stats": { "MaxEvents": 150, "MaxStats": 50 } } ''' class Connection: protocol = "tcp" src_ip = "127.0.0.1" src_port = "12345" dst_ip = "127.0.0.1" dst_host = "localhost" dst_port = "54321" user_id = 1000 process_id = 9876 process_path = "/bin/cmd" process_cwd = "/tmp" process_args = "/bin/cmd --parm1 test" process_env = [] db = Database.instance() db.initialize() Config.init() nodes = Nodes.instance() nodes._nodes["unix:/tmp/osui.sock"] = { 'data': ClientConfig } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/tests/dialogs/test_preferences.py�����������������������������������������������0000664�0000000�0000000�00000014217�15003540030�0023007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # pytest -v tests/dialogs/test_ruleseditor.py # import os import time import json from PyQt5 import QtCore, QtWidgets, QtGui from opensnitch.config import Config from opensnitch.dialogs.preferences import PreferencesDialog class TestPreferences(): @classmethod def reset_settings(self): try: os.remove(os.environ['HOME'] + "/.config/opensnitch/settings.conf") except Exception: pass @classmethod def setup_method(self): white_icon = QtGui.QIcon("../res/icon-white.svg") self.reset_settings() self.prefs = PreferencesDialog(appicon=white_icon) self.prefs.show() def run(self, qtbot): def handle_dialog(): qtbot.mouseClick(self.prefs.applyButton, QtCore.Qt.LeftButton) qtbot.mouseClick(self.prefs.acceptButton, QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(500, handle_dialog) self.prefs.exec_() def test_save_popups_settings(self, qtbot): """ Test saving UI related settings. """ qtbot.addWidget(self.prefs) self.prefs.comboUIAction.setCurrentIndex(Config.ACTION_ALLOW_IDX) self.prefs.comboUITarget.setCurrentIndex(2) self.prefs.comboUIDuration.setCurrentIndex(4) self.prefs.comboUIDialogPos.setCurrentIndex(2) self.prefs.spinUITimeout.setValue(30) self.prefs.showAdvancedCheck.setChecked(True) self.prefs.uidCheck.setChecked(True) self.run(qtbot) assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_ACTION_KEY) == Config.ACTION_ALLOW_IDX and self.prefs.comboUIAction.currentText() == Config.ACTION_ALLOW assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_TARGET_KEY) == 2 assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_DURATION_KEY) == 4 assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_TIMEOUT_KEY) == 30 assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_POPUP_POSITION) == 2 assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_POPUP_ADVANCED) == True assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_POPUP_ADVANCED_UID) == True def test_save_ui_settings(self, qtbot): self.prefs.checkUIRules.setChecked(True) self.prefs.comboUIRules.setCurrentIndex(1) self.prefs.checkHideNode.setChecked(False) self.prefs.checkHideProto.setChecked(False) self.run(qtbot) assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_IGNORE_RULES) == True and self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) == 1 cols = self.prefs._cfg.getSettings(Config.STATS_SHOW_COLUMNS) assert cols == ['0','2','3','5','6'] def test_save_node_settings(self, qtbot, capsys): self.prefs.comboNodeAction.setCurrentIndex(Config.ACTION_ALLOW_IDX) self.prefs.comboNodeMonitorMethod.setCurrentIndex(2) self.prefs.comboNodeLogLevel.setCurrentIndex(5) self.prefs.checkNodeLogUTC.setChecked(False) self.prefs.checkNodeLogMicro.setChecked(True) self.prefs.checkInterceptUnknown.setChecked(True) self.prefs.tabWidget.setCurrentIndex(self.prefs.TAB_NODES) self.prefs._node_needs_update = True self.run(qtbot) assert len(self.prefs._notifications_sent) == 1 for n in self.prefs._notifications_sent: conf = json.loads(self.prefs._notifications_sent[n].data) assert conf['InterceptUnknown'] == True assert conf['ProcMonitorMethod'] == "audit" assert conf['LogLevel'] == 5 assert conf['LogUTC'] == False assert conf['LogMicro'] == True assert conf['DefaultAction'] == "allow" # TODO: click on the QMessageDialog # # def test_save_db_settings(self, qtbot, monkeypatch, capsys): # self.prefs.comboDBType.setCurrentIndex(1) # self.prefs.dbLabel.setText('/tmp/test.db') # # def handle_dialog(): # qtbot.mouseClick(self.prefs.applyButton, QtCore.Qt.LeftButton) # # after saving the settings, a warning dialog must appear, informing # # the user to restart the GUI # time.sleep(.5) # msgbox = QtWidgets.QApplication.activeModalWidget() # try: # assert msgbox != None # okBtn = msgbox.button(QtWidgets.QMessageBox.Ok) # qtbot.mouseClick(okBtn, QtCore.Qt.LeftButton) # except Exception as e: # print("test_save_db_Settings() exception:", e) # qtbot.mouseClick(self.prefs.acceptButton, QtCore.Qt.LeftButton) # # QtCore.QTimer.singleShot(500, handle_dialog) # self.prefs.exec_() # assert self.prefs._cfg.getInt(Config.DEFAULT_DB_TYPE_KEY) == 1 # assert self.prefs._cfg.getSettings(Config.DEFAULT_DB_FILE_KEY) == '/tmp/test.db' def test_load_ui_settings(self, qtbot, capsys): """ reTest saved settings (load_settings()). On dialog show up the widgets must be configured properly, with the settings configured in previous tests. """ self.prefs.checkUIRules.setChecked(False) self.prefs.comboUIRules.setCurrentIndex(0) self.prefs.comboUITarget.setCurrentIndex(0) self.prefs.comboUIDuration.setCurrentIndex(0) self.prefs.checkHideNode.setChecked(True) self.prefs.checkHideProto.setChecked(True) def handle_dialog(): qtbot.mouseClick(self.prefs.cancelButton, QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(500, handle_dialog) self.prefs.exec_() self.prefs.show() print(self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_IGNORE_RULES)) assert self.prefs.comboUIAction.currentIndex() == Config.ACTION_ALLOW_IDX and self.prefs.comboUIAction.currentText() == Config.ACTION_ALLOW assert self.prefs.checkUIRules.isChecked() == True assert self.prefs.comboUIRules.currentIndex() == 1 assert self.prefs.comboUITarget.currentIndex() == 2 assert self.prefs.comboUIDuration.currentIndex() == 4 and self.prefs.comboUIDuration.currentText() == Config.DURATION_30m assert self.prefs.comboUIDialogPos.currentIndex() == 2 assert self.prefs.spinUITimeout.value() == 30 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/tests/dialogs/test_ruleseditor.py�����������������������������������������������0000664�0000000�0000000�00000041546�15003540030�0023054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # pytest -v tests/dialogs/test_ruleseditor.py # import json from PyQt5 import QtCore, QtWidgets, QtGui from opensnitch.config import Config from opensnitch.dialogs.ruleseditor import RulesEditorDialog class TestRulesEditor(): @classmethod def setup_method(self): white_icon = QtGui.QIcon("../res/icon-white.svg") self.rd = RulesEditorDialog(appicon=white_icon) self.rd.show() self.rd.ruleNameEdit.setText("xxx") self.rd.nodesCombo.addItem("unix:/tmp/osui.sock") self.rd.nodesCombo.setCurrentText("unix:/tmp/osui.sock") self.rd._nodes._nodes["unix:/tmp/osui.sock"] = {} def test_rule_no_fields(self, qtbot): """ Test that rules without fields selected cannot be created. """ qtbot.addWidget(self.rd) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() != "" def test_fields_empty(self, qtbot): """ Test that fields cannot be empty. """ self.rd.pidCheck.setChecked(True) self.rd.pidLine.setText("") result, error = self.rd._save_rule() assert error != None self.rd.pidCheck.setChecked(False) self.rd.uidCheck.setChecked(True) self.rd.uidLine.setText("") result, error = self.rd._save_rule() assert error != None self.rd.uidCheck.setChecked(False) self.rd.procCheck.setChecked(True) self.rd.procLine.setText("") result, error = self.rd._save_rule() assert error != None self.rd.procCheck.setChecked(False) self.rd.cmdlineCheck.setChecked(True) self.rd.cmdlineLine.setText("") result, error = self.rd._save_rule() assert error != None self.rd.cmdlineCheck.setChecked(False) self.rd.dstPortCheck.setChecked(True) self.rd.dstPortLine.setText("") result, error = self.rd._save_rule() assert error != None self.rd.dstPortCheck.setChecked(False) self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("") result, error = self.rd._save_rule() assert error != None self.rd.dstHostCheck.setChecked(False) self.rd.dstListsCheck.setChecked(True) self.rd.dstListsLine.setText("") result, error = self.rd._save_rule() assert error != None def test_add_basic_rule(self, qtbot): """ Test adding a basic rule. """ self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test.com") self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("www.test.com") self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_UNTIL_RESTART)) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE assert self.rd.rule.operator.operand == "dest.host" assert self.rd.rule.operator.data == "www.test.com" assert self.rd.rule.duration == Config.DURATION_UNTIL_RESTART def test_add_complex_rule(self, qtbot): """ Test add complex rule. """ self.rd.WORK_MODE = self.rd.ADD_RULE self.rd._reset_state() self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test-complex.com") self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("www.test-complex.com") self.rd.dstPortCheck.setChecked(True) self.rd.dstPortLine.setText("443") def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test-complex.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test-complex.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_LIST assert self.rd.rule.operator.operand == Config.RULE_TYPE_LIST json_rule = json.loads(self.rd.rule.operator.data) assert json_rule[0]['type'] == "simple" assert json_rule[0]['operand'] == "dest.port" assert json_rule[0]['data'] == "443" assert json_rule[1]['type'] == "simple" assert json_rule[1]['operand'] == "dest.host" assert json_rule[1]['data'] == "www.test-complex.com" def test_add_reject_rule(self, qtbot): """ Test adding new rule with action "reject". """ self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test-reject.com") self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("www.test-reject.com") self.rd.actionRejectRadio.setChecked(True) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test-reject.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test-reject.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE assert self.rd.rule.operator.operand == "dest.host" assert self.rd.rule.operator.data == "www.test-reject.com" assert self.rd.rule.action == Config.ACTION_REJECT def test_add_deny_rule(self, qtbot): """ Test adding new rule with action "deny". """ self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test-deny.com") self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("www.test-deny.com") self.rd.actionDenyRadio.setChecked(True) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test-deny.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test-deny.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE assert self.rd.rule.operator.operand == "dest.host" assert self.rd.rule.operator.data == "www.test-deny.com" assert self.rd.rule.action == Config.ACTION_DENY def test_add_allow_rule(self, qtbot): """ Test adding new rule with action "allow". """ self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test-allow.com") self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("www.test-allow.com") self.rd.actionAllowRadio.setChecked(True) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test-allow.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test-allow.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE assert self.rd.rule.operator.operand == "dest.host" assert self.rd.rule.operator.data == "www.test-allow.com" assert self.rd.rule.action == Config.ACTION_ALLOW def test_add_rule_name_conflict(self, qtbot): """ Test that rules with the same name cannot be added. """ assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test.com") self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("www.test.com") def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() != "" def test_load_rule(self, qtbot): """ Test loading a rule. """ self.rd.WORK_MODE = self.rd.ADD_RULE self.rd._reset_state() records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()) assert records.next() == True self.rd.edit_rule(records, self.rd.nodesCombo.currentText()) assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.ruleNameEdit.text() == "www.test.com" assert self.rd.dstHostCheck.isChecked() == True assert self.rd.dstHostLine.text() == "www.test.com" assert self.rd.durationCombo.currentIndex() == self.rd._load_duration(Config.DURATION_UNTIL_RESTART) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() def test_edit_and_rename_rule(self, qtbot): """ Test loading, editing and renaming a rule. """ self.rd.WORK_MODE = self.rd.ADD_RULE self.rd._reset_state() records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()) assert records.next() == True self.rd.edit_rule(records, self.rd.nodesCombo.currentText()) assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.ruleNameEdit.text() == "www.test.com" assert self.rd.dstHostCheck.isChecked() == True assert self.rd.dstHostLine.text() == "www.test.com" self.rd.ruleNameEdit.setText("www.test-renamed.com") self.rd.dstHostLine.setText("www.test-renamed.com") def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()) assert records.next() == False records = self.rd._db.get_rule("www.test-renamed.com", self.rd.nodesCombo.currentText()) assert records.next() == True def test_durations(self, qtbot): """ Test adding new rule with action "deny". """ self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test-duration.com") self.rd.dstHostCheck.setChecked(True) self.rd.dstHostLine.setText("www.test-duration.com") self.rd.actionDenyRadio.setChecked(True) self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS)) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test-duration.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test-duration.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE assert self.rd.rule.operator.operand == "dest.host" assert self.rd.rule.operator.data == "www.test-duration.com" assert self.rd.rule.action == Config.ACTION_DENY assert self.rd.rule.duration == Config.DURATION_ALWAYS def test_rule_LANs(self, qtbot): """ Test rule with regexp and LAN keyword in particular. """ self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test-rule-LAN.com") self.rd.dstIPCheck.setChecked(True) self.rd.dstIPCombo.setCurrentText(self.rd.LAN_LABEL) self.rd.actionDenyRadio.setChecked(True) self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS)) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test-rule-LAN.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test-rule-LAN.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_REGEXP assert self.rd.rule.operator.operand == "dest.ip" assert self.rd.rule.operator.data == self.rd.LAN_RANGES assert self.rd.rule.action == Config.ACTION_DENY assert self.rd.rule.duration == Config.DURATION_ALWAYS def test_rule_networks(self, qtbot): """ Test rule with networks. """ self.rd.statusLabel.setText("") self.rd.ruleNameEdit.setText("www.test-rule-networks.com") self.rd.dstIPCheck.setChecked(True) self.rd.dstIPCombo.setCurrentText("192.168.111.0/24") self.rd.actionDenyRadio.setChecked(True) self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS)) def handle_dialog(): qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) QtCore.QTimer.singleShot(100, handle_dialog) self.rd.exec_() assert self.rd.statusLabel.text() == "" assert self.rd._db.get_rule("www.test-rule-networks.com", self.rd.nodesCombo.currentText()).next() == True assert self.rd._old_rule_name == "www.test-rule-networks.com" # after adding a rule, we enter into editing mode, to allow editing it # without closing the dialog. assert self.rd.WORK_MODE == self.rd.EDIT_RULE assert self.rd.rule.operator.type == Config.RULE_TYPE_NETWORK assert self.rd.rule.operator.operand == "dest.network" assert self.rd.rule.operator.data == "192.168.111.0/24" assert self.rd.rule.action == Config.ACTION_DENY assert self.rd.rule.duration == Config.DURATION_ALWAYS ����������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/ui/tests/test_nodes.py�������������������������������������������������������������0000664�0000000�0000000�00000012555�15003540030�0020177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # pytest -v tests/nodes.py # import json from PyQt5 import QtCore from opensnitch import ui_pb2 from opensnitch.config import Config from opensnitch.nodes import Nodes from tests.dialogs import ClientConfig class NotifTest(QtCore.QObject): """We need to subclass from QObject in order to be able to user signals and slots. """ signal = QtCore.pyqtSignal(ui_pb2.NotificationReply) @QtCore.pyqtSlot(ui_pb2.NotificationReply) def callback(self, reply): assert reply != None assert reply.code == ui_pb2.OK and reply.type == ui_pb2.LOAD_FIREWALL and reply.data == "test" class TestNodes(): @classmethod def setup_method(self): self.nid = None self.daemon_config = ClientConfig self.nodes = Nodes.instance() self.nodes._db.insert("nodes", "(addr, status, hostname, daemon_version, daemon_uptime, " \ "daemon_rules, cons, cons_dropped, version, last_connection)", ( "1.2.3.4", Nodes.ONLINE, "xxx", "v1.2.3", str(0), "", "0", "0", "", "2022-01-03 11:22:48.101624" ) ) def test_add(self, qtbot): node = self.nodes.add("peer:1.2.3.4", self.daemon_config) assert node != None def test_get_node(self, qtbot): node = self.nodes.get_node("peer:1.2.3.4") assert node != None def test_get_addr(self, qtbot): proto, addr = self.nodes.get_addr("peer:1.2.3.4") assert proto == "peer" and addr == "1.2.3.4" def test_get_nodes(self, qtbot): nodes = self.nodes.get_nodes() print(nodes) assert nodes.get("peer:1.2.3.4") != None def test_add_rule(self, qtbot): self.nodes.add_rule( "2022-01-03 11:22:48.101624", "peer:1.2.3.4", "test", True, False, Config.ACTION_ALLOW, Config.DURATION_30s, Config.RULE_TYPE_SIMPLE, False, "dest.host", "" ) query = self.nodes._db.get_rule("test", "peer:1.2.3.4") assert query.first() == True assert query.record().value(0) == "2022-01-03 11:22:48.101624" assert query.record().value(1) == "peer:1.2.3.4" assert query.record().value(2) == "test" assert query.record().value(3) == "1" assert query.record().value(4) == "0" assert query.record().value(5) == Config.ACTION_ALLOW assert query.record().value(6) == Config.DURATION_30s assert query.record().value(7) == Config.RULE_TYPE_SIMPLE assert query.record().value(8) == "0" assert query.record().value(9) == "dest.host" def test_update_rule_time(self, qtbot): query = self.nodes._db.get_rule("test", "peer:1.2.3.4") assert query.first() == True assert query.record().value(0) == "2022-01-03 11:22:48.101624" self.nodes.update_rule_time("2022-01-03 21:22:48.101624", "test", "peer:1.2.3.4") query = self.nodes._db.get_rule("test", "peer:1.2.3.4") assert query.first() == True assert query.record().value(0) == "2022-01-03 21:22:48.101624" def test_delete_rule(self, qtbot): query = self.nodes._db.get_rule("test", "peer:1.2.3.4") assert query.first() == True self.nodes.delete_rule("test", "peer:1.2.3.4", None) query = self.nodes._db.get_rule("test", "peer:1.2.3.4") assert query.first() == False def test_update_node_status(self, qtbot): query = self.nodes._db.select("SELECT status FROM nodes WHERE addr = '{0}'".format("1.2.3.4")) assert query != None and query.exec_() == True and query.first() == True assert query.record().value(0) == Nodes.ONLINE self.nodes.update("peer", "1.2.3.4", Nodes.OFFLINE) query = self.nodes._db.select("SELECT status FROM nodes WHERE addr = '{0}'".format("1.2.3.4")) assert query != None and query.exec_() == True and query.first() == True assert query.record().value(0) == Nodes.OFFLINE def test_send_notification(self, qtbot): notifs = NotifTest() notifs.signal.connect(notifs.callback) test_notif = ui_pb2.Notification( clientName="", serverName="", type=ui_pb2.LOAD_FIREWALL, data="test", rules=[]) self.nid = self.nodes.send_notification("peer:1.2.3.4", test_notif, notifs.signal) assert self.nodes._notifications_sent[self.nid] != None assert self.nodes._notifications_sent[self.nid]['type'] == ui_pb2.LOAD_FIREWALL def test_reply_notification(self, qtbot): reply_notif = ui_pb2.Notification( id = self.nid, clientName="", serverName="", type=ui_pb2.LOAD_FIREWALL, data="test", rules=[]) # just after process the reply, the notification is deleted (except if # is of type MONITOR_PROCESS self.nodes.reply_notification("peer:1.2.3.4", reply_notif) assert self.nid not in self.nodes._notifications_sent def test_delete(self, qtbot): self.nodes.delete("peer:1.2.3.4") node = self.nodes.get_node("peer:1.2.3.4") nodes = self.nodes.get_nodes() assert node == None assert nodes.get("peer:1.2.3.4") == None ���������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0015027�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/legacy/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016273�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/legacy/make_ads_rules.py�����������������������������������������������������0000664�0000000�0000000�00000003370�15003540030�0021626�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import requests import re import ipaddress import datetime import os lists = ( \ "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", "https://mirror1.malwaredomains.com/files/justdomains", "https://sysctl.org/cameleon/hosts", "https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist", "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", "https://hosts-file.net/ad_servers.txt" ) domains = {} for url in lists: print "Downloading %s ..." % url r = requests.get(url) if r.status_code != 200: print "Error, status code %d" % r.status_code continue for line in r.text.split("\n"): line = line.strip() if line == "": continue elif line[0] == "#": continue for part in re.split(r'\s+', line): part = part.strip() if part == "": continue try: duh = ipaddress.ip_address(part) except ValueError: if part != "localhost": domains[part] = 1 print "Got %d unique domains, saving as rules to ./rules/ ..." % len(domains) os.system("mkdir -p rules") idx = 0 for domain, _ in domains.iteritems(): with open("rules/adv-%d.json" % idx, "wt") as fp: tpl = """ { "created": "%s", "updated": "%s", "name": "deny-adv-%d", "enabled": true, "action": "deny", "duration": "always", "operator": { "type": "simple", "operand": "dest.host", "data": "%s" } }""" now = datetime.datetime.utcnow().isoformat("T") + "Z" data = tpl % ( now, now, idx, domain ) fp.write(data) idx = idx + 1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016753�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/build_modules.sh���������������������������������������������������0000664�0000000�0000000�00000004457�15003540030�0022150�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # opensnitch - 2022-2023 # echo """ Dependencies needed to compile the eBPF modules: sudo apt install -y wget flex bison ca-certificates wget python3 rsync bc libssl-dev clang llvm libelf-dev libzip-dev git libpcap-dev --- """ kernel_version=$(uname -r | cut -d. -f1,2) if [ ! -z $1 ]; then kernel_version=$1 fi kernel_sources="v${kernel_version}.tar.gz" if [ -f "${kernel_sources}" ]; then echo -n "[i] Deleting previous kernel sources ${kernel_sources}: " rm -f ${kernel_sources} && echo "OK" || echo "ERROR" fi echo "[+] Downloading kernel sources:" wget -nv --show-progress https://github.com/torvalds/linux/archive/${kernel_sources} 1>/dev/null echo if [ -d "linux-${kernel_version}/" ]; then echo -n "[i] Deleting previous kernel sources dir linux-${kernel_version}/: " rm -rf linux-${kernel_version}/ && echo "OK" || echo "ERROR" fi echo -n "[+] Uncompressing kernel sources: " tar -xf v${kernel_version}.tar.gz && echo "OK" || echo "ERROR" if [ "${ARCH}" == "arm" -o "${ARCH}" == "arm64" ]; then echo "[+] Patching kernel sources" patch linux-${kernel_version}/arch/arm/include/asm/unified.h < ebpf_prog/arm-clang-asm-fix.patch fi echo -n "[+] Preparing kernel sources... (1-2 minutes): " echo -n "." cd linux-${kernel_version} && yes "" | make oldconfig 1>/dev/null echo -n "." make prepare 1>/dev/null echo -n "." make headers_install 1>/dev/null echo " DONE" cd ../ if [ -z $ARCH ]; then ARCH=x86 fi echo "[+] Compiling eBPF modules..." cd ebpf_prog && make KERNEL_DIR=../linux-${kernel_version} KERNEL_HEADERS=../linux-${kernel_version} ARCH=${ARCH} >/dev/null # objdump -h opensnitch.o #you should see many section, number 1 should be called kprobe/tcp_v4_connect if [ ! -d modules/ ]; then mkdir modules/ fi mv opensnitch*o modules/ cd ../ llvm-strip -g ebpf_prog/modules/opensnitch*.o #remove debug info if [ -f ebpf_prog/modules/opensnitch.o ]; then echo if objdump -h ebpf_prog/modules/opensnitch.o | grep "kprobe/tcp_v4_connect"; then ls ebpf_prog/modules/*.o echo -e "\n * eBPF modules compiled. Now you can copy the *.o files to /etc/opensnitchd/ and restart the daemon\n" else echo -e "\n [WARN] opensnitch.o module not valid\n" exit 1 fi else echo -e "\n [WARN] opensnitch.o module not compiled\n" exit 1 fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020216�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/��������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020750�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/�������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022172�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/NEWS���������������������������������������������0000664�0000000�0000000�00000001452�15003540030�0022673�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch (1.6.0-rc.3-1) unstable; urgency=medium From now on the eBPF modules will be installed under /usr/lib/opensnitchd/ebpf/. The daemon will look for the eBPF modules in these directories and order: - /usr/local/lib/opensnitchd/ebpf/ - /usr/lib/opensnitchd/ebpf/ Modules under /etc/opensnitchd/ will still be loaded if found, but it's deprecated and will be removed in the future. There's a new module to intercept processes execution. It may cause some rules not to match: for example if you allowed /bin/telnet, now it may be reported as /usr/bin/inteutils-telnet These cases are mostly expected. We'll keep improving it, sorry for the inconveniences. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Wed, 19 Oct 2022 00:15:19 +0200 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/changelog����������������������������������������0000664�0000000�0000000�00000016050�15003540030�0024046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch (1.6.9-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Mon, 28 Apr 2025 01:31:50 +0200 opensnitch (1.6.6-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 20 Jun 2024 00:02:56 +0200 opensnitch (1.6.5-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Tue, 23 Jan 2024 21:25:33 +0100 opensnitch (1.6.2-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Mon, 31 Jul 2023 00:32:11 +0200 opensnitch (1.6.1-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Sun, 23 Jul 2023 22:13:33 +0200 opensnitch (1.6.0.1-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 15 Jun 2023 23:44:15 +0200 opensnitch (1.6.0-rc.5-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Sat, 18 Feb 2023 20:07:14 +0100 opensnitch (1.6.0-rc.4-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 22 Dec 2022 10:07:14 +0100 opensnitch (1.6.0-rc.3-1) unstable; urgency=medium * eBPF modules are now installed under /usr/lib/opensnitchd/. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 08 Dec 2022 11:13:09 +0100 opensnitch (1.6.0-rc.2-1) unstable; urgency=medium * New eBPF module to receive events (new execs, etc). * Allow to exclude connections from the events. * more on github -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 14 Jul 2022 00:15:19 +0200 opensnitch (1.6.0-rc.1-1) unstable; urgency=medium * Allow to configure firewall from the GUI. * New eBPF module to intercept outbound DNS requests. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Wed, 04 May 2022 00:55:54 +0200 opensnitch (1.5.0-1) unstable; urgency=medium * New release. * Added Reject option. * New lists types to block ads/malware/... * Better connections interception. * Better VPNs handling. * Bug fixes. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 28 Jan 2022 23:20:38 +0100 opensnitch (1.5.0~rc2-1) unstable; urgency=medium * Better connections interception. * Improvements. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Sun, 16 Jan 2022 23:15:12 +0100 opensnitch (1.5.0~rc1-1) unstable; urgency=medium * New features. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 07 Oct 2021 14:57:35 +0200 opensnitch (1.4.0-1) unstable; urgency=medium * final release. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 27 Aug 2021 13:33:07 +0200 opensnitch (1.4.0~rc4-1) unstable; urgency=medium * Bug fix release. -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 11 Aug 2021 15:17:49 +0200 opensnitch (1.4.0~rc3-1) unstable; urgency=medium * Bug fix release. -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 16 Jul 2021 23:28:52 +0200 opensnitch (1.4.0~rc2-1) unstable; urgency=medium * Added eBPF support. * Fixes and improvements. -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 07 May 2021 01:08:02 +0200 opensnitch (1.4.0~rc-1) unstable; urgency=medium * Bug fix and improvements release. -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 25 Mar 2021 01:02:31 +0100 opensnitch (1.3.6-1) unstable; urgency=medium * Bug fix and improvements release. -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 10 Feb 2021 10:17:43 +0100 opensnitch (1.3.5-1) unstable; urgency=medium * Bug fix and improvements release. -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 11 Jan 2021 18:01:53 +0100 opensnitch (1.3.0-1) unstable; urgency=medium * Fixed how we check rules * Fixed cpu spike after disable interception. * Fixed cleaning up fw rules on exit. * make regexp rules case-insensitive by default * allow to filter by dst network. -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 16 Dec 2020 01:15:03 +0100 opensnitch (1.3.0~rc-1) unstable; urgency=medium * Non-maintainer upload. -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 13 Nov 2020 00:51:34 +0100 opensnitch (1.2.0-1) unstable; urgency=medium * Fixed memleaks. * Sort rules by name * Added priority field to rules. * Other fixes -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 09 Nov 2020 22:55:13 +0100 opensnitch (1.0.1-1) unstable; urgency=medium * Fixed app exit when IPv6 is not supported. * Other fixes. -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 30 Jul 2020 21:56:20 +0200 opensnitch (1.0.0-1) unstable; urgency=medium * v1.0.0 released. -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 16 Jul 2020 00:19:26 +0200 opensnitch (1.0.0rc11-1) unstable; urgency=medium * Fixed multiple race conditions. * Fixed CWD parsing when using audit proc monitor method. -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 24 Jun 2020 00:10:38 +0200 opensnitch (1.0.0rc10-1) unstable; urgency=medium * Fixed checking UID functions availability. * Improved process path parsing. * Fixed applying config from the UI. * Fixed default log level. * Gather CWD and process environment vars. * Increase default timeout when asking for a rule. -- gustavo-iniguez-goya <gooffy1@gmail.com> Sat, 13 Jun 2020 18:45:02 +0200 opensnitch (1.0.0rc9-1) unstable; urgency=medium * Ignore malformed rules from loading. * Allow to modify and add rules from the UI. -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 17 May 2020 18:18:24 +0200 opensnitch (1.0.0rc8) unstable; urgency=medium * Allow to change settings from the UI. * Improved connection handling with the UI. -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 29 Apr 2020 21:52:27 +0200 opensnitch (1.0.0rc7-1) unstable; urgency=medium * Stability, performance and realiability improvements. -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 12 Apr 2020 23:25:41 +0200 opensnitch (1.0.0rc6-1) unstable; urgency=medium * Fixed iptables rules deletion. * Improved PIDs cache. * Added audit process monitoring method. * Added logrotate file. * Added default configuration file. -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 08 Mar 2020 20:47:58 +0100 opensnitch (1.0.0rc-5) unstable; urgency=medium * Fixed netlink socket querying. * Added check to reload firewall rules if missing. -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 24 Feb 2020 19:55:06 +0100 opensnitch (1.0.0rc-3) unstable; urgency=medium * @see: https://github.com/gustavo-iniguez-goya/opensnitch/releases -- gustavo-iniguez-goya <gooffy1@gmail.com> Tue, 18 Feb 2020 10:09:45 +0100 opensnitch (1.0.0rc-2) unstable; urgency=medium * UI minor changes * Expand deb package compatibility. -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 05 Feb 2020 21:50:20 +0100 opensnitch (1.0.0rc-1) unstable; urgency=medium * Initial release -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 22 Nov 2019 01:14:08 +0100 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/control������������������������������������������0000664�0000000�0000000�00000003130�15003540030�0023572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Source: opensnitch Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org> Uploaders: Gustavo Iniguez Goya <gooffy@gmail.com> Section: devel Testsuite: autopkgtest-pkg-go Priority: optional Build-Depends: debhelper-compat (= 11), debhelper (>= 9), dh-golang, golang-any, golang-github-vishvananda-netlink-dev, golang-github-google-gopacket-dev, golang-github-fsnotify-fsnotify-dev, golang-golang-x-net-dev, golang-google-grpc-dev, golang-goprotobuf-dev, pkg-config, libnetfilter-queue-dev, libmnl-dev Standards-Version: 4.4.0 Vcs-Browser: https://salsa.debian.org/go-team/packages/opensnitch Vcs-Git: https://salsa.debian.org/go-team/packages/opensnitch.git Homepage: https://github.com/evilsocket/opensnitch Rules-Requires-Root: no XS-Go-Import-Path: github.com/evilsocket/opensnitch Package: opensnitch Section: net Architecture: any Depends: libnetfilter-queue1, libc6, libnfnetlink0 Built-Using: ${misc:Built-Using} Description: GNU/Linux interactive application firewall OpenSnitch is a GNU/Linux firewall application. Whenever a program makes a connection, it'll prompt the user to allow or deny it. . The user can decide if block the outgoing connection based on properties of the connection: by port, by uid, by dst ip, by program or a combination of them. . These rules can last forever, until the app restart or just one time. . The GUI allows the user to view live outgoing connections, as well as search by process, user, host or port. . OpenSnitch can also work as a system-wide domains blocker, by using lists of domains, list of IPs or list of regular expressions. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/copyright����������������������������������������0000664�0000000�0000000�00000002151�15003540030�0024124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Source: https://github.com/evilsocket/opensnitch Upstream-Name: opensnitch Files-Excluded: Godeps/_workspace Files: * Copyright: 2017-2018 evilsocket 2019-2020 Gustavo Iñiguez Goia Comment: Debian packaging is licensed under the same terms as upstream License: GPL-3.0 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. . 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 for more details. . You should have received a copy of the GNU General Public License along with this program. If not, If not, see http://www.gnu.org/licenses/. . On Debian systems, the full text of the GNU General Public License version 3 can be found in the file '/usr/share/common-licenses/GPL-3'. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/gbp.conf�����������������������������������������0000664�0000000�0000000�00000000036�15003540030�0023610�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[DEFAULT] pristine-tar = True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/gitlab-ci.yml������������������������������������0000664�0000000�0000000�00000002525�15003540030�0024554�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# auto-generated, DO NOT MODIFY. # The authoritative copy of this file lives at: # https://salsa.debian.org/go-team/ci/blob/master/config/gitlabciyml.go # TODO: publish under debian-go-team/ci image: stapelberg/ci2 test_the_archive: artifacts: paths: - before-applying-commit.json - after-applying-commit.json script: # Create an overlay to discard writes to /srv/gopath/src after the build: - "rm -rf /cache/overlay/{upper,work}" - "mkdir -p /cache/overlay/{upper,work}" - "mount -t overlay overlay -o lowerdir=/srv/gopath/src,upperdir=/cache/overlay/upper,workdir=/cache/overlay/work /srv/gopath/src" - "export GOPATH=/srv/gopath" - "export GOCACHE=/cache/go" # Build the world as-is: - "ci-build -exemptions=/var/lib/ci-build/exemptions.json > before-applying-commit.json" # Copy this package into the overlay: - "GBP_CONF_FILES=:debian/gbp.conf gbp buildpackage --git-no-pristine-tar --git-ignore-branch --git-ignore-new --git-export-dir=/tmp/export --git-no-overlay --git-tarball-dir=/nonexistant --git-cleaner=/bin/true --git-builder='dpkg-buildpackage -S -d --no-sign'" - "pgt-gopath -dsc /tmp/export/*.dsc" # Rebuild the world: - "ci-build -exemptions=/var/lib/ci-build/exemptions.json > after-applying-commit.json" - "ci-diff before-applying-commit.json after-applying-commit.json" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/opensnitch.init����������������������������������0000664�0000000�0000000�00000003562�15003540030�0025237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh ### BEGIN INIT INFO # Provides: opensnitchd # Required-Start: $network $local_fs # Required-Stop: $network $local_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: opensnitchd daemon # Description: opensnitch application firewall ### END INIT INFO NAME=opensnitchd PIDDIR=/var/run/$NAME OPENSNITCHDPID=$PIDDIR/$NAME.pid # clear conflicting settings from the environment unset TMPDIR test -x /usr/bin/$NAME || exit 0 . /lib/lsb/init-functions case $1 in start) log_daemon_msg "Starting opensnitch daemon" $NAME if [ ! -d /etc/$NAME/rules ]; then mkdir -p /etc/$NAME/rules &>/dev/null fi # Make sure we have our PIDDIR, even if it's on a tmpfs install -o root -g root -m 755 -d $PIDDIR if ! start-stop-daemon --start --quiet --oknodo --pidfile $OPENSNITCHDPID --background --exec /usr/bin/$NAME -- -rules-path /etc/$NAME/rules; then log_end_msg 1 exit 1 fi log_end_msg 0 ;; stop) log_daemon_msg "Stopping $NAME daemon" $NAME start-stop-daemon --stop --quiet --signal QUIT --name $NAME # Wait a little and remove stale PID file sleep 1 if [ -f $OPENSNITCHDPID ] && ! ps h `cat $OPENSNITCHDPID` > /dev/null then rm -f $OPENSNITCHDPID fi log_end_msg 0 ;; reload) log_daemon_msg "Reloading $NAME" $NAME start-stop-daemon --stop --quiet --signal HUP --pidfile $OPENSNITCHDPID log_end_msg 0 ;; restart|force-reload) $0 stop sleep 1 $0 start ;; status) status_of_proc /usr/bin/$NAME $NAME exit $? ;; *) echo "Usage: /etc/init.d/opensnitchd {start|stop|reload|restart|force-reload|status}" exit 1 ;; esac exit 0 ����������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/opensnitch.install�������������������������������0000664�0000000�0000000�00000000360�15003540030�0025733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������daemon/default-config.json etc/opensnitchd/ daemon/system-fw.json etc/opensnitchd/ ebpf_prog/opensnitch.o usr/lib/opensnitchd/ebpf/ ebpf_prog/opensnitch-dns.o usr/lib/opensnitchd/ebpf/ ebpf_prog/opensnitch-procs.o usr/lib/opensnitchd/ebpf/ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/opensnitch.logrotate�����������������������������0000664�0000000�0000000�00000000353�15003540030�0026267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/var/log/opensnitchd.log { rotate 7 # order of the fields is important maxsize 50M # we need this option in order to keep logging copytruncate missingok notifempty delaycompress compress create 640 root root weekly } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/opensnitch.service�������������������������������0000664�0000000�0000000�00000000635�15003540030�0025732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Unit] Description=Application firewall OpenSnitch Documentation=https://github.com/gustavo-iniguez-goya/opensnitch/wiki Wants=network.target After=network.target [Service] Type=simple PermissionsStartOnly=true ExecStartPre=/bin/mkdir -p /etc/opensnitchd/rules ExecStart=/usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules Restart=always RestartSec=30 TimeoutStopSec=10 [Install] WantedBy=multi-user.target ���������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/rules��������������������������������������������0000775�0000000�0000000�00000000726�15003540030�0023257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/make -f export DH_VERBOSE = 1 export DESTDIR = debian/opensnitch override_dh_installsystemd: dh_installsystemd --restart-after-upgrade execute_before_dh_auto_build: cd proto; make ../daemon/ui/protocol/ui.pb.go execute_before_dh_auto_install: mkdir -p $(DESTDIR)/usr/bin mv _build/bin/daemon $(DESTDIR)/usr/bin/opensnitchd override_dh_auto_install: dh_auto_install -- --no-source %: dh $@ --builddirectory=_build --buildsystem=golang --with=golang ������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/source/������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0023472�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/source/format������������������������������������0000664�0000000�0000000�00000000014�15003540030�0024700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������3.0 (quilt) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/deb/debian/watch��������������������������������������������0000664�0000000�0000000�00000000352�15003540030�0023223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������version=4 opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/opensnitch-\$1\.tar\.gz/,\ uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \ https://github.com/evilsocket/opensnitch/tags .*/v?(\d\S*)\.tar\.gz ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/rpm/��������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0021014�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/daemon/rpm/opensnitch.spec�����������������������������������������0000664�0000000�0000000�00000006047�15003540030�0024051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Name: opensnitch Version: 1.6.9 Release: 1%{?dist} Summary: OpenSnitch is a GNU/Linux interactive application firewall License: GPLv3+ URL: https://github.com/evilsocket/%{name} Source0: https://github.com/evilsocket/%{name}/releases/download/v%{version}/%{name}_%{version}.orig.tar.gz #BuildArch: x86_64 #BuildRequires: godep Requires(post): info Requires(preun): info %description Whenever a program makes a connection, it'll prompt the user to allow or deny it. The user can decide if block the outgoing connection based on properties of the connection: by port, by uid, by dst ip, by program or a combination of them. These rules can last forever, until the app restart or just one time. The GUI allows the user to view live outgoing connections, as well as search by process, user, host or port. %prep rm -rf %{buildroot} %setup %build mkdir -p go/src/github.com/evilsocket ln -s $(pwd) go/src/github.com/evilsocket/opensnitch export GOPATH=$(pwd)/go cd go/src/github.com/evilsocket/opensnitch/ make protocol cd go/src/github.com/evilsocket/opensnitch/daemon/ go mod vendor go build -o opensnitchd . %install mkdir -p %{buildroot}/usr/bin/ %{buildroot}/usr/lib/opensnitchd/ebpf/ %{buildroot}/usr/lib/systemd/system/ %{buildroot}/etc/opensnitchd/rules %{buildroot}/etc/logrotate.d sed -i 's/\/usr\/local/\/usr/' daemon/opensnitchd.service install -m 755 daemon/opensnitchd %{buildroot}/usr/bin/opensnitchd install -m 644 daemon/opensnitchd.service %{buildroot}/usr/lib/systemd/system/opensnitch.service install -m 644 utils/packaging/daemon/deb/debian/opensnitch.logrotate %{buildroot}/etc/logrotate.d/opensnitch B="" if [ -f /etc/opensnitchd/default-config.json ]; then B="-b" fi install -m 644 $B daemon/default-config.json %{buildroot}/etc/opensnitchd/default-config.json B="" if [ -f /etc/opensnitchd/system-fw.json ]; then B="-b" fi install -m 644 $B daemon/system-fw.json %{buildroot}/etc/opensnitchd/system-fw.json install -m 644 ebpf_prog/opensnitch.o %{buildroot}/usr/lib/opensnitchd/ebpf/opensnitch.o install -m 644 ebpf_prog/opensnitch-dns.o %{buildroot}/usr/lib/opensnitchd/ebpf/opensnitch-dns.o install -m 644 ebpf_prog/opensnitch-procs.o %{buildroot}/usr/lib/opensnitchd/ebpf/opensnitch-procs.o # upgrade, uninstall %preun systemctl stop opensnitch.service || true %post if [ $1 -eq 1 ]; then systemctl enable opensnitch.service fi systemctl start opensnitch.service # uninstall,upgrade %postun if [ $1 -eq 0 ]; then systemctl disable opensnitch.service fi if [ $1 -eq 0 -a -f /etc/logrotate.d/opensnitch ]; then rm /etc/logrotate.d/opensnitch fi # postun is the last step after reinstalling if [ $1 -eq 1 ]; then systemctl start opensnitch.service fi %clean rm -rf %{buildroot} %files %config(noreplace) %{_sysconfdir}/opensnitchd/* %{_bindir}/opensnitchd %{_prefix}/lib/systemd/system/opensnitch.service %{_prefix}/lib/opensnitchd/ebpf/opensnitch.o %{_prefix}/lib/opensnitchd/ebpf/opensnitch-dns.o %{_prefix}/lib/opensnitchd/ebpf/opensnitch-procs.o %{_sysconfdir}/logrotate.d/opensnitch �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017370�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020122�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/�����������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0021344�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/changelog��������������������������������������������0000664�0000000�0000000�00000017316�15003540030�0023226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-ui (1.6.9-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 28 Apr 2025 01:32:23 +0200 opensnitch-ui (1.6.8-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sat, 22 Feb 2025 11:24:32 +0100 opensnitch-ui (1.6.7-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 25 Dec 2024 21:34:39 +0100 opensnitch-ui (1.6.6-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 20 Jun 2024 00:03:33 +0200 opensnitch-ui (1.6.5.1-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 09 Feb 2024 13:56:13 +0100 opensnitch-ui (1.6.5-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 23 Jan 2024 21:26:13 +0100 opensnitch-ui (1.6.4-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 06 Nov 2023 00:29:15 +0100 opensnitch-ui (1.6.3-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 16 Aug 2023 22:19:51 +0200 opensnitch-ui (1.6.2-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 31 Jul 2023 00:33:48 +0200 opensnitch-ui (1.6.1-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 23 Jul 2023 22:14:13 +0200 opensnitch-ui (1.6.0.1-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 15 Jun 2023 23:44:50 +0200 opensnitch-ui (1.6.0-rc.5-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sat, 18 Feb 2023 20:06:42 +0100 opensnitch-ui (1.6.0-rc.4-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 22 Dec 2022 10:06:42 +0100 opensnitch-ui (1.6.0-rc.3-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 15 Nov 2022 00:14:48 +0100 opensnitch-ui (1.6.0-rc.2-1) unstable; urgency=medium * Bugfixes. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 14 Jul 2022 00:20:00 +0200 opensnitch-ui (1.6.0-rc.1-1) unstable; urgency=medium * Allow to configure firewall from the GUI. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 04 May 2022 00:57:31 +0200 opensnitch-ui (1.5.0-1) unstable; urgency=medium * New release. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 28 Jan 2022 23:24:49 +0100 opensnitch-ui (1.5.0~rc2-1) unstable; urgency=medium * Performance improvements. * Better sqlite3 support. * Better system notifications. * Better user experience. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 16 Jan 2022 23:17:02 +0100 opensnitch-ui (1.5.0~rc1-1) unstable; urgency=medium * New features. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 07 Oct 2021 14:58:39 +0200 opensnitch-ui (1.4.0-1) unstable; urgency=medium * Final release. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 27 Aug 2021 13:35:06 +0200 opensnitch-ui (1.4.0~rc4-1) unstable; urgency=medium * UI improvements. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 11 Aug 2021 15:18:34 +0200 opensnitch-ui (1.4.0~rc3-1) unstable; urgency=medium * UI improvements. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 16 Jul 2021 23:30:47 +0200 opensnitch-ui (1.4.0~rc2-1) unstable; urgency=medium * Fixes and improvements. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 07 May 2021 01:08:51 +0200 opensnitch-ui (1.4.0~rc-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 25 Mar 2021 01:03:57 +0100 opensnitch-ui (1.3.6-1) unstable; urgency=medium * Bug fix and improvements release. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 10 Feb 2021 10:17:43 +0100 opensnitch-ui (1.3.5-1) unstable; urgency=medium * Bug fix and improvements release. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 11 Jan 2021 18:02:35 +0100 opensnitch-ui (1.3.0-1) unstable; urgency=medium * Allow to filter by dst networks. * Added check for configure showing pop-ups. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 16 Dec 2020 01:18:31 +0100 opensnitch-ui (1.3.0~rc-1) unstable; urgency=medium * Non-maintainer upload. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 20 Nov 2020 13:32:07 +0100 opensnitch-ui (1.2.0-1) unstable; urgency=medium * Sort rules by name. * Allow to set priority on rules. * Rules are case-insensitive by default. * Other fixes. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 09 Nov 2020 23:00:38 +0100 opensnitch-ui (1.0.1-1) unstable; urgency=medium * Fixed crash when clicking on General tab columns. * Added literal DstHost to the pop-up combo box. * Shorten autogenerated rules names. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 28 Jul 2020 23:43:15 +0200 opensnitch-ui (1.0.0-1) unstable; urgency=medium * v1.0.0 released. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 16 Jul 2020 00:20:19 +0200 opensnitch-ui (1.0.0rc11-1) unstable; urgency=medium * Added CWD field. * Fixed columns resizing/restoring. * Fixed General tab fields filtering. * Pop-up window: display process path if it's hidden. * Display better regexp errors on the rules editor. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 24 Jun 2020 00:20:57 +0200 opensnitch-ui (1.0.0rc10-2) unstable; urgency=medium * Fixed crash when selecting a user (closes #38). -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 17 Jun 2020 20:50:54 +0200 opensnitch-ui (1.0.0rc10-1) unstable; urgency=medium * Allow to filter data in all tabs. * Refresh rules list after deleting a rule. * Fixed high CPU usage while showing a notification. * Fixed columns sort order. * Allow to delete rules in batch. * Remember the columns size. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sat, 13 Jun 2020 18:49:11 +0200 opensnitch-ui (1.0.0rc9-1) unstable; urgency=medium * Added rules editor dialog. * Restart UI upon starting a new X session. * Allow to configure max clients from the cli. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 17 May 2020 18:19:38 +0200 opensnitch-ui (1.0.0rc8) unstable; urgency=medium * Allow to change settings (daemon && UI) from the UI. * Added Nodes view. * Improved UI performance, specially when remote nodes connected. * Fixed race condition when adding stats of remote nodes. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 29 Apr 2020 21:56:54 +0200 opensnitch-ui (1.0.0rc7-1) unstable; urgency=medium * Added help menu. * Added option to filter by command line. * Fixed UI icons. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 12 Apr 2020 23:49:13 +0200 opensnitch-ui (1.0.0rc6-1) unstable; urgency=medium * Fixed showing systray icon in Cinnamon. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 08 Mar 2020 20:50:52 +0100 opensnitch-ui (1.0.0rc5-1) unstable; urgency=medium * Workaround for crash parsing non-utf8 desktop files. * Fixed crash loading sqlite driver. * Fixed HighDpi scaling. * Fixed prompt layout. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 24 Feb 2020 19:56:01 +0100 opensnitch-ui (1.0.0rc3-1) unstable; urgency=medium * Fixed regex patterns. * Display alerts for not answered questions. * Added option to allow/deny second level domains. -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 18 Feb 2020 10:14:59 +0100 opensnitch-ui (1.0.0rc2-1) unstable; urgency=low * initial release -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 06 Feb 2020 00:20:02 +0100 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/compat�����������������������������������������������0000664�0000000�0000000�00000000002�15003540030�0022542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������9 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/control����������������������������������������������0000664�0000000�0000000�00000002445�15003540030�0022754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Source: opensnitch-ui Maintainer: Gustavo Iñiguez Goia <gooffy1@gmail.com> Uploaders: Gustavo Iniguez Goya <gooffy@gmail.com>, Priority: optional Homepage: https://github.com/evilsocket/opensnitch Build-Depends: qttools5-dev-tools, python3-setuptools, pyqt5-dev-tools, python3-grpc-tools, python3-all, debhelper (>= 7.4.3), dh-python Standards-Version: 3.9.1 Package: python3-opensnitch-ui Architecture: all Section: net Depends: netbase, libqt5sql5-sqlite, python3:any, python3-six, python3-pyqt5, python3-pyqt5.qtsql, python3-pyinotify, python3-grpcio, python3-protobuf, python3-packaging, python3-slugify, python3-notify2, xdg-user-dirs, gtk-update-icon-cache Recommends: python3-pyasn Description: GNU/Linux interactive application firewall opensnitch-ui is a GUI for opensnitch written in Python. It allows the user to view live outgoing connections, as well as search for details of the intercepted connections. . The user can decide if block outgoing connections based on properties of the connection: by port, by uid, by dst ip, by program or a combination of them. . These rules can last forever, until restart the daemon or just one time. . OpenSnitch can also work as a system-wide domains blocker, by using lists of domains, list of IPs or list of regular expressions. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/copyright��������������������������������������������0000664�0000000�0000000�00000002110�15003540030�0023271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Source: https://github.com/evilsocket/opensnitch Upstream-Name: opensnitch-ui Files: * Copyright: 2017-2018 evilsocket 2019-2022 Gustavo Iñiguez Goia Comment: Debian packaging is licensed under the same terms as upstream License: GPL-3.0 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. . 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 for more details. . You should have received a copy of the GNU General Public License along with this program. If not, If not, see http://www.gnu.org/licenses/. . On Debian systems, the full text of the GNU General Public License version 3 can be found in the file '/usr/share/common-licenses/GPL-3'. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/postinst���������������������������������������������0000775�0000000�0000000�00000003434�15003540030�0023161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash set -e # https://github.com/evilsocket/opensnitch/issues/647 wa_grpcio_647() { badversion="1.30.2-3build6" source /etc/os-release if [ "$ID" = "linuxmint" -o "$ID" = "ubuntu" -o "$ID" = "pop" -o "$ID" = "elementary" -o "$ID" = "zorin" ]; then v=$(dpkg-query -W -f '${Version}' python3-grpcio) if [ "$v" = "$badversion" ]; then echo echo echo "@@@@@@@@@@@@@@@@@@@ WARNING @@@@@@@@@@@@@@@@@@@@" echo " invalid python3-grpcio version installed" echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" echo echo "Installed python3-grpcio package ($badversion) has a bug which makes opensnitch UI unresponsive." echo echo "Launch opensnitch-ui, and if it consumes 100% of the CPU, try this:" echo "~ $ sudo apt install python3-pip" echo "~ $ pip3 install grpcio==1.41.0" echo "~ $ pip3 install protobuf==3.20.0" echo echo "More information:" echo " - https://bugs.launchpad.net/ubuntu/+source/grpc/+bug/1971114" echo " - https://github.com/evilsocket/opensnitch/issues/647" echo " - https://github.com/evilsocket/opensnitch/issues/1214#issuecomment-2518864350" echo echo fi fi } autostart_by_default() { deskfile=/etc/xdg/autostart/opensnitch_ui.desktop if [ -d /etc/xdg/autostart -a ! -h $deskfile -a ! -f $deskfile ]; then ln -s /usr/share/applications/opensnitch_ui.desktop /etc/xdg/autostart/ fi } autostart_by_default if command -v gtk-update-icon-cache >/dev/null && test -f /usr/share/icons/hicolor/index.theme ; then gtk-update-icon-cache --quiet /usr/share/icons/hicolor/ fi wa_grpcio_647 #DEBHELPER# ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/postrm�����������������������������������������������0000775�0000000�0000000�00000001177�15003540030�0022624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh set -e purge_files() { for i in $(ls /home) do path=/home/$i/.config/ if [ -h $path/autostart/opensnitch_ui.desktop -o -f $path/autostart/opensnitch_ui.desktop ];then rm -f $path/autostart/opensnitch_ui.desktop fi if [ -d $path/opensnitch/ ]; then rm -rf $path/opensnitch/ fi done deskfile=/etc/xdg/autostart/opensnitch_ui.desktop if [ -h $deskfile -o -f $deskfile ]; then rm -f $deskfile fi } pkill -15 opensnitch-ui || true case "$1" in purge) purge_files ;; remove) purge_files ;; esac �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/rules������������������������������������������������0000775�0000000�0000000�00000001432�15003540030�0022424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/make -f # This file was automatically generated by stdeb 0.9.0 at # Thu, 06 Feb 2020 00:20:02 +0100 %: dh $@ --with python3 --buildsystem=python_distutils override_dh_auto_clean: rm -f opensnitch/resources_rc.py rm -rf opensnitch/i18n/ python3 setup.py clean -a find . -name \*.pyc -exec rm {} \; override_dh_auto_build: python3 setup.py build --force override_dh_auto_install: cd i18n; make cp -r i18n/locales/ opensnitch/i18n/ pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc find opensnitch/proto/ -name 'ui_pb2_grpc.py' -exec sed -i 's/^import ui_pb2/from . import ui_pb2/' {} \; python3 setup.py install --force --root=debian/python3-opensnitch-ui --no-compile -O0 --install-layout=deb override_dh_python2: dh_python2 --no-guessing-versions ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/source/����������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0022644�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/source/format����������������������������������������0000664�0000000�0000000�00000000014�15003540030�0024052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������3.0 (quilt) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/deb/debian/source/options���������������������������������������0000664�0000000�0000000�00000000040�15003540030�0024254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������extend-diff-ignore="\.egg-info$"������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/rpm/������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0020166�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/packaging/ui/rpm/opensnitch-ui.spec������������������������������������������0000664�0000000�0000000�00000005747�15003540030�0023644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%define name opensnitch-ui %define version 1.6.9 %define unmangled_version 1.6.9 %define release 1 %define __python python3 %define desktop_file opensnitch_ui.desktop Summary: Prompt service and UI for the OpenSnitch interactive application firewall. Name: %{name} Version: %{version} Release: %{release} Source0: %{name}-%{unmangled_version}.tar.gz License: GPL-3.0 Group: Development/Libraries BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Prefix: %{_prefix} BuildArch: noarch Vendor: OpenSnitch project Packager: Gustavo Iñiguez Goya <gooffy1@gmail.com> Url: https://github.com/evilsocket/opensnitch Requires: python3, python3-pip, (netcfg or setup), (python3-pyinotify or python3-inotify), python3-qt5 Recommends: (python3-slugify or python3-python-slugify), python3-notify2, python3-protobuf >= 3.0, python3-grpcio >= 1.10.0, (qgnomeplatform-qt5 or QGnomePlatform-qt5), (python3-packaging or python-rpm-packaging) # avoid to depend on a particular python version %global __requires_exclude ^python\\(abi\\) = 3\\..$ %description GUI for the opensnitch application firewall opensnitch-ui is a GUI for opensnitch written in Python. It allows the user to view live outgoing connections, as well as search to make connections. . The user can decide if block the outgoing connection based on properties of the connection: by port, by uid, by dst ip, by program or a combination of them. . These rules can last forever, until the app restart or just one time. %prep %setup -n %{name}-%{unmangled_version} -n %{name}-%{unmangled_version} %post if [ $1 -ge 1 ]; then deskfile=/etc/xdg/autostart/opensnitch_ui.desktop if [ -d /etc/xdg/autostart -a ! -h $deskfile -a ! -f $deskfile ]; then ln -s /usr/share/applications/opensnitch_ui.desktop /etc/xdg/autostart/ fi gtk-update-icon-cache /usr/share/icons/hicolor/ || true fi %postun if [ $1 -eq 0 ]; then # deprecated: kept for uninstalling old (<= v1.6.4) autostart files for i in $(ls /home) do if grep /home/$i /etc/passwd &>/dev/null; then path=/home/$i/.config/autostart/%{desktop_file} if [ -h $path -o -f $path ]; then rm -f $path else echo "[INFO] No desktop file for this user: $path" fi fi done deskfile=/etc/xdg/autostart/opensnitch_ui.desktop if [ -h $deskfile -o -f $deskfile ]; then rm -f $deskfile fi pkill -15 opensnitch-ui 2>/dev/null || true fi %build cd i18n; make; cd .. cp -r i18n/locales/ opensnitch/i18n pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc find opensnitch/proto/ -name 'ui_pb2_grpc.py' -exec sed -i 's/^import ui_pb2/from . import ui_pb2/' {} \; python3 setup.py build %install python3 setup.py install --install-lib=/usr/lib/python3/dist-packages/ --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --prefix=/usr --record=INSTALLED_FILES --install-scripts=/usr/bin %clean rm -rf $RPM_BUILD_ROOT %files -f INSTALLED_FILES %defattr(-,root,root) �������������������������opensnitch-1.6.9/utils/scripts/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0016516�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/scripts/ads/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�15003540030�0017265�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/scripts/ads/update_adlists.sh������������������������������������������������0000775�0000000�0000000�00000010325�15003540030�0022632�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # opensnitch - 2021-2022 # # https://github.com/evilsocket/opensnitch/wiki/block-lists # # Add the script to a regular user's crontab: # $ crontab -e # 0 11,17,23 * * * /home/user/scripts/update_adlists.sh # https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext # https://hostfiles.frogeye.fr/multiparty-trackers-hosts.txt # https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt # https://adaway.org/hosts.txt # https://www.github.developerdan.com/hosts/lists/tracking-aggressive-extended.txt # https://raw.githubusercontent.com/Kees1958/W3C_annual_most_used_survey_blocklist/master/TOP_EU_US_Ads_Trackers_HOST # https://curben.gitlab.io/malware-filter/urlhaus-filter-hosts.txt # Use any directory you want to save the lists. # If you use /etc/opensnitchd, give write permissions to blocklists/* for your user (chown -R /etc/opensnitchd/blocklists/). # or use a directory from your user's home. adsDir="/etc/opensnitchd/blocklists/domains/" # If you add new urls, remember to add the corresponding filename where it'll be save on disk. adsList=( "https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt" "https://hostfiles.frogeye.fr/multiparty-trackers-hosts.txt" "https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt" "https://www.github.developerdan.com/hosts/lists/tracking-aggressive-extended.txt" "https://adaway.org/hosts.txt" "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext") adsListNames=( "urlhaus-filter-hosts.txt" "multiparty-trackers-hosts.txt" "firstparty-trackers-hosts.txt" "tracking-aggressive-extended.txt" "adaway-hosts.txt" "yoyo-adservers.txt") function download_cname_trackers { remoteSize=$(curl --silent -I https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SpywareFilter/sections/cname_trackers.txt | awk '/content-length:/ {print $2}'|tr -d '\r') localSize=$(stat -c %s $adsDir/cname_trackers.txt) if [ ! -z $remoteSize ]; then if [ "$remoteSize" != "$localSize" ]; then > /tmp/.cname_temp cname_trackers=$(curl --silent https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SpywareFilter/sections/cname_trackers.txt | awk '/^!#include/ { print $2 }') for tracker in $cname_trackers do curl --silent $tracker | grep "^||" | sed 's/^||\(.*\)^/0.0.0.0 \1/' >> /tmp/.cname_temp done mv /tmp/.cname_temp $adsDir/cname_trackers.txt else echo "[-] cname trackers not updated yet" fi fi } function download_list { echo -n "[+] downloading new ads list... $1 -> $2 ($3, $4)" curl --silent "$1" -o $2 [ $? -eq 0 ] && echo " OK" || echo " FAIL" } function download_ads_list { reload=0 for idx in ${!adsList[@]} do echo "[+] Checking list ${adsList[$idx]}, ${adsListNames[$idx]}" remoteSize=$(curl --silent -I ${adsList[$idx]}|awk '/content-length:/ {print $2}'|tr -d '\r') localSize=$(stat -c %s $adsDir/${adsListNames[$idx]} 2>/dev/null) if [ ! -z $remoteSize ]; then if [ "$remoteSize" != "$localSize" ]; then download_list "${adsList[$idx]}" "$adsDir/${adsListNames[$idx]}" $remoteSize $localSize reload=1 else echo "[-] ads list not updated yet: $remoteSize, $localSize - ${adsList[$idx]}" fi else echo "[!] No content-length header found: ${adsList[$idx]}" echo "[.] Trying with Last-Modidifed" lastMod=$(date +%s -d "$(curl --silent -I ${adsList[$idx]}|grep Last-Modified|cut -d: -f 2)") localMod=$(stat -c %Y $adsDir/${adsListNames[$idx]} 2>/dev/null) [ $? -eq 1 ] && localMod=0 if [ ! -z $lastMod -a $lastMod -gt $localMod ]; then download_list "${adsList[$idx]}" "$adsDir/${adsListNames[$idx]}" $remoteSize $localSize else echo "[-] ads list not updated yet: $lastMod, $localMod - ${adsList[$idx]}" fi fi done } if [ ! -d $adsDir ]; then mkdir -p $adsDir fi cd $adsDir download_ads_list download_cname_trackers echo "[~] Done" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/scripts/debug-ebpf-maps.sh���������������������������������������������������0000664�0000000�0000000�00000004414�15003540030�0022013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # OpenSnitch - 2023 # https://github.com/evilsocket/opensnitch # # Usage: bash debug-ebpf-maps.sh tcp (or tcpv6, udp, udpv6) # function print_map_proto { case "$1" in 12001) echo "------------------------------ TCP ------------------------------" ;; 12002) echo "------------------------------ TCPv6 ------------------------------" ;; 12003) echo "------------------------------ UDP ------------------------------" ;; 12004) echo "------------------------------ UDPv6 ------------------------------" ;; esac } function dump_map { echo print_map_proto $mid bpftool map dump id $1 |awk ' BEGIN { total=0; } { split($0, line); if (line[1] == "key:"){ is_key=1; total++; } else if (is_key == 1){ sport=strtonum("0x" line[2] line[1]); dport=strtonum("0x" line[7] line[8]); printf("%d:%d.%d.%d.%d -> %d.%d.%d.%d:%d\n", sport, strtonum("0x" line[3]), strtonum("0x" line[4]), strtonum("0x" line[5]), strtonum("0x" line[6]), strtonum("0x" line[9]), strtonum("0x" line[10]), strtonum("0x" line[11]), strtonum("0x" line[12]), dport); is_key=0; } } END { printf("Total: %d\n", total); }' print_map_proto $mid } if [ -z $1 ]; then echo echo " Usage: bash debug-ebpf-maps.sh <proto> (tcp, tcpv6, udp or udpv6)" echo exit fi if ! command -v bpftool; then echo echo " [error] bpftool not found. Install it." echo exit fi mid=0 case "$1" in tcp) mid=$(bpftool map list | grep -B 1 12001 | grep hash | cut -d: -f1) ;; tcpv6) mid=$(bpftool map list | grep -B 1 12002 | grep hash | cut -d: -f1) ;; udp) mid=$(bpftool map list | grep -B 1 12003 | grep hash | cut -d: -f1) ;; udpv6) mid=$(bpftool map list | grep -B 1 12004 | grep hash | cut -d: -f1) ;; esac if [ $mid -eq 0 ]; then echo echo " [error] Invalid protocol ($1)" echo exit fi dump_map $mid ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.6.9/utils/scripts/restart-opensnitch-onsleep.sh����������������������������������������0000775�0000000�0000000�00000000635�15003540030�0024360�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # opensnitch - 2022-2023 # # Due to a bug in gobpf, when coming back from suspend state, ebpf stops working. # The temporal solution is to stop/start the daemon on suspend. # # Copy it to /lib/systemd/system-sleep/ with any name and exec permissions. # if [ "$1" == "pre" ]; then service opensnitchd stop elif [ "$1" == "post" ]; then service opensnitchd stop service opensnitchd start fi ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������