pax_global_header00006660000000000000000000000064144013267160014516gustar00rootroot0000000000000052 comment=82929239874bb680b6317dfa061ef8790da48c9d opensnitch-1.5.8.1/000077500000000000000000000000001440132671600140425ustar00rootroot00000000000000opensnitch-1.5.8.1/.github/000077500000000000000000000000001440132671600154025ustar00rootroot00000000000000opensnitch-1.5.8.1/.github/FUNDING.yml000066400000000000000000000012511440132671600172160ustar00rootroot00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: evilsocket 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.5.8.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001440132671600175655ustar00rootroot00000000000000opensnitch-1.5.8.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000032161440132671600222610ustar00rootroot00000000000000--- 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 https://github.com/evilsocket/opensnitch/wiki/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: - 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 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.5.8.1/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002131440132671600215510ustar00rootroot00000000000000contact_links: - name: 🙋 Question url: https://github.com/evilsocket/opensnitch/discussions/new about: Ask your question here opensnitch-1.5.8.1/.github/ISSUE_TEMPLATE/feature-request.md000066400000000000000000000004341440132671600232310ustar00rootroot00000000000000--- 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.5.8.1/.github/workflows/���������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017437�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/.github/workflows/debian-package.yml���������������������������������������������0000664�0000000�0000000�00000002720�14401326716�0022776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: Build status on: [push, pull_request] jobs: Builddeb: runs-on: ubuntu-latest strategy: matrix: image: ["debian:bookworm", "debian:sid"] container: image: ${{ matrix.image }} options: --cpus=2 steps: - name: Dump GitHub context env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - name: Check out git code uses: actions/checkout@v2 - name: Install pre-dependencies env: DEBIAN_FRONTEND: noninteractive run: | set -e set -x apt --quiet update # Install stuff needed to check out the linuxcnc repo and turn it into a debian source package. apt --yes --quiet install --no-install-suggests eatmydata eatmydata apt --yes --quiet install --no-install-suggests git devscripts - name: Install build dependencies env: DEBIAN_FRONTEND: noninteractive run: | set -e set -x eatmydata apt --yes --quiet build-dep --indep-only . - name: Build source client env: DEBIAN_FRONTEND: noninteractive run: | set -e set -x # Workaround for missing source tarball echo 1.0 > debian/source/format yes y | eatmydata debuild -us -uc - name: Test install debian packages env: DEBIAN_FRONTEND: noninteractive run: | set -e set -x eatmydata apt --yes --quiet install ../*.deb ������������������������������������������������opensnitch-1.5.8.1/.github/workflows/ebpf.yml�������������������������������������������������������0000664�0000000�0000000�00000002516�14401326716�0021102�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: Build eBPF on: # Trigger this workflow only when ebpf modules changes. push: paths: - 'ebpf_prog/*' - '.github/workflows/ebpf.yml' pull_request: paths: - 'ebpf_prog/*' - '.github/workflows/ebpf.yml' # Allow to run this workflow manually from the Actions tab workflow_dispatch: jobs: build: name: Build eBPF object 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 eatmydata sudo eatmydata apt install wget tar patch clang llvm libelf-dev libzip-dev flex bison libssl-dev bc rsync python3 binutils eatmydata wget --no-verbose https://github.com/torvalds/linux/archive/v5.8.tar.gz eatmydata tar -xf v5.8.tar.gz - name: Build eBPF module run: | set -e set -x eatmydata patch linux-5.8/tools/lib/bpf/bpf_helpers.h < ebpf_prog/file.patch eatmydata cp ebpf_prog/opensnitch.c ebpf_prog/Makefile linux-5.8/samples/bpf cd linux-5.8 && yes "" | eatmydata make oldconfig eatmydata make prepare eatmydata make headers_install cd samples/bpf eatmydata make eatmydata objdump -h opensnitch.o eatmydata llvm-strip -g opensnitch.o ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/.github/workflows/go.yml���������������������������������������������������������0000664�0000000�0000000�00000002436�14401326716�0020574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: Build status on: # Trigger this workflow only when daemon code changes. push: paths: - 'daemon/*' - '.github/workflows/go.yml' pull_request: paths: - 'daemon/*' - '.github/workflows/go.yml' # Allow to run this workflow manually from the Actions tab workflow_dispatch: jobs: build: name: Build Go code runs-on: ubuntu-latest steps: - name: Set up Go 1.15.15 uses: actions/setup-go@v1 with: go-version: 1.15.15 id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Get dependencies run: | sudo apt --yes --quiet install --no-install-suggests eatmydata sudo eatmydata apt install git libnetfilter-queue-dev libmnl-dev libpcap-dev protobuf-compiler export GOPATH=~/go export PATH=$PATH:$GOPATH/bin eatmydata go get github.com/golang/protobuf/protoc-gen-go eatmydata go install google.golang.org/protobuf/cmd/protoc-gen-go eatmydata go get google.golang.org/grpc/cmd/protoc-gen-go-grpc cd proto eatmydata make ../daemon/ui/protocol/ui.pb.go - name: Build run: | cd daemon eatmydata go mod tidy eatmydata go mod vendor eatmydata go build -v . ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/.gitignore�����������������������������������������������������������������������0000664�0000000�0000000�00000000035�14401326716�0016030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.sock *.pyc *.profile rules ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/LICENSE��������������������������������������������������������������������������0000664�0000000�0000000�00000104515�14401326716�0015055�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.5.8.1/Makefile�������������������������������������������������������������������������0000664�0000000�0000000�00000001543�14401326716�0015505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������all: protocol opensnitch_daemon gui install: @$(MAKE) -C daemon install @$(MAKE) -C ui install protocol: @$(MAKE) -C proto opensnitch_daemon: @$(MAKE) -C daemon gui: @$(MAKE) -C ui clean: @$(MAKE) -C daemon clean @$(MAKE) -C proto clean @$(MAKE) -C ui 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.5.8.1/README.md������������������������������������������������������������������������0000664�0000000�0000000�00000002706�14401326716�0015326�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> **OpenSnitch** is a GNU/Linux application firewall. <p align="center"> <img src="https://user-images.githubusercontent.com/2742953/85205382-6ba9cb00-b31b-11ea-8e9a-bd4b8b05a236.png" alt="OpenSnitch"/> </p> ### Installation and configuration Please, refer to [the documentation](https://github.com/evilsocket/opensnitch/wiki) for detailed information. ### Contributors [See the list](https://github.com/evilsocket/opensnitch/graphs/contributors) ����������������������������������������������������������opensnitch-1.5.8.1/daemon/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015305�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/.gitignore����������������������������������������������������������������0000664�0000000�0000000�00000000023�14401326716�0017270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitchd vendor �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/Gopkg.toml����������������������������������������������������������������0000664�0000000�0000000�00000000540�14401326716�0017250�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.5.8.1/daemon/Makefile������������������������������������������������������������������0000664�0000000�0000000�00000000740�14401326716�0016746�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') all: opensnitchd install: @mkdir -p /etc/opensnitchd/rules @cp opensnitchd /usr/local/bin/ @cp opensnitchd.service /etc/systemd/system/ @cp default-config.json /etc/opensnitchd/ @cp system-fw.json /etc/opensnitchd/ @systemctl daemon-reload opensnitchd: $(SRC) @go get @go build -o opensnitchd . clean: @rm -rf opensnitchd ��������������������������������opensnitch-1.5.8.1/daemon/conman/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016560�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/conman/connection.go������������������������������������������������������0000664�0000000�0000000�00000016171�14401326716�0021254�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/ebpf" "github.com/evilsocket/opensnitch/daemon/ui/protocol" "github.com/google/gopacket/layers" ) // Connection represents an outgoing connection. type Connection struct { Protocol string SrcIP net.IP SrcPort uint DstIP net.IP DstPort uint DstHost string Entry *netstat.Entry Process *procmon.Process pkt *netfilter.Packet } 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:%d uid: %d", c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstPort, nfp.UID) 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() { pid, uid, err = ebpf.GetPid(c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstPort) if err != nil { log.Warning("ebpf warning: %v", err) return nil, nil } } // sometimes when using eBPF the connection is not found, but falling back to legacy // methods helps to find it and avoid "unknown/kernel pop-ups". TODO: investigate 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 { if c.Entry = netstat.FindEntry(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort); c.Entry == nil { return nil, fmt.Errorf("Could not find netstat entry for: %s", c) } if c.Entry.INode > 0 { log.Debug("connection found in netstat: %v", c.Entry) inodeList = append([]int{c.Entry.INode}, inodeList...) } } if len(inodeList) == 0 { log.Debug("<== no inodes found, applying default action.") } 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 nfp.UID != 0xffffffff { c.Entry.UserId = int(nfp.UID) } else { c.Entry.UserId = uid } 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 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 } } return ret } func (c *Connection) getDomains(nfp *netfilter.Packet, con *Connection) { domains := dns.GetQuestions(nfp) if len(domains) > 0 { 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 c.DstHost } func (c *Connection) String() string { if c.Entry == nil { return fmt.Sprintf("%s ->(%s)-> %s:%d", c.SrcIP, c.Protocol, c.To(), c.DstPort) } if c.Process == nil { return fmt.Sprintf("%s (uid:%d) ->(%s)-> %s:%d", 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.5.8.1/daemon/conman/connection_test.go�������������������������������������������������0000664�0000000�0000000�00000007042�14401326716�0022310�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.5.8.1/daemon/core/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016235�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/core/core.go��������������������������������������������������������������0000664�0000000�0000000�00000002610�14401326716�0017513�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 } // 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 } ������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/core/system.go������������������������������������������������������������0000664�0000000�0000000�00000001153�14401326716�0020110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package core import ( "io/ioutil" "strings" ) 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 name of the host where the daemon is running. func GetKernelVersion() string { version, _ := ioutil.ReadFile("/proc/sys/kernel/version") return strings.Replace(string(version), "\n", "", -1) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/core/version.go�����������������������������������������������������������0000664�0000000�0000000�00000000310�14401326716�0020243�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package core // version related consts const ( Name = "opensnitch-daemon" Version = "1.5.8" Author = "Simone 'evilsocket' Margaritelli" Website = "https://github.com/evilsocket/opensnitch" ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/default-config.json�������������������������������������������������������0000664�0000000�0000000�00000000551�14401326716�0021070�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, "Firewall": "iptables", "Stats": { "MaxEvents": 150, "MaxStats": 25 } } �������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/dns/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016071�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/dns/parse.go��������������������������������������������������������������0000664�0000000�0000000�00000000777�14401326716�0017545�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.5.8.1/daemon/dns/track.go��������������������������������������������������������������0000664�0000000�0000000�00000004037�14401326716�0017530�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 resolved == "127.0.0.1" || resolved == "::1" { 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.5.8.1/daemon/firewall/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017112�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/common/����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020402�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/common/common.go�������������������������������������������������0000664�0000000�0000000�00000004010�14401326716�0022214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package common import ( "sync" "time" "github.com/evilsocket/opensnitch/daemon/log" ) type ( callback func() callbackBool func() bool stopChecker struct { sync.RWMutex ch chan bool } // Common holds common fields and functionality of both firewalls, // iptables and nftables. Common struct { sync.RWMutex QueueNum uint16 Running bool RulesChecker *time.Ticker stopCheckerChan *stopChecker } ) func (s *stopChecker) exit() chan bool { s.RLock() defer s.RUnlock() return s.ch } func (s *stopChecker) stop() { s.Lock() defer s.Unlock() if s.ch != nil { s.ch <- true close(s.ch) s.ch = nil } } // 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 } // NewRulesChecker starts monitoring firewall for configuration or rules changes. func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callback) { c.Lock() defer c.Unlock() c.stopCheckerChan = &stopChecker{ch: make(chan bool, 1)} c.RulesChecker = time.NewTicker(time.Second * 30) go c.startCheckingRules(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 (c *Common) startCheckingRules(areRulesLoaded callbackBool, reloadRules callback) { for { select { case <-c.stopCheckerChan.exit(): goto Exit case <-c.RulesChecker.C: if areRulesLoaded() == false { reloadRules() } } } Exit: log.Info("exit checking iptables rules") } // StopCheckingRules stops checking if firewall rules are loaded. func (c *Common) StopCheckingRules() { if c.RulesChecker != nil { c.RulesChecker.Stop() } c.stopCheckerChan.stop() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/config/����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020357�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/config/config.go�������������������������������������������������0000664�0000000�0000000�00000011273�14401326716�0022157�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" "sync" "github.com/evilsocket/opensnitch/daemon/log" "github.com/fsnotify/fsnotify" ) type callback func() // FwRule holds the fields of a rule type FwRule struct { sync.RWMutex Description string Table string Chain string Parameters string Target string TargetParameters string } type rulesList struct { sync.RWMutex Rule *FwRule } // SystemConfig holds the list of rules to be added to the system type SystemConfig struct { sync.RWMutex SystemRules []*rulesList } // 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 { sync.Mutex file string watcher *fsnotify.Watcher monitorExitChan chan bool SysConfig SystemConfig // subscribe to this channel to receive config reload events ReloadConfChan chan bool // preloadCallback is called before reloading the configuration, // in order to delete old fw rules. preloadCallback callback } // NewSystemFwConfig initializes config fields func (c *Config) NewSystemFwConfig(preLoadCb callback) (*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.watcher = watcher c.ReloadConfChan = make(chan bool, 1) return c, nil } // LoadDiskConfiguration reads and loads the firewall configuration from disk func (c *Config) LoadDiskConfiguration(reload bool) { 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 } c.loadConfiguration(raw) // 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 } if reload { c.ReloadConfChan <- true return } go c.monitorConfigWorker() } // loadConfigutation reads the system firewall rules from disk. // Then the rules are added based on the configuration defined. func (c *Config) loadConfiguration(rawConfig []byte) { 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) } log.Info("fw configuration loaded") } func (c *Config) saveConfiguration(rawConfig string) error { conf, err := json.Marshal([]byte(rawConfig)) if err != nil { log.Error("saving json firewall configuration: %s %s", err, conf) return err } c.loadConfiguration([]byte(rawConfig)) if err = ioutil.WriteFile(c.file, []byte(rawConfig), 0644); 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.ReloadConfChan != nil { c.ReloadConfChan <- false // exit close(c.ReloadConfChan) } 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(true) } } } Exit: log.Debug("stop monitoring firewall config file") c.Lock() c.monitorExitChan = nil c.Unlock() } // MonitorSystemFw waits for configuration reloads. func (c *Config) MonitorSystemFw(reloadCallback callback) { for { select { case reload := <-c.ReloadConfChan: if reload { reloadCallback() } else { goto Exit } } } Exit: log.Info("iptables, stop monitoring system fw rules") c.Lock() c.ReloadConfChan = nil c.Unlock() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/iptables/��������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020715�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/iptables/iptables.go���������������������������������������������0000664�0000000�0000000�00000006604�14401326716�0023055�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iptables import ( "os/exec" "regexp" "sync" "github.com/evilsocket/opensnitch/daemon/firewall/common" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/log" ) // 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") ) // SystemChains holds the fw rules defined by the user type SystemChains struct { sync.RWMutex Rules map[string]config.FwRule } // Iptables struct holds the fields of the iptables fw type Iptables struct { sync.Mutex config.Config common.Common bin string bin6 string regexRulesQuery *regexp.Regexp regexSystemRulesQuery *regexp.Regexp chains SystemChains } // 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]config.FwRule)}, } 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) // In order to clean up any existing firewall rule before start, // we need to load the fw configuration first. ipt.NewSystemFwConfig(ipt.preloadConfCallback) go ipt.MonitorSystemFw(ipt.AddSystemRules) ipt.LoadDiskConfiguration(false) // start from a clean state ipt.CleanRules(false) ipt.InsertRules() ipt.AddSystemRules() // start monitoring firewall rules to intercept network traffic ipt.NewRulesChecker(ipt.AreRulesLoaded, ipt.reloadRulesCallback) 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. func IsAvailable() error { _, err := exec.Command("iptables", []string{"-V"}...).CombinedOutput() if err != nil { return err } return nil } // InsertRules adds fw rules to intercept connections func (ipt *Iptables) InsertRules() { if err4, err6 := ipt.QueueDNSResponses(true, true); err4 != nil || err6 != nil { log.Error("Error while running DNS firewall rule: %s %s", err4, err6) } else if err4, err6 = ipt.QueueConnections(true, true); err4 != nil || err6 != nil { log.Fatal("Error while running conntrack firewall rule: %s %s", err4, err6) } } // CleanRules deletes the rules we added. func (ipt *Iptables) CleanRules(logErrors bool) { ipt.QueueDNSResponses(false, logErrors) ipt.QueueConnections(false, logErrors) ipt.DeleteSystemRules(true, logErrors) } ����������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/iptables/monitor.go����������������������������������������������0000664�0000000�0000000�00000003117�14401326716�0022735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iptables import ( "github.com/evilsocket/opensnitch/daemon/core" "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 } func (ipt *Iptables) reloadRulesCallback() { log.Important("firewall rules changed, reloading") ipt.QueueDNSResponses(false, false) ipt.QueueConnections(false, false) ipt.InsertRules() ipt.AddSystemRules() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/iptables/rules.go������������������������������������������������0000664�0000000�0000000�00000004276�14401326716�0022407�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. // They 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(INSERT, 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.5.8.1/daemon/firewall/iptables/system.go�����������������������������������������������0000664�0000000�0000000�00000005237�14401326716�0022577�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iptables import ( "strings" "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/log" ) // CreateSystemRule creates the custom firewall chains and adds them to the system. func (ipt *Iptables) CreateSystemRule(rule *config.FwRule, logErrors bool) { ipt.chains.Lock() defer ipt.chains.Unlock() if rule == nil { return } chainName := SystemRulePrefix + "-" + rule.Chain if _, ok := ipt.chains.Rules[rule.Table+"-"+chainName]; ok { return } ipt.RunRule(NEWCHAIN, true, logErrors, []string{chainName, "-t", rule.Table}) // Insert the rule at the top of the chain if err4, err6 := ipt.RunRule(INSERT, true, logErrors, []string{rule.Chain, "-t", rule.Table, "-j", chainName}); err4 == nil && err6 == nil { ipt.chains.Rules[rule.Table+"-"+chainName] = *rule } } // 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, logErrors bool) { ipt.chains.Lock() defer ipt.chains.Unlock() for _, r := range ipt.SysConfig.SystemRules { if r.Rule == nil { continue } chain := SystemRulePrefix + "-" + r.Rule.Chain if _, ok := ipt.chains.Rules[r.Rule.Table+"-"+chain]; !ok && !force { continue } ipt.RunRule(FLUSH, true, false, []string{chain, "-t", r.Rule.Table}) ipt.RunRule(DELETE, false, logErrors, []string{r.Rule.Chain, "-t", r.Rule.Table, "-j", chain}) ipt.RunRule(DELCHAIN, true, false, []string{chain, "-t", r.Rule.Table}) delete(ipt.chains.Rules, r.Rule.Table+"-"+chain) } } // AddSystemRule inserts a new rule. func (ipt *Iptables) AddSystemRule(rule *config.FwRule, enable bool) (err4, err6 error) { if rule == nil { return nil, nil } rule.RLock() defer rule.RUnlock() chain := SystemRulePrefix + "-" + rule.Chain if rule.Table == "" { rule.Table = "filter" } r := []string{chain, "-t", rule.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) } // AddSystemRules creates the system firewall from configuration. func (ipt *Iptables) AddSystemRules() { ipt.DeleteSystemRules(true, false) for _, r := range ipt.SysConfig.SystemRules { ipt.CreateSystemRule(r.Rule, true) ipt.AddSystemRule(r.Rule, true) } } // preloadConfCallback gets called before the fw configuration is reloaded func (ipt *Iptables) preloadConfCallback() { ipt.DeleteSystemRules(true, log.GetLogLevel() == log.DEBUG) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/nftables/��������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020710�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/nftables/monitor.go����������������������������������������������0000664�0000000�0000000�00000002327�14401326716�0022732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "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 for _, table := range n.mangleTables { rules, err := n.conn.GetRule(table, n.outputChains[table]) if err != nil { log.Error("nftables mangle rules error: %s, %s", table.Name, n.outputChains[table].Name) return false } for _, r := range rules { if string(r.UserData) == fwKey { nRules++ } } } if nRules != 2 { log.Warning("nftables mangle rules not loaded: %d", nRules) return false } nRules = 0 for _, table := range n.filterTables { rules, err := n.conn.GetRule(table, n.inputChains[table]) if err != nil { log.Error("nftables filter rules error: %s, %s", table.Name, n.inputChains[table].Name) return false } for _, r := range rules { if string(r.UserData) == fwKey { nRules++ } } } if nRules != 2 { log.Warning("nfables filter rules not loaded: %d", nRules) return false } return true } func (n *Nft) reloadRulesCallback() { log.Important("nftables firewall rules changed, reloading") n.AddSystemRules() n.InsertRules() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/nftables/nftables.go���������������������������������������������0000664�0000000�0000000�00000006655�14401326716�0023051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "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/log" "github.com/google/nftables" ) const ( // Name is the name that identifies this firewall Name = "nftables" mangleTableName = "mangle" filterTableName = "filter" // The following chains will be under our own mangle or filter tables. // There shouldn't be other chains with the same name here. outputChain = "output" inputChain = "input" // key assigned to every fw rule we add, in order to get rules by this key. fwKey = "opensnitch-key" ) var ( filterTable = &nftables.Table{ Family: nftables.TableFamilyIPv4, Name: filterTableName, } filterTable6 = &nftables.Table{ Family: nftables.TableFamilyIPv6, Name: filterTableName, } mangleTable = &nftables.Table{ Family: nftables.TableFamilyIPv4, Name: mangleTableName, } mangleTable6 = &nftables.Table{ Family: nftables.TableFamilyIPv6, Name: mangleTableName, } ) // Nft holds the fields of our nftables firewall type Nft struct { sync.Mutex config.Config common.Common conn *nftables.Conn mangleTables []*nftables.Table filterTables []*nftables.Table outputChains map[*nftables.Table]*nftables.Chain inputChains map[*nftables.Table]*nftables.Chain chains iptables.SystemChains } // 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{ outputChains: make(map[*nftables.Table]*nftables.Chain), inputChains: make(map[*nftables.Table]*nftables.Chain), chains: iptables.SystemChains{Rules: make(map[string]config.FwRule)}, } 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.SetQueueNum(qNum) n.conn = NewNft() // In order to clean up any existing firewall rule before start, // we need to load the fw configuration first. n.NewSystemFwConfig(n.preloadConfCallback) go n.MonitorSystemFw(n.AddSystemRules) n.LoadDiskConfiguration(false) // start from a clean state n.CleanRules(false) n.AddSystemRules() n.InsertRules() // start monitoring firewall rules to intercept network traffic. n.NewRulesChecker(n.AreRulesLoaded, n.reloadRulesCallback) 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.Running = false } // InsertRules adds fw rules to intercept connections func (n *Nft) InsertRules() { n.delInterceptionRules() n.addGlobalTables() n.addGlobalChains() if err, _ := n.QueueDNSResponses(true, true); err != nil { log.Error("Error while Running DNS nftables rule: %s", err) } else if err, _ = n.QueueConnections(true, true); err != nil { log.Fatal("Error while Running conntrack nftables rule: %s", err) } } // CleanRules deletes the rules we added. func (n *Nft) CleanRules(logErrors bool) { n.delInterceptionRules() err := n.conn.Flush() if err != nil && logErrors { log.Error("Error cleaning nftables tables: %s", err) } n.DeleteSystemRules(true, logErrors) } �����������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/nftables/rules.go������������������������������������������������0000664�0000000�0000000�00000012500�14401326716�0022367�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/nftables" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" "github.com/vishvananda/netlink" "golang.org/x/sys/unix" ) func (n *Nft) addGlobalTables() error { filter := n.conn.AddTable(filterTable) filter6 := n.conn.AddTable(filterTable6) mangle := n.conn.AddTable(mangleTable) mangle6 := n.conn.AddTable(mangleTable6) n.mangleTables = []*nftables.Table{mangle, mangle6} n.filterTables = []*nftables.Table{filter, filter6} // apply changes if err := n.conn.Flush(); err != nil { return err } return nil } // TODO: add more parameters, make it more generic func (n *Nft) addChain(name string, table *nftables.Table, prio *nftables.ChainPriority, ctype nftables.ChainType, hook *nftables.ChainHook) *nftables.Chain { // nft list chains return n.conn.AddChain(&nftables.Chain{ Name: name, Table: table, Type: ctype, Hooknum: hook, Priority: prio, //Policy: nftables.ChainPolicyDrop }) } func (n *Nft) addGlobalChains() error { // nft list tables for _, table := range n.mangleTables { n.outputChains[table] = n.addChain(outputChain, table, nftables.ChainPriorityMangle, nftables.ChainTypeRoute, nftables.ChainHookOutput) } for _, table := range n.filterTables { n.inputChains[table] = n.addChain(inputChain, table, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput) } // apply changes if err := n.conn.Flush(); err != nil { log.Warning("Error adding nftables mangle tables: %v", err) } return nil } // QueueDNSResponses redirects DNS responses to us, in order to keep a cache // of resolved domains. // 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 } for _, table := range n.filterTables { // nft list ruleset -a n.conn.InsertRule(&nftables.Rule{ Position: 0, Table: table, Chain: n.inputChains[table], 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(fwKey), }) } // apply changes if err := n.conn.Flush(); err != nil { return err, nil } return nil, nil } // QueueConnections inserts the firewall rule which redirects connections to us. // They are queued until the user denies/accept them, or reaches a timeout. // 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, nil } 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("nftables, error in ConntrackTableFlush %s", err) } } for _, table := range n.mangleTables { n.conn.InsertRule(&nftables.Rule{ Position: 0, Table: table, Chain: n.outputChains[table], Exprs: []expr.Any{ &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(fwKey), }) } // apply changes if err := n.conn.Flush(); err != nil { return err, nil } return nil, nil } func (n *Nft) delInterceptionRules() { n.delRulesByKey(fwKey) } func (n *Nft) delRulesByKey(key string) { chains, err := n.conn.ListChains() if err != nil { log.Warning("nftables, error listing chains: %s", err) return } commit := false for _, c := range chains { rules, err := n.conn.GetRule(c.Table, c) if err != nil { log.Warning("nftables, error listing rules (%s): %s", c.Table.Name, err) continue } commit = false for _, r := range rules { if string(r.UserData) != key { continue } // just passing the rule 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 adding rule to be deleted (%s/%s): %s", c.Table.Name, c.Name, err) continue } commit = true } if commit { if err := n.conn.Flush(); err != nil { log.Warning("nftables, error deleting interception rules (%s/%s): %s", c.Table.Name, c.Name, err) } } if rules, err := n.conn.GetRule(c.Table, c); err == nil { if commit && len(rules) == 0 { n.conn.DelChain(c) n.conn.Flush() } } } return } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/nftables/system.go�����������������������������������������������0000664�0000000�0000000�00000002251�14401326716�0022563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nftables import ( "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/log" ) // 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(rule *config.FwRule, logErrors bool) { // TODO } // 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 (n *Nft) DeleteSystemRules(force, logErrors bool) { // TODO } // AddSystemRule inserts a new rule. func (n *Nft) AddSystemRule(rule *config.FwRule, enable bool) (error, error) { // TODO return nil, nil } // AddSystemRules creates the system firewall from configuration func (n *Nft) AddSystemRules() { n.DeleteSystemRules(true, false) for _, r := range n.SysConfig.SystemRules { n.CreateSystemRule(r.Rule, true) n.AddSystemRule(r.Rule, true) } } // preloadConfCallback gets called before the fw configuration is reloaded func (n *Nft) preloadConfCallback() { n.DeleteSystemRules(true, log.GetLogLevel() == log.DEBUG) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/firewall/rules.go���������������������������������������������������������0000664�0000000�0000000�00000003566�14401326716�0020605�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package firewall import ( "github.com/evilsocket/opensnitch/daemon/firewall/config" "github.com/evilsocket/opensnitch/daemon/firewall/iptables" "github.com/evilsocket/opensnitch/daemon/firewall/nftables" "github.com/evilsocket/opensnitch/daemon/log" ) // Firewall is the interface that all firewalls (iptables, nftables) must implement. type Firewall interface { Init(*int) Stop() Name() string IsRunning() bool SetQueueNum(num *int) InsertRules() QueueDNSResponses(bool, bool) (error, error) QueueConnections(bool, bool) (error, error) CleanRules(bool) AddSystemRules() DeleteSystemRules(bool, bool) AddSystemRule(*config.FwRule, bool) (error, error) CreateSystemRule(*config.FwRule, bool) } var fw Firewall // IsRunning returns if the firewall is running or not. func IsRunning() bool { return fw != nil && fw.IsRunning() } // CleanRules deletes the rules we added. func CleanRules(logErrors bool) { if fw == nil { return } fw.CleanRules(logErrors) } // Stop deletes the firewall rules, allowing network traffic. func Stop() { if fw == nil { return } fw.Stop() } // Init initializes the firewall and loads firewall rules. func Init(fwType string, qNum *int) { var err error if fwType == iptables.Name { fw, err = iptables.Fw() if err != nil { log.Warning("iptables not available: %s", err) } } // if iptables is not installed, we can add nftables rules directly to the kernel, // without relying on any binaries. if fwType == nftables.Name || err != nil { fw, err = nftables.Fw() if err != nil { log.Warning("nftables not available: %s", err) } } if err != nil { log.Warning("firewall error: %s, not iptables nor nftables are available or are usable. Please, report it on github.", err) return } if fw == nil { log.Error("firewall not initialized.") return } fw.Stop() fw.Init(qNum) log.Info("Using %s firewall", fw.Name()) } ������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/go.mod��������������������������������������������������������������������0000664�0000000�0000000�00000000746�14401326716�0016422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������module github.com/evilsocket/opensnitch/daemon go 1.15 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/iovisor/gobpf v0.2.0 github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452 golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 google.golang.org/grpc v1.32.0 google.golang.org/protobuf v1.26.0 ) ��������������������������opensnitch-1.5.8.1/daemon/log/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016066�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/log/log.go����������������������������������������������������������������0000664�0000000�0000000�00000010054�14401326716�0017176�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 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.Lock() defer mutex.Unlock() 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.Lock() defer mutex.Unlock() return MinLevel } // 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] when := time.Now().UTC().Format(DateFormat) 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.5.8.1/daemon/main.go�������������������������������������������������������������������0000664�0000000�0000000�00000026144�14401326716�0016567�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "bytes" "context" "flag" "fmt" "io/ioutil" golog "log" "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/firewall" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/netfilter" "github.com/evilsocket/opensnitch/daemon/netlink" "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" ) var ( showVersion = false procmonMethod = "" logFile = "" rulesPath = "rules" 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) ) func init() { flag.BoolVar(&showVersion, "version", debug, "Show daemon version of this executable and exit.") 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.StringVar(&rulesPath, "rules-path", rulesPath, "Path to load JSON rules from.") 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(&logFile, "log-file", logFile, "Write logs to this file instead of the standard output.") 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.") } func overwriteLogging() bool { return debug || warning || important || errorlog || logFile != "" } 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) } 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 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() }() } 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) } } func doCleanup(queue, repeatQueue *netfilter.Queue) { log.Info("Cleaning up ...") firewall.Stop() monitor.End() uiClient.Close() queue.Close() repeatQueue.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) 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) stats.OnConnectionEvent(con, r, r == nil) } func applyDefaultAction(packet *netfilter.Packet) { if uiClient.DefaultAction() == rule.Allow { packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark) } else { 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) 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 r = uiClient.Ask(con) if r == nil { log.Error("Invalid rule received, applying default action") applyDefaultAction(packet) 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) 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 -> %s:%d (%s)", log.Bold(log.Green("✔")), log.Bold(con.Process.Path), log.Bold(con.To()), con.DstPort, 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 -> %s:%d (%s)", log.Bold(log.Red("✘")), log.Bold(con.Process.Path), log.Bold(con.To()), con.DstPort, 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) } setupLogging() 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) } } log.Important("Starting %s v%s", core.Name, core.Version) rulesPath, err := core.ExpandPath(rulesPath) if err != nil { log.Fatal("%s", err) } setupSignals() log.Info("Loading rules from %s ...", rulesPath) if rules, err = rule.NewLoader(!noLiveReload); err != nil { log.Fatal("%s", err) } else if err = rules.Load(rulesPath); err != nil { log.Fatal("%s", err) } stats = statistics.New(rules) // prepare the queue setupWorkers() queue, err := netfilter.NewQueue(uint16(queueNum)) if err != nil { log.Warning("Is opensnitchd already running?") log.Fatal("Error while creating queue #%d: %s", queueNum, err) } pktChan = queue.Packets() repeatQueueNum = queueNum + 1 repeatQueue, rqerr := netfilter.NewQueue(uint16(repeatQueueNum)) if rqerr != nil { log.Warning("Is opensnitchd already running?") log.Fatal("Error while creating queue #%d: %s", repeatQueueNum, rqerr) } repeatPktChan = repeatQueue.Packets() uiClient = ui.NewClient(uiSocket, stats, rules) stats.SetConfig(uiClient.GetStatsConfig()) // queue is ready, run firewall rules firewall.Init(uiClient.GetFirewallType(), &queueNum) 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); err != nil { log.Warning("Unable to set process monitor method via parameter: %v", err) } } 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.5.8.1/daemon/netfilter/����������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017301�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/netfilter/packet.go�������������������������������������������������������0000664�0000000�0000000�00000002516�14401326716�0021103�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 type VerdictContainer struct { Verdict Verdict Mark uint32 Packet []byte } // Packet holds the data of a network packet type Packet struct { Packet gopacket.Packet Mark uint32 verdictChannel chan VerdictContainer 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} } 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: 0} } 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.5.8.1/daemon/netfilter/queue.c���������������������������������������������������������0000664�0000000�0000000�00000000024�14401326716�0020565�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "queue.h" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/netfilter/queue.go��������������������������������������������������������0000664�0000000�0000000�00000014647�14401326716�0020770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netfilter /* #cgo pkg-config: libnetfilter_queue #cgo CFLAGS: -Wall -I/usr/include #cgo LDFLAGS: -L/usr/lib64/ -ldl #include "queue.h" */ import "C" import ( "fmt" "os" "sync" "time" "unsafe" "github.com/evilsocket/opensnitch/daemon/log" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) 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{} exitChan = make(chan bool, 1) 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 fd C.int packets chan Packet 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(exitChan) 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 { return fmt.Errorf("Error unbinding existing q handler from AF_INET protocol family: %v", err) } else if ret, err = C.nfq_unbind_pf(q.h, AF_INET6); err != nil || ret < 0 { return fmt.Errorf("Error unbinding existing q handler from AF_INET6 protocol family: %v", err) } else if ret, err := C.nfq_bind_pf(q.h, AF_INET); err != nil || ret < 0 { return fmt.Errorf("Error binding to AF_INET protocol family: %v", err) } else if ret, err := C.nfq_bind_pf(q.h, AF_INET6); err != nil || ret < 0 { return fmt.Errorf("Error binding to AF_INET6 protocol family: %v", err) } else if q.qh, err = C.CreateQueue(q.h, C.u_int16_t(queueID), C.u_int32_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.u_int32_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.u_int8_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(exitCh chan<- bool) { if errno := C.Run(q.h, q.fd); errno != 0 { fmt.Fprintf(os.Stderr, "Terminating, unable to receive packet due to errno=%d", errno) } exitChan <- true } // 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() { close(q.packets) C.stop_reading_packets() q.destroy() queueIndexLock.Lock() delete(queueIndex, q.idx) queueIndexLock.Unlock() } func (q *Queue) destroy() { // we'll try to exit cleanly, but sometimes nfqueue gets stuck time.AfterFunc(5*time.Second, func() { log.Warning("queue stuck, closing by timeout") if q != nil { C.close(q.fd) q.closeNfq() } os.Exit(0) }) C.nfq_unbind_pf(q.h, AF_INET) C.nfq_unbind_pf(q.h, AF_INET6) if q.qh != nil { if ret := C.nfq_destroy_queue(q.qh); ret != 0 { log.Warning("Queue.destroy(), nfq_destroy_queue() not closed: %d", ret) } } q.closeNfq() } func (q *Queue) closeNfq() { if q.h != nil { if ret := C.nfq_close(q.h); ret != 0 { log.Warning("Queue.destroy(), nfq_close() not closed: %d", 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 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 } 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.5.8.1/daemon/netfilter/queue.h���������������������������������������������������������0000664�0000000�0000000�00000006020�14401326716�0020574�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 { uint verdict; uint mark; uint mark_set; uint length; unsigned char *data; } verdictContainer; static void *get_uid = NULL; extern void go_callback(int id, unsigned char* data, int len, uint mark, u_int32_t idx, verdictContainer *vc, uint32_t uid); 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; 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); 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, u_int16_t queue, u_int32_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.5.8.1/daemon/netlink/������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016751�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/netlink/socket.go���������������������������������������������������������0000664�0000000�0000000�00000013212�14401326716�0020567�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netlink import ( "fmt" "net" "strconv" "syscall" "github.com/evilsocket/opensnitch/daemon/log" ) // 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 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) } } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/netlink/socket_linux.go���������������������������������������������������0000664�0000000�0000000�00000014546�14401326716�0022021�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.5.8.1/daemon/netlink/socket_test.go����������������������������������������������������0000664�0000000�0000000�00000005646�14401326716�0021642�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.5.8.1/daemon/netstat/������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016767�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/netstat/entry.go����������������������������������������������������������0000664�0000000�0000000�00000001404�14401326716�0020456�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 SrcPort uint DstIP net.IP DstPort uint UserId int INode int } // 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.5.8.1/daemon/netstat/find.go�����������������������������������������������������������0000664�0000000�0000000�00000002462�14401326716�0020242�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.5.8.1/daemon/netstat/parse.go����������������������������������������������������������0000664�0000000�0000000�00000005254�14401326716�0020436�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package netstat import ( "bufio" "encoding/binary" "fmt" "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 := fmt.Sprintf("/proc/net/%s", 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.5.8.1/daemon/opensnitch.spec�����������������������������������������������������������0000664�0000000�0000000�00000005312�14401326716�0020334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Name: opensnitch Version: 1.5.8 Release: 1%{?dist} Summary: OpenSnitch is a GNU/Linux 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/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 debian/opensnitch.logrotate %{buildroot}/etc/logrotate.d/opensnitch B="" if [ -f /etc/opensnitchd/default-config.json ]; then B="-b" fi install -m 644 -b $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 $B daemon/system-fw.json %{buildroot}/etc/opensnitchd/system-fw.json install -m 644 ebpf_prog/opensnitch.o %{buildroot}/etc/opensnitchd/opensnitch.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 %{_bindir}/opensnitchd /usr/lib/systemd/system/opensnitch.service %{_sysconfdir}/opensnitchd/default-config.json %{_sysconfdir}/opensnitchd/system-fw.json %{_sysconfdir}/opensnitchd/opensnitch.o %{_sysconfdir}/logrotate.d/opensnitch ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/opensnitchd.service�������������������������������������������������������0000664�0000000�0000000�00000000673�14401326716�0021213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Unit] Description=OpenSnitch is a GNU/Linux port of the Little Snitch application firewall. 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/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules Restart=always RestartSec=30 [Install] WantedBy=multi-user.target ���������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016762�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/activepids.go�����������������������������������������������������0000664�0000000�0000000�00000004604�14401326716�0021450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "io/ioutil" "strconv" "strings" "sync" "time" "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(fmt.Sprintf("/proc/%d/stat", k)) 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 } func addToActivePidsCache(pid uint64, proc *Process) { data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", pid)) 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.5.8.1/daemon/procmon/activepids_test.go������������������������������������������������0000664�0000000�0000000�00000006033�14401326716�0022505�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.5.8.1/daemon/procmon/audit/������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020070�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/audit/client.go���������������������������������������������������0000664�0000000�0000000�00000022076�14401326716�0021704�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.5.8.1/daemon/procmon/audit/parse.go����������������������������������������������������0000664�0000000�0000000�00000017246�14401326716�0021543�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.5.8.1/daemon/procmon/cache.go����������������������������������������������������������0000664�0000000�0000000�00000016423�14401326716�0020362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "os" "sort" "sync" "time" "github.com/evilsocket/opensnitch/daemon/core" ) // InodeItem represents an item of the InodesCache. type InodeItem struct { sync.RWMutex Pid int FdPath string LastSeen int64 } // ProcItem represents an item of the pidsCache type ProcItem struct { sync.RWMutex Pid int FdPath string Descriptors []string LastSeen int64 } // CacheProcs holds the cache of processes that have established connections. type CacheProcs struct { sync.RWMutex items []*ProcItem } // CacheInodes holds the cache of Inodes. // The key is formed as follow: // inode+srcip+srcport+dstip+dstport type CacheInodes struct { sync.RWMutex items map[string]*InodeItem } 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 = fmt.Sprint("/proc/", 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 := fmt.Sprint(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.5.8.1/daemon/procmon/cache_test.go�����������������������������������������������������0000664�0000000�0000000�00000006605�14401326716�0021422�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.5.8.1/daemon/procmon/details.go��������������������������������������������������������0000664�0000000�0000000�00000011244�14401326716�0020740�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/netlink" ) var socketsRegex, _ = regexp.Compile(`socket:\[([0-9]+)\]`) // GetInfo collects information of a process. func (p *Process) GetInfo() error { if err := p.readPath(); err != nil { return err } p.readCwd() p.readCmdline() p.readEnv() p.readDescriptors() p.readIOStats() p.readStatus() p.cleanPath() return nil } func (p *Process) setCwd(cwd string) { p.CWD = cwd } func (p *Process) readComm() error { data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", p.ID)) if err != nil { return err } p.Comm = core.Trim(string(data)) return nil } func (p *Process) readCwd() error { link, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", p.ID)) if err != nil { return err } p.CWD = link return nil } // read and parse environment variables of a process. func (p *Process) readEnv() { if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/environ", p.ID)); err == nil { 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 } } } } func (p *Process) readPath() error { linkName := fmt.Sprint("/proc/", p.ID, "/exe") if _, err := os.Lstat(linkName); err != nil { return err } if link, err := os.Readlink(linkName); err == nil { p.Path = link } return nil } func (p *Process) readCmdline() { if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", p.ID)); err == nil { if len(data) == 0 { return } for i, b := range data { if b == 0x00 { data[i] = byte(' ') } } p.Args = make([]string, 0) args := strings.Split(string(data), " ") for _, arg := range args { arg = core.Trim(arg) if arg != "" { p.Args = append(p.Args, arg) } } } } func (p *Process) readDescriptors() { f, err := os.Open(fmt.Sprint("/proc/", 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(fmt.Sprint("/proc/", 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(fmt.Sprint("/proc/", 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(fmt.Sprint("/proc/", p.ID, "/status")); err == nil { p.Status = string(data) } if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stat")); err == nil { p.Stat = string(data) } if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stack")); err == nil { p.Stack = string(data) } if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/maps")); err == nil { p.Maps = string(data) } if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", 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) } } func (p *Process) cleanPath() { pathLen := len(p.Path) if pathLen >= 10 && p.Path[pathLen-10:] == " (deleted)" { p.Path = p.Path[:len(p.Path)-10] } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/ebpf/�������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017676�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/ebpf/cache.go�����������������������������������������������������0000664�0000000�0000000�00000004000�14401326716�0021262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "sync" "time" ) type ebpfCacheItem struct { Key []byte LastSeen int64 UID int Pid int Hits uint } type ebpfCacheType struct { Items map[string]*ebpfCacheItem sync.RWMutex } var ( maxTTL = 20 // Seconds maxCacheItems = 5000 ebpfCache *ebpfCacheType ebpfCacheTicker *time.Ticker ) // NewEbpfCacheItem creates a new cache item. func NewEbpfCacheItem(key []byte, pid, uid int) *ebpfCacheItem { return &ebpfCacheItem{ Key: key, Hits: 1, Pid: pid, UID: uid, 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[string]*ebpfCacheItem, 0), } } func (e *ebpfCacheType) addNewItem(key string, itemKey []byte, pid, uid int) { e.Lock() defer e.Unlock() e.Items[key] = NewEbpfCacheItem(itemKey, pid, uid) } func (e *ebpfCacheType) isInCache(key string) (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 string, item *ebpfCacheItem) { item.Hits++ 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.isValid() { delete(e.Items, k) } } } func (e *ebpfCacheType) clear() { if e == nil { return } for k := range e.Items { delete(e.Items, k) } if ebpfCacheTicker != nil { ebpfCacheTicker.Stop() } } opensnitch-1.5.8.1/daemon/procmon/ebpf/debug.go�����������������������������������������������������0000664�0000000�0000000�00000005205�14401326716�0021315�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) value = make([]byte, 24) } else { lookupKey = make([]byte, 36) nextKey = make([]byte, 36) value = make([]byte, 24) } 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.5.8.1/daemon/procmon/ebpf/ebpf.go������������������������������������������������������0000664�0000000�0000000�00000011676�14401326716�0021154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "encoding/binary" "fmt" "net" "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" ) //contains pointers to ebpf maps for a given protocol (tcp/udp/v6) type ebpfMapsForProto struct { counterMap *elf.Map 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 } var ( m *elf.Module lock = sync.RWMutex{} mapSize = uint(12000) ebpfMaps map[string]*ebpfMapsForProto //connections which were established at the time when opensnitch started alreadyEstablished = alreadyEstablishedConns{ TCP: make(map[*daemonNetlink.Socket]int), TCPv6: make(map[*daemonNetlink.Socket]int), } //stop == true is a signal for all goroutines to stop stop = false // list of local addresses of this machine localAddresses []net.IP hostByteOrder binary.ByteOrder ) //Start installs ebpf kprobes func Start() error { if err := mountDebugFS(); err != nil { log.Error("ebpf.Start -> mount debugfs error. Report on github please: %s", err) return err } m = elf.NewModule("/etc/opensnitchd/opensnitch.o") if err := m.Load(nil); err != nil { log.Error("eBPF Failed to load /etc/opensnitchd/opensnitch.o: %v", err) return err } // 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 { log.Error("eBPF failed to load /etc/opensnitchd/opensnitch.o (2): %v", err) return err } if err := m.EnableKprobes(0); err != nil { log.Error("eBPF error when enabling kprobes: %v", err) return err } } // init all connection counters to 0 zeroKey := make([]byte, 4) zeroValue := make([]byte, 8) for _, name := range []string{"tcpcounter", "tcpv6counter", "udpcounter", "udpv6counter"} { err := m.UpdateElement(m.Map(name), unsafe.Pointer(&zeroKey[0]), unsafe.Pointer(&zeroValue[0]), 0) if err != nil { log.Error("eBPF could not init counters to zero: %v", err) return err } } ebpfCache = NewEbpfCache() 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() ebpfMaps = map[string]*ebpfMapsForProto{ "tcp": { counterMap: m.Map("tcpcounter"), bpfmap: m.Map("tcpMap")}, "tcp6": { counterMap: m.Map("tcpv6counter"), bpfmap: m.Map("tcpv6Map")}, "udp": { counterMap: m.Map("udpcounter"), bpfmap: m.Map("udpMap")}, "udp6": { counterMap: m.Map("udpv6counter"), bpfmap: m.Map("udpv6Map")}, } saveEstablishedConnections(uint8(syscall.AF_INET)) if core.IPv6Enabled { saveEstablishedConnections(uint8(syscall.AF_INET6)) } go monitorCache() go monitorMaps() go monitorLocalAddresses() go monitorAlreadyEstablished() 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 } // Stop stops monitoring connections using kprobes func Stop() { lock.Lock() stop = true lock.Unlock() if m != nil { m.Close() } ebpfCache.clear() } func isStopped() bool { lock.RLock() defer lock.RUnlock() return stop } //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 := 24 //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 } ������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/ebpf/find.go������������������������������������������������������0000664�0000000�0000000�00000013035�14401326716�0021147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "encoding/binary" "fmt" "net" "unsafe" daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" ) // we need to manually remove old connections from a bpf map // GetPid looks up process pid in a bpf map. If not found there, then it searches // already-established TCP connections. func GetPid(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (int, int, error) { if hostByteOrder == nil { return -1, -1, fmt.Errorf("eBPF monitoring method not initialized yet") } if pid, uid := getPidFromEbpf(proto, srcPort, srcIP, dstIP, dstPort); pid != -1 { return pid, uid, nil } //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 { return pid, uid, 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 -100, -100, nil } if !findAddressInLocalAddresses(srcIP) { // 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 wireshark does not show neither // resolved's TCP Fast Open packet, nor the response // Until this is better understood, we simply do not allow this machine to make connections with // arbitrary source IPs return -1, -1, fmt.Errorf("eBPF packet with unknown source IP: %s", srcIP) } return -1, -1, 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; // }__attribute__((packed));; func getPidFromEbpf(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (pid int, uid int) { if hostByteOrder == nil { return -1, -1 } // 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 var key []byte var value []byte var isIP4 bool = (proto == "tcp") || (proto == "udp") || (proto == "udplite") if isIP4 { key = make([]byte, 12) value = make([]byte, 24) copy(key[2:6], dstIP) binary.BigEndian.PutUint16(key[6:8], uint16(dstPort)) copy(key[8:12], srcIP) } else { // IPv6 key = make([]byte, 36) value = make([]byte, 24) 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 := fmt.Sprint(proto, srcPort, srcIP.String(), dstIP.String(), dstPort) cacheItem, isInCache := ebpfCache.isInCache(k) if isInCache { deleteEbpfEntry(proto, unsafe.Pointer(&key[0])) return cacheItem.Pid, cacheItem.UID } err := m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value[0])) 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[0])) 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[0])) } if err != nil { // key not found in bpf maps return -1, -1 } pid = int(hostByteOrder.Uint32(value[0:4])) uid = int(hostByteOrder.Uint32(value[8:12])) ebpfCache.addNewItem(k, key, pid, uid) if delItemIfFound { deleteEbpfEntry(proto, unsafe.Pointer(&key[0])) } return pid, uid } // 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() for _, a := range localAddresses { if addr.String() == a.String() { return true } } return false } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/ebpf/monitor.go���������������������������������������������������0000664�0000000�0000000�00000006600�14401326716�0021716�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 { if isStopped() { return } 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) } } } } func monitorCache() { for { select { case <-ebpfCacheTicker.C: if isStopped() { return } ebpfCache.DeleteOldItems() } } } // maintains a list of this machine's local addresses // TODO: use netlink.AddrSubscribeWithOptions() func monitorLocalAddresses() { for { 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) continue } lock.Lock() localAddresses = nil for _, a := range addr { localAddresses = append(localAddresses, a.IP) } lock.Unlock() time.Sleep(time.Second * 1) if isStopped() { return } } } // 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() { for { time.Sleep(time.Second * 1) if isStopped() { return } socketListTCP, err := daemonNetlink.SocketsDump(uint8(syscall.AF_INET), uint8(syscall.IPPROTO_TCP)) if err != nil { log.Debug("eBPF error in dumping TCP sockets via netlink") continue } alreadyEstablished.Lock() for aesock := range alreadyEstablished.TCP { found := false for _, sock := range socketListTCP { if 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 { log.Debug("eBPF error in dumping TCPv6 sockets via netlink: %s", err) continue } alreadyEstablished.Lock() for aesock := range alreadyEstablished.TCPv6 { found := false for _, sock := range socketListTCPv6 { if socketsAreEqual(aesock, sock) { found = true break } } if !found { delete(alreadyEstablished.TCPv6, aesock) } } alreadyEstablished.Unlock() } } } func socketsAreEqual(aSocket, bSocket *daemonNetlink.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.5.8.1/daemon/procmon/ebpf/utils.go�����������������������������������������������������0000664�0000000�0000000�00000005623�14401326716�0021373�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "fmt" "unsafe" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" ) 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 err } } return nil } func deleteEbpfEntry(proto string, key unsafe.Pointer) bool { if err := m.DeleteElement(ebpfMaps[proto].bpfmap, key); err != nil { return false } return true } func getItems(proto string, isIPv6 bool) (items uint) { isDup := make(map[string]uint8) 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, 24) firstrun := true for { ok, err := m.LookupNextElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&lookupKey[0]), unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value[0])) 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 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, 24) 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[0])) if !ok || err != nil { //reached end of map return } if counter, duped := isDup[string(lookupKey)]; duped && counter > 1 { 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.5.8.1/daemon/procmon/find.go�����������������������������������������������������������0000664�0000000�0000000�00000005262�14401326716�0020236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "os" "sort" "strconv" ) 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 := fmt.Sprint(pidsPath, pid, "/fd/") fdList := lookupPidDescriptors(fdPath, pid) if fdList == nil { return false } for idx := 0; idx < len(fdList); idx++ { descLink := fmt.Sprint(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.5.8.1/daemon/procmon/find_test.go������������������������������������������������������0000664�0000000�0000000�00000001574�14401326716�0021277�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.5.8.1/daemon/procmon/monitor/����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020451�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/monitor/init.go���������������������������������������������������0000664�0000000�0000000�00000004122�14401326716�0021742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package monitor import ( "net" "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 ) // ReconfigureMonitorMethod configures a new method for parsing connections. func ReconfigureMonitorMethod(newMonitorMethod string) error { if procmon.GetMonitorMethod() == newMonitorMethod { return nil } oldMethod := procmon.GetMonitorMethod() 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. if err := Init(); err != nil { 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() (err error) { if cacheMonitorsRunning == false { go procmon.MonitorActivePids() go procmon.CacheCleanerTask() cacheMonitorsRunning = true } if procmon.MethodIsEbpf() { err = ebpf.Start() if err == nil { log.Info("Process monitor method ebpf") return nil } // 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() log.Warning("error starting ebpf monitor method: %v", err) } else if procmon.MethodIsAudit() { var auditConn net.Conn auditConn, err = audit.Start() if err == nil { log.Info("Process monitor method audit") go audit.Reader(auditConn, (chan<- audit.Event)(audit.EventChan)) return nil } 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 err } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/parse.go����������������������������������������������������������0000664�0000000�0000000�00000007423�14401326716�0020431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "fmt" "os" "time" "github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/log" "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 } // 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 } if MethodIsAudit() { if aevent := audit.GetEventByPid(pid); aevent != nil { audit.Lock.RLock() proc := NewProcess(pid, aevent.ProcPath) proc.readCmdline() proc.setCwd(aevent.ProcDir) audit.Lock.RUnlock() // if the proc dir contains non alhpa-numeric chars the field is empty if proc.CWD == "" { proc.readCwd() } proc.readEnv() proc.cleanPath() addToActivePidsCache(uint64(pid), proc) return proc } } // 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 core.Exists(fmt.Sprint("/proc/", pid)) == false { log.Debug("PID can't be read /proc/ %d", pid) return nil } linkName := fmt.Sprint("/proc/", pid, "/exe") link, err := os.Readlink(linkName) proc := NewProcess(pid, link) proc.readCmdline() proc.readCwd() proc.readEnv() proc.cleanPath() if len(proc.Args) == 0 { proc.readComm() proc.Args = make([]string, 0) proc.Args = append(proc.Args, proc.Comm) } // If the link to the binary can't be read, the PID may be of a kernel task if err != nil || proc.Path == "" { proc.Path = "Kernel connection" } addToActivePidsCache(uint64(pid), proc) return proc } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/procmon/process.go��������������������������������������������������������0000664�0000000�0000000�00000004005�14401326716�0020766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "sync" "time" ) var ( cacheMonitorsRunning = false lock = sync.RWMutex{} monitorMethod = MethodProc ) // monitor method supported types const ( MethodProc = "proc" MethodAudit = "audit" MethodEbpf = "ebpf" ) // man 5 proc; man procfs type procIOstats struct { RChar int64 WChar int64 SyscallRead int64 SyscallWrite int64 ReadBytes int64 WriteBytes int64 } type procDescriptors struct { Name string SymLink string Size int64 ModTime time.Time } 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 { ID int Comm string Path string Args []string Env map[string]string CWD string Descriptors []*procDescriptors IOStats *procIOstats Status string Stat string Statm *procStatm Stack string Maps string } // NewProcess returns a new Process structure. func NewProcess(pid int, path string) *Process { return &Process{ ID: pid, Path: path, Args: make([]string, 0), Env: make(map[string]string), } } // 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.5.8.1/daemon/procmon/process_test.go���������������������������������������������������0000664�0000000�0000000�00000006042�14401326716�0022030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package procmon import ( "os" "testing" ) var ( myPid = os.Getpid() proc = NewProcess(myPid, "/fake/path") ) func TestNewProcess(t *testing.T) { if proc.ID != myPid { t.Error("NewProcess PID not equal to ", myPid) } if proc.Path != "/fake/path" { t.Error("NewProcess path not equal to /fake/path") } } 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) } proc.setCwd("/home") if proc.CWD != "/home" { t.Error("Proc setCwd() should be /home:", proc.CWD) } } 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.5.8.1/daemon/rule/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016254�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/loader.go������������������������������������������������������������0000664�0000000�0000000�00000024253�14401326716�0020057�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 { sync.RWMutex path string rules map[string]*Rule rulesKeys []string watcher *fsnotify.Watcher 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", path) } 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 } 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 { if err := r.Operator.Compile(); err != nil { log.Warning("Operator.Compile() error: %s: %s", err, r.Operator.Data) 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: ", err) 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) 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) } } } 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) } } 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 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 { // TODO: use List protobuf object instead of un/marshalling to/from json if err = json.Unmarshal([]byte(rule.Operator.Data), &rule.Operator.List); err != nil { return fmt.Errorf("Error loading rule of type list: %s", err) } 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 } // 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() 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, 0644); 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) } // 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.5.8.1/daemon/rule/loader_test.go�������������������������������������������������������0000664�0000000�0000000�00000016724�14401326716�0021122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "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", true, false, Allow, dur1s, dummyOper) inMemUntilRestartRule := Create("000-aaa-name", true, 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) } testNumRules(t, l, 2) if err = l.Add(inMem1sRule, false); err != nil { t.Error("Error adding temporary rule") } testNumRules(t, l, 3) // test auto deletion of temporary rule time.Sleep(time.Second * 2) testNumRules(t, l, 2) if err = l.Add(inMemUntilRestartRule, false); err != nil { t.Error("Error adding temporary rule (2)") } testNumRules(t, l, 3) 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", true, 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()") } }) } 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.5.8.1/daemon/rule/operator.go����������������������������������������������������������0000664�0000000�0000000�00000016652�14401326716�0020450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package rule import ( "fmt" "net" "reflect" "regexp" "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") OpDstIP = Operand("dest.ip") OpDstHost = Operand("dest.host") OpDstPort = Operand("dest.port") OpDstNetwork = Operand("dest.network") OpProto = Operand("protocol") 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 { Type Type `json:"type"` Operand Operand `json:"operand"` Sensitive Sensitive `json:"sensitive"` Data string `json:"data"` List []Operator `json:"list"` sync.RWMutex cb opCallback re *regexp.Regexp netMask *net.IPNet isCompiled bool lists map[string]interface{} listsMonitorRunning bool exitMonitorChan chan (bool) } // 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.Operand == OpDomainsLists { if o.Data == "" { return fmt.Errorf("Operand lists is empty, nothing to load: %s", o) } o.loadLists() o.cb = o.domainsListCmp } else if o.Operand == OpDomainsRegexpLists { if o.Data == "" { return fmt.Errorf("Operand regexp lists is empty, nothing to load: %s", o) } o.loadLists() o.cb = o.reListCmp } else if o.Operand == OpIPLists { if o.Data == "" { return fmt.Errorf("Operand ip lists is empty, nothing to load: %s", o) } o.loadLists() o.cb = o.ipListCmp } else if o.Operand == OpNetLists { if o.Data == "" { return fmt.Errorf("Operand net lists is empty, nothing to load: %s", o) } o.loadLists() o.cb = o.ipNetCmp } 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 } 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(fmt.Sprintf("%d", con.DstPort)) } else if o.Operand == OpUserID { return o.cb(fmt.Sprintf("%d", con.Entry.UserId)) } else if o.Operand == OpProcessID { return o.cb(fmt.Sprint(con.Process.ID)) } 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 == OpDstNetwork { return o.cb(con.DstIP) } else if o.Operand == OpNetLists { return o.cb(con.DstIP) } else if o.Operand == OpDomainsRegexpLists { return o.cb(con.DstHost) } else if o.Operand == OpProto { return o.cb(con.Protocol) } 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.5.8.1/daemon/rule/operator_lists.go����������������������������������������������������0000664�0000000�0000000�00000014673�14401326716�0021667�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.5.8.1/daemon/rule/operator_test.go�����������������������������������������������������0000664�0000000�0000000�00000053132�14401326716�0021501�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.5.8.1/daemon/rule/rule.go��������������������������������������������������������������0000664�0000000�0000000�00000005526�14401326716�0017562�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 { Created time.Time `json:"created"` Updated time.Time `json:"updated"` Name string `json:"name"` Enabled bool `json:"enabled"` Precedence bool `json:"precedence"` Action Action `json:"action"` Duration Duration `json:"duration"` Operator Operator `json:"operator"` } // Create creates a new rule object with the specified parameters. func Create(name string, enabled bool, precedence bool, action Action, duration Duration, op *Operator) *Rule { return &Rule{ Created: time.Now(), Enabled: enabled, Precedence: precedence, Name: name, Action: action, Duration: duration, Operator: *op, } } func (r *Rule) String() string { return fmt.Sprintf("%s: if(%s){ %s %s }", 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 } return Create( reply.Name, reply.Enabled, reply.Precedence, Action(reply.Action), Duration(reply.Duration), operator, ), nil } // Serialize translates a Rule to the protocol object func (r *Rule) Serialize() *protocol.Rule { if r == nil { return nil } return &protocol.Rule{ Name: string(r.Name), Enabled: bool(r.Enabled), Precedence: bool(r.Precedence), 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), }, } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/rule_test.go���������������������������������������������������������0000664�0000000�0000000�00000002136�14401326716�0020613�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", true, 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() } }) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020065�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/000-allow-chrome.json���������������������������������������0000664�0000000�0000000�00000000570�14401326716�0023650�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.5.8.1/daemon/rule/testdata/001-deny-chrome.json����������������������������������������0000664�0000000�0000000�00000000567�14401326716�0023500�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.5.8.1/daemon/rule/testdata/invalid-regexp-list.json������������������������������������0000664�0000000�0000000�00000001526�14401326716�0024653�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.5.8.1/daemon/rule/testdata/invalid-regexp.json�����������������������������������������0000664�0000000�0000000�00000000574�14401326716�0023704�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.5.8.1/daemon/rule/testdata/lists/������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0021223�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/lists/domains/����������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0022655�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/lists/domains/domainlists.txt�������������������������������0000664�0000000�0000000�00000000164�14401326716�0025745�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.5.8.1/daemon/rule/testdata/lists/ips/��������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0022016�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/lists/ips/ips.txt�������������������������������������������0000664�0000000�0000000�00000000227�14401326716�0023353�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.5.8.1/daemon/rule/testdata/lists/nets/�������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0022174�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/lists/nets/nets.txt�����������������������������������������0000664�0000000�0000000�00000000240�14401326716�0023702�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.5.8.1/daemon/rule/testdata/lists/regexp/�����������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0022515�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/lists/regexp/domainsregexp.txt������������������������������0000664�0000000�0000000�00000000132�14401326716�0026117�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.5.8.1/daemon/rule/testdata/live_reload/������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0022352�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/rule/testdata/live_reload/test-live-reload-delete.json��������������������0000664�0000000�0000000�00000000620�14401326716�0027663�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.5.8.1/daemon/rule/testdata/live_reload/test-live-reload-remove.json��������������������0000664�0000000�0000000�00000000620�14401326716�0027716�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.5.8.1/daemon/statistics/���������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017477�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/statistics/event.go�������������������������������������������������������0000664�0000000�0000000�00000001251�14401326716�0021146�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.5.8.1/daemon/statistics/stats.go�������������������������������������������������������0000664�0000000�0000000�00000013262�14401326716�0021170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package statistics import ( "fmt" "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/rule" "github.com/evilsocket/opensnitch/daemon/ui/protocol" ) // StatsConfig holds the stats confguration type StatsConfig struct { MaxEvents int `json:"MaxEvents"` MaxStats int `json:"MaxStats"` } 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 { sync.RWMutex Started time.Time DNSResponses int Connections int Ignored int Accepted int Dropped int RuleHits int RuleMisses int Events []*Event ByProto map[string]uint64 ByAddress map[string]uint64 ByHost map[string]uint64 ByPort map[string]uint64 ByUID map[string]uint64 ByExecutable map[string]uint64 rules *rule.Loader jobs chan conEvent // max number of events to keep in the buffer maxEvents int // max number of entries for each By* map maxStats int } // 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, } go stats.eventWorker(0) go stats.eventWorker(1) go stats.eventWorker(2) go stats.eventWorker(3) return stats } // SetConfig 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) SetConfig(config StatsConfig) { if config.MaxEvents > 0 { s.maxEvents = config.MaxEvents } if config.MaxStats > 0 { s.maxStats = config.MaxStats } } // 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, fmt.Sprintf("%d", con.DstPort)) s.incMap(&s.ByUID, fmt.Sprintf("%d", 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)) } // 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, } } 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.5.8.1/daemon/system-fw.json������������������������������������������������������������0000664�0000000�0000000�00000000476�14401326716�0020145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "SystemRules": [ { "Rule": { "Description": "Allow icmp", "Table": "mangle", "Chain": "OUTPUT", "Parameters": "-p icmp", "Target": "ACCEPT", "TargetParameters": "" } } ] } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/ui/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015722�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/ui/client.go��������������������������������������������������������������0000664�0000000�0000000�00000021055�14401326716�0017532�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/rule" "github.com/evilsocket/opensnitch/daemon/statistics" "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, 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, rule.Deny, rule.Once, dummyOperator) clientErrorRule = rule.Create("ui.client.error", true, false, rule.Allow, rule.Once, dummyOperator) config Config ) type serverConfig struct { Address string `json:"Address"` LogFile string `json:"LogFile"` } // Config holds the values loaded from configFile type Config struct { sync.RWMutex Server serverConfig `json:"Server"` DefaultAction string `json:"DefaultAction"` DefaultDuration string `json:"DefaultDuration"` InterceptUnknown bool `json:"InterceptUnknown"` ProcMonitorMethod string `json:"ProcMonitorMethod"` LogLevel *uint32 `json:"LogLevel"` Firewall string `json:"Firewall"` Stats statistics.StatsConfig `json:"Stats"` } // Client holds the connection information of a client. type Client struct { sync.RWMutex clientCtx context.Context clientCancel context.CancelFunc stats *statistics.Statistics rules *rule.Loader socketPath string isUnixSocket bool con *grpc.ClientConn client protocol.UIClient configWatcher *fsnotify.Watcher streamNotifications protocol.UI_NotificationsClient //isAsking is set to true if the client is awaiting a decision from the GUI isAsking bool } // NewClient creates and configures a new client. func NewClient(socketPath string, stats *statistics.Statistics, rules *rule.Loader) *Client { c := &Client{ stats: stats, rules: rules, isUnixSocket: false, isAsking: false, } 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)) } go c.poller() return c } // 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 { config.RLock() defer config.RUnlock() return config.ProcMonitorMethod } // InterceptUnknown returns func (c *Client) InterceptUnknown() bool { config.RLock() defer config.RUnlock() return config.InterceptUnknown } // GetStatsConfig returns the stats config from disk func (c *Client) GetStatsConfig() statistics.StatsConfig { config.RLock() defer config.RUnlock() return config.Stats } // GetFirewallType returns the firewall to use func (c *Client) GetFirewallType() string { config.RLock() defer config.RUnlock() if config.Firewall == "" { return iptables.Name } return config.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() } 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 { 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() if c.isUnixSocket { c.con, err = grpc.Dial(c.socketPath, grpc.WithInsecure(), grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { return net.DialTimeout("unix", 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, grpc.WithInsecure(), grpc.WithKeepaliveParams(kacp)) } return err } func (c *Client) disconnect() { c.Lock() defer c.Unlock() c.client = nil if c.con != nil { c.con.Close() c.con = nil log.Debug("client.disconnect()") } } 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 } 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.5.8.1/daemon/ui/config.go��������������������������������������������������������������0000664�0000000�0000000�00000005617�14401326716�0017527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ui import ( "encoding/json" "fmt" "io/ioutil" "strings" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/procmon/monitor" "github.com/evilsocket/opensnitch/daemon/rule" ) func (c *Client) getSocketPath(socketPath string) string { c.Lock() defer c.Unlock() if strings.HasPrefix(socketPath, "unix://") == true { c.isUnixSocket = true return socketPath[7:] } 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 { config.RLock() defer config.RUnlock() return newMonitorMethod == config.ProcMonitorMethod } func (c *Client) parseConf(rawConfig string) (conf Config, err error) { err = json.Unmarshal([]byte(rawConfig), &conf) return conf, err } func (c *Client) loadDiskConfiguration(reload bool) { raw, err := ioutil.ReadFile(configFile) if err != nil { fmt.Errorf("Error loading disk configuration %s: %s", configFile, err) } 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 { config.Lock() defer config.Unlock() if err := json.Unmarshal(rawConfig, &config); err != nil { log.Error("Error parsing configuration %s: %s", configFile, err) return false } // firstly load config level, to detect further errors if any if config.LogLevel != nil { log.SetLogLevel(int(*config.LogLevel)) } if config.Server.LogFile != "" { log.Close() log.OpenFile(config.Server.LogFile) } if config.Server.Address != "" { tempSocketPath := c.getSocketPath(config.Server.Address) if tempSocketPath != c.socketPath { // disconnect, and let the connection poller reconnect to the new address c.disconnect() } c.setSocketPath(tempSocketPath) } if config.DefaultAction != "" { clientDisconnectedRule.Action = rule.Action(config.DefaultAction) clientErrorRule.Action = rule.Action(config.DefaultAction) } if config.DefaultDuration != "" { clientDisconnectedRule.Duration = rule.Duration(config.DefaultDuration) clientErrorRule.Duration = rule.Duration(config.DefaultDuration) } if config.ProcMonitorMethod != "" { if err := monitor.ReconfigureMonitorMethod(config.ProcMonitorMethod); err != nil { log.Warning("Unable to set new process monitor method from disk: %v", err) } } return true } func (c *Client) saveConfiguration(rawConfig string) (err error) { if c.loadConfiguration([]byte(rawConfig)) != true { return fmt.Errorf("Error parsing configuration %s: %s", rawConfig, 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.5.8.1/daemon/ui/notifications.go�������������������������������������������������������0000664�0000000�0000000�00000023224�14401326716�0021125�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/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++ } 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, } } func (c *Client) monitorProcessDetails(pid int, stream protocol.UI_NotificationsClient, notification *protocol.Notification) { p := procmon.NewProcess(pid, "") ticker := time.NewTicker(2 * time.Second) for { select { case _pid := <-stopMonitoringProcess: if _pid != pid { continue } goto Exit case <-ticker.C: if err := p.GetInfo(); 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 := c.parseConf(notification.Data) if err != nil { log.Warning("[notification] error parsing received config: %v", notification.Data) c.sendNotificationReply(stream, notification.Id, "", err) return } if err := monitor.ReconfigureMonitorMethod(newConf.ProcMonitorMethod); err != nil { c.sendNotificationReply(stream, notification.Id, "", err) return } // this save operation triggers a re-loadConfiguration() err = c.saveConfiguration(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) 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_LOAD_FIREWALL: log.Info("[notification] starting firewall") firewall.Init(c.GetFirewallType(), nil) c.sendNotificationReply(stream, notification.Id, "", nil) case notification.Type == protocol.Action_UNLOAD_FIREWALL: log.Info("[notification] stopping firewall") firewall.Stop() c.sendNotificationReply(stream, notification.Id, "", nil) // 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 := c.parseConf(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.5.8.1/daemon/ui/protocol/��������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017563�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/daemon/ui/protocol/.gitkeep������������������������������������������������������0000664�0000000�0000000�00000000000�14401326716�0021202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015264�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/changelog�����������������������������������������������������������������0000664�0000000�0000000�00000015350�14401326716�0017142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch (1.5.5-1) unstable; urgency=medium * New upstream release. * Bump Standards-Version to 4.6.2. * Upload sponsored by Petter Reinholdtsen. -- Gustavo Iñiguez Goya <gustavo.iniguez.goya@gmail.com> Wed, 01 Feb 2023 22:37:12 +0100 opensnitch (1.5.4-1) unstable; urgency=high * New upstream release. (Closes: #1030115) * debian/control: - Updated packages description. - Removed debconf and whiptail|dialog dependencies. - Added xdg-user-dirs, gtk-update-icon-cache dependencies. - Point Vcs-Git field to the 1.5.0 branch. * debian/postinst: - Fixed opensnitch_ui.desktop installation. - Fixed updating icons cache. * debian/postrm: - Fixed removing opensnitch_ui.desktop * debian/tests/: - Added autopkgtests. * Upload sponsored by Petter Reinholdtsen. -- Gustavo Iñiguez Goya <gustavo.iniguez.goya@gmail.com> Tue, 31 Jan 2023 23:48:58 +0100 opensnitch (1.5.3-1) unstable; urgency=medium * Added debian/upstream/metadata. * Updated Homepage url. * Updated Copyright years. -- Gustavo-Iniguez-Goya <gustavo.iniguez.goya@gmail.com> Sun, 22 Jan 2023 21:30:45 +0100 opensnitch (1.5.2.1-1) unstable; urgency=medium * Initial release. (Closes: #909567) -- Gustavo-Iniguez-Goya <gustavo.iniguez.goya@gmail.com> Fri, 20 Jan 2023 22:26:40 +0000 opensnitch (1.5.2-1) unstable; urgency=medium * try to mount debugfs on boot up -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Wed, 27 Jul 2022 17:29:33 +0200 opensnitch (1.5.1-1) unstable; urgency=medium * Better eBPF cache. * Fixed error resolving domains to localhost. * Fixed error deleting our nftables rules. -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 25 Feb 2022 01:21:38 +0100 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.5.8.1/debian/control�������������������������������������������������������������������0000664�0000000�0000000�00000005343�14401326716�0016674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Source: opensnitch Maintainer: Gustavo Iñiguez Goya <gustavo.iniguez.goya@gmail.com> Section: devel Testsuite: autopkgtest-pkg-go Priority: optional Build-Depends: debhelper-compat (= 11), dh-golang, dh-python, golang-any, golang-github-evilsocket-ftrace-dev, golang-github-fsnotify-fsnotify-dev, golang-github-google-gopacket-dev, golang-github-google-nftables-dev, golang-github-iovisor-gobpf-dev, golang-github-vishvananda-netlink-dev, golang-golang-x-net-dev, golang-google-grpc-dev, golang-goprotobuf-dev, libmnl-dev, libnetfilter-queue-dev, pkg-config, protoc-gen-go-grpc, pyqt5-dev-tools, qttools5-dev-tools, python3-all, python3-grpc-tools, python3-setuptools Standards-Version: 4.6.2 Vcs-Browser: https://github.com/evilsocket/opensnitch Vcs-Git: https://github.com/evilsocket/opensnitch.git -b 1.5.0 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: ${misc:Depends}, ${shlibs:Depends}, Recommends: python3-opensnitch-ui 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. Package: python3-opensnitch-ui Architecture: all Section: net Depends: ${misc:Depends}, ${shlibs:Depends}, libqt5sql5-sqlite, python3-grpcio, python3-notify2, python3-pyinotify, python3-pyqt5, python3-pyqt5.qtsql, python3-setuptools, python3-six, python3-slugify, python3:any, xdg-user-dirs, gtk-update-icon-cache Recommends: python3-pyasn Suggests: opensnitch Description: GNU/Linux interactive application firewall GUI 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.5.8.1/debian/copyright�����������������������������������������������������������������0000664�0000000�0000000�00000002246�14401326716�0017223�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-Contact: Gustavo Iñiguez Goia <gooffy1@gmail.com> Upstream-Name: opensnitch Files-Excluded: Godeps/_workspace Files: * Copyright: 2017-2018 evilsocket 2019-2023 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.5.8.1/debian/gbp.conf������������������������������������������������������������������0000664�0000000�0000000�00000000036�14401326716�0016702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[DEFAULT] pristine-tar = True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/gitlab-ci.yml�������������������������������������������������������������0000664�0000000�0000000�00000002525�14401326716�0017646�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.5.8.1/debian/opensnitch.init�����������������������������������������������������������0000664�0000000�0000000�00000003562�14401326716�0020331�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.5.8.1/debian/opensnitch.install��������������������������������������������������������0000664�0000000�0000000�00000000174�14401326716�0021030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������daemon/default-config.json etc/opensnitchd/ daemon/system-fw.json etc/opensnitchd/ #ebpf_prog/opensnitch.o etc/opensnitchd/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/opensnitch.logrotate������������������������������������������������������0000664�0000000�0000000�00000000353�14401326716�0021361�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.5.8.1/debian/opensnitch.service��������������������������������������������������������0000664�0000000�0000000�00000000633�14401326716�0021022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Unit] Description=OpenSnitch is a GNU/Linux application firewall. 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 [Install] WantedBy=multi-user.target �����������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/python3-opensnitch-ui.postinst��������������������������������������������0000775�0000000�0000000�00000000646�14401326716�0023271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh set -e autostart_by_default() { if [ -f /etc/xdg/autostart -a ! -f /etc/xdg/autostart/opensnitch_ui.desktop ]; 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 #DEBHELPER# ������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/python3-opensnitch-ui.postrm����������������������������������������������0000775�0000000�0000000�00000000402�14401326716�0022720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh set -e case "$1" in purge) if [ -f /etc/xdg/autostart/opensnitch_ui.desktop ];then rm -f /etc/xdg/autostart/opensnitch_ui.desktop fi ;; remove) pkill -15 opensnitch-ui || true ;; esac #DEBHELPER# ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/rules���������������������������������������������������������������������0000775�0000000�0000000�00000002475�14401326716�0016354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/make -f export DH_VERBOSE = 1 export DESTDIR := $(shell pwd)/debian/opensnitch export UIDESTDIR := $(shell pwd)/debian/python3-opensnitch-ui override_dh_installsystemd: dh_installsystemd --restart-after-upgrade override_dh_auto_build: $(MAKE) protocol # Workaround for Go build problem when building in _build mkdir -p _build/src/github.com/evilsocket/opensnitch/daemon/ui/protocol/ cp daemon/ui/protocol/* _build/src/github.com/evilsocket/opensnitch/daemon/ui/protocol/ dh_auto_build cd ui && python3 setup.py build --force override_dh_auto_install: # daemon mkdir -p $(DESTDIR)/usr/bin cp _build/bin/daemon $(DESTDIR)/usr/bin/opensnitchd # GUI make -C ui/i18n cp -r ui/i18n/locales/ ui/opensnitch/i18n/ pyrcc5 -o ui/opensnitch/resources_rc.py ui/opensnitch/res/resources.qrc sed -i 's/^import ui_pb2/from . import ui_pb2/' ui/opensnitch/ui_pb2* cd ui && python3 setup.py install --force --root=$(UIDESTDIR) --no-compile -O0 --install-layout=deb # daemon dh_auto_install %: dh $@ --builddirectory=_build --buildsystem=golang --with=golang,python3 override_dh_auto_clean: dh_auto_clean $(MAKE) clean $(RM) ui/opensnitch/resources_rc.py $(RM) -r ui/opensnitch/i18n/ $(RM) ui/i18n/locales/*/*.qm cd ui && python3 setup.py clean -a $(RM) -r ui/opensnitch_ui.egg-info/ find ui -name \*.pyc -exec rm {} \; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/source/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016564�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/source/format�������������������������������������������������������������0000664�0000000�0000000�00000000014�14401326716�0017772�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������3.0 (quilt) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/source/options������������������������������������������������������������0000664�0000000�0000000�00000000040�14401326716�0020174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������extend-diff-ignore="\.egg-info$"������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/tests/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016426�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/tests/control�������������������������������������������������������������0000664�0000000�0000000�00000000055�14401326716�0020031�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Tests: test-resources.sh Depends: opensnitch �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/tests/test-resources.sh���������������������������������������������������0000775�0000000�0000000�00000000536�14401326716�0021760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh set -e ophome="/etc/opensnitchd" ls -dl $ophome 1>/dev/null echo "installed OK: $ophome" ls -l $ophome/system-fw.json 1>/dev/null echo "installed OK: $ophome/system-fw.json" ls -l $ophome/default-config.json 1>/dev/null echo "installed OK: $ophome/default-config.json" ls -dl $ophome/rules 1>/dev/null echo "installed OK: $ophome/rules/" ������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/upstream/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017124�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/upstream/metadata���������������������������������������������������������0000664�0000000�0000000�00000000632�14401326716�0020630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- Name: opensnitch Bug-Database: https://github.com/evilsocket/opensnitch/issues Bug-Submit: https://github.com/evilsocket/opensnitch/issues/new Contact: Gustavo Iñiguez Goia <gooffy1@gmail.com> Documentation: https://github.com/evilsocket/opensnitch/wiki CPE: cpe:/a:evilsocket:opensnitch Repository: https://github.com/evilsocket/opensnitch.git Repository-Browse: https://github.com/evilsocket/opensnitch ������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/debian/watch���������������������������������������������������������������������0000664�0000000�0000000�00000000352�14401326716�0016315�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.5.8.1/ebpf_prog/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016005�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ebpf_prog/Makefile���������������������������������������������������������������0000664�0000000�0000000�00000012353�14401326716�0017451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#taken from /samples/bpf/Makefile and removed all targets # SPDX-License-Identifier: GPL-2.0 BPF_SAMPLES_PATH ?= $(abspath $(srctree)/$(src)) TOOLS_PATH := $(BPF_SAMPLES_PATH)/../../tools # Libbpf dependencies LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o TRACE_HELPERS := ../../tools/testing/selftests/bpf/trace_helpers.o always-y += opensnitch.o ifeq ($(ARCH), arm) # Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux # headers when arm instruction set identification is requested. ARM_ARCH_SELECTOR := $(filter -D__LINUX_ARM_ARCH__%, $(KBUILD_CFLAGS)) BPF_EXTRA_CFLAGS := $(ARM_ARCH_SELECTOR) TPROGS_CFLAGS += $(ARM_ARCH_SELECTOR) endif TPROGS_CFLAGS += -Wall -O2 TPROGS_CFLAGS += -Wmissing-prototypes TPROGS_CFLAGS += -Wstrict-prototypes TPROGS_CFLAGS += -I$(objtree)/usr/include TPROGS_CFLAGS += -I$(srctree)/tools/testing/selftests/bpf/ TPROGS_CFLAGS += -I$(srctree)/tools/lib/ TPROGS_CFLAGS += -I$(srctree)/tools/include TPROGS_CFLAGS += -I$(srctree)/tools/perf TPROGS_CFLAGS += -DHAVE_ATTR_TEST=0 ifdef SYSROOT TPROGS_CFLAGS += --sysroot=$(SYSROOT) TPROGS_LDFLAGS := -L$(SYSROOT)/usr/lib endif TPROGCFLAGS_bpf_load.o += -Wno-unused-variable TPROGS_LDLIBS += $(LIBBPF) -lelf -lz TPROGLDLIBS_tracex4 += -lrt TPROGLDLIBS_trace_output += -lrt TPROGLDLIBS_map_perf_test += -lrt TPROGLDLIBS_test_overhead += -lrt TPROGLDLIBS_xdpsock += -pthread # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make M=samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang LLC ?= llc CLANG ?= clang LLVM_OBJCOPY ?= llvm-objcopy BTF_PAHOLE ?= pahole # Detect that we're cross compiling and use the cross compiler ifdef CROSS_COMPILE CLANG_ARCH_ARGS = --target=$(notdir $(CROSS_COMPILE:%-=%)) endif # Don't evaluate probes and warnings if we need to run make recursively ifneq ($(src),) HDR_PROBE := $(shell printf "\#include <linux/types.h>\n struct list_head { int a; }; int main() { return 0; }" | \ $(CC) $(TPROGS_CFLAGS) $(TPROGS_LDFLAGS) -x c - \ -o /dev/null 2>/dev/null && echo okay) ifeq ($(HDR_PROBE),) $(warning WARNING: Detected possible issues with include path.) $(warning WARNING: Please install kernel headers locally (make headers_install).) endif BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris) BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF) BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm') BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \ $(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \ readelf -S ./llvm_btf_verify.o | grep BTF; \ /bin/rm -f ./llvm_btf_verify.o) BPF_EXTRA_CFLAGS += -fno-stack-protector ifneq ($(BTF_LLVM_PROBE),) BPF_EXTRA_CFLAGS += -g else ifneq ($(and $(BTF_LLC_PROBE),$(BTF_PAHOLE_PROBE),$(BTF_OBJCOPY_PROBE)),) BPF_EXTRA_CFLAGS += -g LLC_FLAGS += -mattr=dwarfris DWARF2BTF = y endif endif endif # Trick to allow make to be run from this directory all: $(MAKE) -C ../../ M=$(CURDIR) BPF_SAMPLES_PATH=$(CURDIR) clean: $(MAKE) -C ../../ M=$(CURDIR) clean @find $(CURDIR) -type f -name '*~' -delete $(LIBBPF): FORCE # Fix up variables inherited from Kbuild that tools/ build system won't like $(MAKE) -C $(dir $@) RM='rm -rf' EXTRA_CFLAGS="$(TPROGS_CFLAGS)" \ LDFLAGS=$(TPROGS_LDFLAGS) srctree=$(BPF_SAMPLES_PATH)/../../ O= $(obj)/syscall_nrs.h: $(obj)/syscall_nrs.s FORCE $(call filechk,offsets,__SYSCALL_NRS_H__) targets += syscall_nrs.s clean-files += syscall_nrs.h FORCE: # Verify LLVM compiler tools are available and bpf target is supported by llc .PHONY: verify_cmds verify_target_bpf $(CLANG) $(LLC) verify_cmds: $(CLANG) $(LLC) @for TOOL in $^ ; do \ if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \ echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\ exit 1; \ else true; fi; \ done verify_target_bpf: verify_cmds @if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \ echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\ echo " NOTICE: LLVM version >= 3.7.1 required" ;\ exit 2; \ else true; fi $(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF) $(src)/*.c: verify_target_bpf $(LIBBPF) $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h $(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h $(obj)/hbm.o: $(src)/hbm.h $(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h -include $(BPF_SAMPLES_PATH)/Makefile.target # asm/sysreg.h - inline assembly used by it is incompatible with llvm. # But, there is no easy way to fix it, so just exclude it since it is # useless for BPF samples. $(obj)/%.o: $(src)/%.c @echo " CLANG-bpf " $@ $(Q)$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(BPF_EXTRA_CFLAGS) \ -I$(obj) -I$(srctree)/tools/testing/selftests/bpf/ \ -I$(srctree)/tools/lib/ \ -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \ -D__TARGET_ARCH_$(SRCARCH) -Wno-compare-distinct-pointer-types \ -Wno-gnu-variable-sized-type-not-at-end \ -Wno-address-of-packed-member -Wno-tautological-compare \ -Wno-unknown-warning-option $(CLANG_ARCH_ARGS) \ -I$(srctree)/samples/bpf/ -include asm_goto_workaround.h \ -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf $(LLC_FLAGS) -filetype=obj -o $@ ifeq ($(DWARF2BTF),y) $(BTF_PAHOLE) -J $@ endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ebpf_prog/README�����������������������������������������������������������������0000664�0000000�0000000�00000002122�14401326716�0016662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch.c is an eBPF program. Compilation requires getting kernel source. 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 patch linux-5.8/tools/lib/bpf/bpf_helpers.h < ebpf_prog/file.patch cp ebpf_prog/opensnitch.c ebpf_prog/Makefile linux-5.8/samples/bpf cd linux-5.8 && yes "" | make oldconfig && make prepare && make headers_install # (1 min) cd samples/bpf && make objdump -h opensnitch.o #you should see many section, number 1 should be called kprobe/tcp_v4_connect llvm-strip -g opensnitch.o #remove debug info sudo cp opensnitch.o /etc/opensnitchd/ cd ../../../daemon --opensnitchd expects to find opensnitch.o in /etc/opensnitchd/ --start opensnitchd with: opensnitchd -rules-path /etc/opensnitchd/rules -process-monitor-method ebpf 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 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ebpf_prog/arm-clang-asm-fix.patch������������������������������������������������0000664�0000000�0000000�00000000601�14401326716�0022226�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.5.8.1/ebpf_prog/file.patch�������������������������������������������������������������0000664�0000000�0000000�00000000616�14401326716�0017750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- linux-5.8/tools/lib/bpf/bpf_helpers.h 2020-08-03 00:21:45.000000000 +0300 +++ linux-5.8/tools/lib/bpf/bpf_helpersnew.h 2021-02-23 18:45:21.789624834 +0300 @@ -54,7 +54,7 @@ * Helper structure used by eBPF C program * to describe BPF map attributes to libbpf loader */ -struct bpf_map_def { +struct bpf_map_defold { unsigned int type; unsigned int key_size; unsigned int value_size; ������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ebpf_prog/opensnitch.c�����������������������������������������������������������0000664�0000000�0000000�00000036010�14401326716�0020323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define KBUILD_MODNAME "dummy" //uncomment if building on x86_32 //#define OPENSNITCH_x86_32 #include <linux/sched.h> #include <linux/ptrace.h> #include <linux/version.h> #include <uapi/linux/bpf.h> #include <uapi/linux/tcp.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> #include <net/sock.h> #include <net/udp_tunnel.h> #include <net/inet_sock.h> #define MAPSIZE 12000 //-------------------------------map definitions // which github.com/iovisor/gobpf/elf expects #define BUF_SIZE_MAP_NS 256 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, }; //----------------------------------- // 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; 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; u64 counter; }__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; u64 counter; }__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; u64 counter; }__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; u64 counter; }__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), .value_size = sizeof(u64),// using u64 instead of sizeof(struct sock *) // to avoid pointer size related quirks on x86_32 .max_entries = 100, }; struct bpf_map_def SEC("maps/tcpv6sock") tcpv6sock = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(u64), .value_size = sizeof(u64), .max_entries = 100, }; // //counts how many connections we've processed. Starts at 0. struct bpf_map_def SEC("maps/tcpcounter") tcpcounter = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u64), .max_entries = 1, }; struct bpf_map_def SEC("maps/tcpv6counter") tcpv6counter = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u64), .max_entries = 1, }; struct bpf_map_def SEC("maps/udpcounter") udpcounter = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u64), .max_entries = 1, }; struct bpf_map_def SEC("maps/udpv6counter") udpv6counter = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u64), .max_entries = 1, }; struct bpf_map_def SEC("maps/debugcounter") debugcounter = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u64), .max_entries = 1, }; // size 150 gave ebpf verifier errors for kernel 4.14, 100 is ok // we can cast any struct into rawBytes_t to be able to access arbitrary bytes of the struct struct rawBytes_t { u8 bytes[100]; }; //used for debug purposes only struct bpf_map_def SEC("maps/bytes") bytes = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(u32), .value_size = sizeof(u32), .max_entries = 222, }; //used for debug purposes only struct bpf_map_def SEC("maps/debug") debug = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(struct tcpv6_key_t), .value_size = sizeof(struct rawBytes_t), .max_entries = 555, }; // 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) { #ifdef OPENSNITCH_x86_32 // 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); u32 zero_key = 0; u64 *val = bpf_map_lookup_elem(&tcpcounter, &zero_key); if (val == NULL){return 0;} struct tcp_value_t tcp_value; __builtin_memset(&tcp_value, 0, sizeof(tcp_value)); tcp_value.pid = pid_tgid >> 32; tcp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; tcp_value.counter = *val; bpf_map_update_elem(&tcpMap, &tcp_key, &tcp_value, BPF_ANY); u64 newval = *val + 1; bpf_map_update_elem(&tcpcounter, &zero_key, &newval, 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) { #ifdef OPENSNITCH_x86_32 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); #ifdef OPENSNITCH_x86_32 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 u32 zero_key = 0; u64 *val = bpf_map_lookup_elem(&tcpv6counter, &zero_key); if (val == NULL){return 0;} struct tcpv6_value_t tcpv6_value; __builtin_memset(&tcpv6_value, 0, sizeof(tcpv6_value)); tcpv6_value.pid = pid_tgid >> 32; tcpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff; tcpv6_value.counter = *val; bpf_map_update_elem(&tcpv6Map, &tcpv6_key, &tcpv6_value, BPF_ANY); u64 newval = *val + 1; bpf_map_update_elem(&tcpv6counter, &zero_key, &newval, BPF_ANY); bpf_map_delete_elem(&tcpv6sock, &pid_tgid); return 0; }; SEC("kprobe/udp_sendmsg") int kprobe__udp_sendmsg(struct pt_regs *ctx) { #ifdef OPENSNITCH_x86_32 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); u32 zero_key = 0; __builtin_memset(&zero_key, 0, sizeof(zero_key)); u64 *counterVal = bpf_map_lookup_elem(&udpcounter, &zero_key); if (counterVal == NULL){return 0;} 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; __builtin_memset(&udp_value, 0, sizeof(udp_value)); udp_value.pid = pid; udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; udp_value.counter = *counterVal; bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); u64 newval = *counterVal + 1; bpf_map_update_elem(&udpcounter, &zero_key, &newval, BPF_ANY); } //else nothing to do return 0; }; SEC("kprobe/udpv6_sendmsg") int kprobe__udpv6_sendmsg(struct pt_regs *ctx) { #ifdef OPENSNITCH_x86_32 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); #ifdef OPENSNITCH_x86_32 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 u32 zero_key = 0; u64 *counterVal = bpf_map_lookup_elem(&udpv6counter, &zero_key); if (counterVal == NULL){return 0;} 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; __builtin_memset(&udpv6_value, 0, sizeof(udpv6_value)); udpv6_value.pid = pid; udpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff; udpv6_value.counter = *counterVal; bpf_map_update_elem(&udpv6Map, &udpv6_key, &udpv6_value, BPF_ANY); u64 newval = *counterVal + 1; bpf_map_update_elem(&udpv6counter, &zero_key, &newval, BPF_ANY); } //else nothing to do return 0; }; SEC("kprobe/iptunnel_xmit") int kprobe__iptunnel_xmit(struct pt_regs *ctx) { #ifdef OPENSNITCH_x86_32 // TODO return 0; #else 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); #endif 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; u32 zero_key = 0; __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); u64 *counterVal = bpf_map_lookup_elem(&udpcounter, &zero_key); if (counterVal == NULL){return 0;} 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) { udp_value.pid = pid; udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; udp_value.counter = *counterVal; bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); u64 newval = *counterVal + 1; bpf_map_update_elem(&udpcounter, &zero_key, &newval, BPF_ANY); } return 0; }; // debug only: increment key's value by 1 in map "bytes" void increment(u32 key){ u32 *lookedupValue = bpf_map_lookup_elem(&bytes, &key); if (lookedupValue == NULL){ u32 zero = 0; bpf_map_update_elem(&bytes, &key, &zero, BPF_ANY); } else { u32 newval = *lookedupValue + 1; bpf_map_update_elem(&bytes, &key, &newval, BPF_ANY); } } 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.5.8.1/proto/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015205�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/proto/.gitignore�����������������������������������������������������������������0000664�0000000�0000000�00000000006�14401326716�0017171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.pyc ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/proto/Makefile�������������������������������������������������������������������0000664�0000000�0000000�00000001105�14401326716�0016642�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.5.8.1/proto/ui.proto�������������������������������������������������������������������0000664�0000000�0000000�00000005355�14401326716�0016717�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) {} } 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 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; } message Rule { string name = 1; bool enabled = 2; bool precedence = 3; string action = 4; string duration = 5; Operator operator = 6; } enum Action { NONE = 0; LOAD_FIREWALL = 1; UNLOAD_FIREWALL = 2; CHANGE_CONFIG = 3; ENABLE_RULE = 4; DISABLE_RULE = 5; DELETE_RULE = 6; CHANGE_RULE = 7; LOG_LEVEL = 8; STOP = 9; MONITOR_PROCESS = 10; STOP_MONITOR_PROCESS = 11; } // 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; } // 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; } // 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.5.8.1/release.sh�����������������������������������������������������������������������0000775�0000000�0000000�00000001243�14401326716�0016021�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.5.8.1/screenshots/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016402�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/screenshots/opensnitch-ui-general-tab-deny.png�����������������������������������0000664�0000000�0000000�00000325323�14401326716�0025021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR����G���8���sBIT|d���tEXtSoftware�mate-screenshotȖJ�� �IDATxyXTe þ *+ZjKkefV˲̲E}\3-r/5rBPPPP]9?hN (ܟ 8˳3s9g43j(%2!B!>[g^SׯOƍqtt|%B!BQfRSS VΞ=ܹs5�  {P!B!DJNNfʕ̝;W_B!Bruu^z�X5J޽;666\,!B!;?!B!Ŀ` ++l jj`ggVqqq<F!B!VAff&FQ]AhDד-h4⒴$�B!B<E!--lEB TP'''t:z4nܸABBD999*Hz�P!BR۷o5jA]o4j₷7.\ ''t*Hz+ɪB!BQƲAQn]0ESVVVz%<:�TRSSy&7o$55uB򠐝KlR6i)c+GQz=zիh0 w|iZW^W")ztU�111舵&)KH}2Woat=<ٕFgZNʶ?ùz#,3Л>M+񊝒|PM]{UZ!;:;t%?qrbޞ܋*e$wYnġsL u{=O룞<$cᆲen %8VO<%Xi!h4b0pqqV}8JIBff&ΡÒߢEQڵSݻw/ 6A)F�333qrr~Fqrr"33nBr a<ss7ࣙx:c2'ks!A??[}dӪķ+>~xj@If 1KP~9f6tWLe$Ǫ)b~! C777kի8pl999镑NFFF*:uh"v؁``jPx/A)F�Gכ-"<<ggMR!I.ƣx1 [e~-HcaVg˱[Ӎ7~ }&ؕj}m1}DOY hВ±uhUj?|IؖWBQrrr 7h:vVb„ x{{̙3Y&͚5Sbkk^`00gBCC={6:VVVf۔U~mڴښsrOFA۶mȸJPwP_&&JMަٞZ'jBsYw$OҨEߍ[BRq i<@Ia_h4NTז;SYT 0]k`BޠuFgwHpo㠮-{<?Nz*tf ȎĖ_v2Tk*ҪP:6 B\!UW1su hx2gͶyO5#>N)O%W֩m~N7խ _Ʃ3(釘?iU:SAMv"VWGұq[^`Q6$էG5>qn<߷xtԩ> 9[vr<<YXP}>ڢ T&~~7R18T۷)}^lMQyPXxÞ$Vk]x&ƍ㫯O>^`2vX4 FRQ ,X@tt4SLaΜ9̙3#GM,i޼9:ubǎk׎֭[fjkkKllzB!"AW=>85iּ:ZEOXr?su);9_2V7صd.e>$T-p_v[1#dyJ|pH%l 7ZWgdO9v1xEaΎ-:RSuNq� ΟɷN`]u}MD*x?/7i[E<eLcCO՝op>EU[|Rp_ BJ&Y9Dq)GKFxj,JS p$5 kMQz\IpW4xzEż_IuyMOP(*AĹXyѸQ 9߲%QϜ>r#,Ixvu*+yŝE_) VEѐm61991cȜ9sVo&fegghjK^o|G… U~׶mعs';vd߾}l޼6wbΎTΝ;GJ�~:2SdX98R(-N.4R^})d=ʀWWT5tJ'?9rxgԲ?eں(BnĠ0,̉92'v9C ?Ea;L]T]z'#v-<xsL'*坽wБk~1XJW5Aߌ3 C4l}ߊnh_DYMHҺV"%2Tgw\ӓtp%<tm4!b52=!zg{>f4? #dlb6tcUtўWOҙ0;/z?I6 7IRwlE?Aj�}dQŤ/Vʊ۷od.))3{lƌCbbbaj%XT~mڴaذat:n߾N?$<<EQ|*?0bڵkG ;w.жmRJ�t:ʕ+@s<#=(dp[Bpvt@C V�7,qDǓm˓UyͶFgW%5BAc`^y 7.?x87.fr/ƴ(_$G]!IR^}*ZZO-is 8wX~fCcukYYAL#MD_>캤\1x bÅps9> okTRH:Eſn]5�5nDW#葛/*|«z5?^'I˓WC`Kp4fa$1l7'MR06;VڏѬBNħא=zѥ%JK$(xILL޾$ƍ͛7 ̾T5lؐlr/  iذٔӲʕ+k4nܘ7nШQ#^{5N<i6U4uĦhZFjV>UitSZ6(J gO] j@e/V;f TQ2/OZ;*=F+_a\iO‚(O 3Gk4%Ѻ؀'9t~5m�Sd!|nM\HkjkB6mB;qӽ!> h:Vy*$;?~GEХJvEUcUă[s"~NrvaE@Y5a{5hO:Éټ 7c`;k= !EQpqqʕ+ܾ};;FB$==UM/55YYYj@X1LHIIaÆlْ7n:?(=Bи6䱺ΖagsOl<h=`[$f)BP _nZKWȩ䇟߯p,'D2-Zki;Kim!'T .*b1riKD42k57tϝUq>55GHD ǻN]*(18/Ѡ [S'a�Pu.F Urvw%3 %:VG!!<7Z4upMEC>F ՚7HLLqQFCŊ-^^PR%4]}1Ð_\\IIIfy$%%wOA)* >KxRYͧSZ4I\MFX'>Em;{㔄},qU!L8V~RG?o:O;?+6MMֿ>wzv|$9V;DborhKNeOJ^?|*9wߗ}OZ: :iyŬhV#j&H;ϻmtvHǣ~Հwa؎.Anʪw#yoas9՞C]jngOx$Z4͍luSl\]g N]$ۡ.]WÊ;ʧ#t?ɧ/Ǫ4|{ XQX}^"vh"~Dz@!آ!_V1խNET.Ѫh5a_y*B{ z+W]'ãTU'Bhso ]õHn+Pu?L~Af@j&pQFN䥐T cM8ސEz LtkN w-)qo<7]PIwVtS|!(b0`Hz6};ATCFE潙NSDY>7FhPf;u rD g:H*<156ݭs:s 8Ρ-Cx}/+ ##υՒФ/u1SGΐR;݂^()9VT 5ipzJŜ9wH_!L<==ڵk\vdeeիWv>>>xzz]-8?ͨQQFBa1Y8[Naa(6G%E/4J_ooQ))LWP!r١HLL$)) z=:{{{ ''G,!yP![.)OI(hE�eYro2*@1BBff&Zggg<<<R3}?yzzz�P!Ba4,7KOB!B !#BQ&<{~yB!B!BX �B!BH�(B!B@!B!E>AC!B!D-t� !B!�P!B!,B!Ba!$�B!B !B!BX �B!BH�(B!B@!B!� !B!�P!B!, 552IWWWW^& !B!+�0""FC"9lj$00LB!BKT&S@SSSiܸqY$eI&yB!Bad0!!EQԿ9?L}]csd`I< B!Bܻ20o�|;"Voө+vn3[fP:}-b !B![Omэ>�Ne2t4MYO!B!,Z�7k+[5aR۟:gezCNyFB!B24<K+m,nUlK}.ʎGB!ݗ~ &Я \bOC §� !B!Dٻ/`^WB$:'[jUlYVEB!BQ2@'�o8uS˞xxѯKԨSO~XB!B!}T# 9ɲwצe+g%=['B!Bg!0y=d_�\M ̃F$R o_}!B!N=|�<;#Ot,NI"U*B!}Z"S&Û( 66XYgS5d&;'ӧOH'B!Q�BQSOĭ[ ܼqF-YY\IVfVh?!B!+s0$&%ƹP233C�X_` B!ׇ][\] ZB!B美�3⿯U-B!k�8u-B!$�twwh4h0e$Zш{'B!LJhhh4kYQ!B!,^�֨Q .pq"IquuFeB!BX2f͚eB!BL^!B!K@!B!� !B!�P!B!,B!Ba!)PVI><==quuz]!B!)�0""VKǎ"9p1"## ,!B!,DݥMk.}6iii] '''|}}-&\Mں|XZ[Z}M[!ĿW2tpsszh44MyPEQ!<<G&\Mں|XZ[Z}M;&&??.BR*{�Eh0]JGxxYzlbuФˇT/wQB܃2 �E),tF#Z,%A.V_S-aB<k ٣<e(?S]-&\Mں|XZ[Z}M,B(�P!B!,B!Ba!$�B!B QO5 lٲ{o>  6'Gٕw4m4bbbX`AyE!B!\G�oݺŋ/Ȍ3]6>5bƍt>tV\z'>}:QQQwNHH?}*eZr%ڵ+tݤI4iRc0HNNFחIze!2d-G0x`8|8q"|Iϝ;eUG< *ɡcƌa֬YP*!\#O>[[[mۆcy_ϏEQHMMeݺu 4.޿իWi֬j*Lh4W_}šC7o\$"##ٹs'�{΅G:BQʭxY>S.\P/::ٳg}vիѣy�ؽ{7s'd͚530rH5mȚ5kXr%qqqߟ#F̍7bYxblmmYr%#11=z0fk܉#͛7Wnٲ%߿K&ǻe˖Ԯ]N: /ЪUr.ݣgۗf͚O?ceeUz9.B<8E}oܸAN0a 22ݻw^4h([�Ə۷yw[�̟?m۶1vXYp!o6sQ aܹt Oy7}6?##Gdڵw!)ru/kTX�~g ݫnGɴi Mh4qF֭[ٳ'?<NNN@ŋٿ?~~~4oޜ EQصkk֬!<<m+ ^gݺu/кukOʕi֬�m۶`ԨQ㡫{:u"**VZ1|RSSeԭ[>l~GnJZZݻwgС1w߱o>Vʳ>O<cXl'O]v1???�Y|9;v`0ЦMogz+V0vXWɓ nݺf۝>}ŋsZh˗Tgffl2l@z/K߿2E}�� �IDAT+VpI4iBNׯ�,[[O?M޽}Csrg,YаaCy6m @BBue͚5lذG2e |O?l-JiKٳSN>!F�\B@@�Z?!ߟ_~E?_gժUtЁW^y-ZplB }]\\\�y&3gdĉ$&&2qDOÆ f͚j'h˖-tMM[nfӲeK}$33;wr Zn]t,Yݻ=|3f 55?ۗ3g̍7h�αc ,~:*UR_/x뭷<x0k֬l޼OOOV^:u*...\|<==Yj< K,___RRRʽzRRRYf1h ^z%*V(\v#FW_}ř3gV%22޽{oЫW/‘#Gxי:u*?<{eذaYNJNNo6a?N:Ehh(-ZB {fʔ) 8ӧOb Zl ^2e qqq;^… A׏ٳgK/q5|M|||:u*)))̜9Hݷ?ٳ'.{СCу͛7/ `ƌk׎~[{y/mU'²[�ˁɓ'3~x�Fn{I,Yw}gƛoYda0"##&M.-- WWW�)ίʹs ++5.;xyyrJTR6m7oVG*hٲ%Ǐ֭[DFFңG#JLL =z(tK/~!_~zſyՋ 6/ri{1:uF1J[F �ʽyzjjժev>k׎?\_ٳXÇr`` Z⥗^Yf,X_~_~\MoΜ9;V�ҰaC֭[ɓ'iݺ56l`̙CXXXo8϶oNϞ={iϪUx饗Զ]p!cǎUϻ05e˖q)5f׮]fy?f>ѻwo=z]vqIʙ|7߿}Ca_رcy6OJJbfVZEǎտMy[}rRLrGqB!,G@43S'2o3tm n۶�IJJ*4%Kd}^~eܹsIw_UZoKr{GCFF:u*.==�y3<ChҤ/*((lJI'CPPΎΝ;_лwoF͛7y'С:Rnݺ1o<2"""hԨh`` 5kҥK4i҄[/33~~B @jj*VVVL2'r :uD֭qpp(,--˗3duɉPBCC h4W_kmm~E7nl6B[X{i{ ooo @߾}СzAŋ$&&RZ54+T�PJq`_-u;[fdggJ20Է$djqB!M�6jԈ_~K;aÆ|8;;+ ___t]p7nT;z]yQFڵcΝtUݦGիWUXkkkK.\)S믿~`0[nkk|<<x;w2aΝKϞ= MֶnzBVRJT\AQvv6;99accCvv6޿jccz?/={d߾}L:EQv"a?>͛7={6g6[g1 8),{iʕ+~zN8޽{ׯ bҤI899n.o iSq^'11@[[[xʻ 7iiiDuJkq=w:WRRR n?B<-�jL:W^yv1vXɓ'9s:Ūo߾̟?W_}W_}>Lnԑ8::3|piܸ1QQQܼy_|4h+Y&vvv@ɓ'[ni׮vvv899c68>>>TZ+W}{ZjQV-|I6lș3gò닽=OV 8O?nDHH!!!xyytRzv]O}- N8+WZ*�qqq8q5j`ggGV8s !!!fjZBBB8uÆ +SEyꩧxgعs'k.7nPg6mbȐ!|fg͚ʕ+1b666tܙCѥKutj 8{}t:7oNiڴ) bĈT^T Gi~+{:tl:S֭[`QoՁ/ysu<Ș1c_RQBa9K*WڵkYz5gШQ#~wX7n믿fҤIӶm[;yqssgԩS>}믿NVV'N$((gyÇȁ?~< .$ t q Ƃ ؼy3~1w\ڷoOTT6m2zyy_qj֬ɔ)S7n4hЀhnݺ槟~YfqIK}ɓy}6+Wf۶m.YgggcOOOظq#233uϯiӦՋqꫯXhzȑ#>|8seܹ3#Fk׮TRnݺѣG9r$ZӧӲeKHHH̙3Ş+yvuVXU ر#3g̙34mڔÇӻwohѢ^#Gиqczok-p/w^"""[.޽ƍF >|8'N䭷[bmbԨQj7mڔ0Ν͛ݯsʵUTaڴikL0@Yd =:x/異'rH?nɓ<yru:t(0=',t3+:Ã3f=Um>>>ŖA`ر̚5|||XbseÆ tԉg}xu3i$;w.{ՕիW_PfMuNʊ9sEfXz:06l]:ẘ+{{{yfN:3O<?<;v|g߿wyM󲲲/`ѢE|gңG7$$oUV1{l5jĠA�U;v`ѢE븹Ѿ}{233ߟ~P>}:!!!^yo>hѢEu 4 ((ݻwӴiSZlɺuXt)k׮K.7 _p!| ^^^<9r(m$%%qeV^Mzz:۷g=SLa͚5,]4i!C '0hٲ%ׯgٲeZVZe˖;~{a釡 lذ ?O<ZPܹR>BXͨQQFXa޴رcCJuѱcb'zXYYqI5jd1u6?hkb;B<bcc ,7o^}B!BK{MB!B< $�B!B oF]}g߿TgK&m]>,-&!B?�3`Q�ˬ%A.V_!~�3^/b<PXgK&m]>,-B!$�jVҨT,b<pXgK&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�tY'N @'yGDD<мڽ{7]vGGG af-_iӦ_:?iӦ1m4nݺuE(ĨǵGfM[~]}N8a?T&+VPmz9::ҠAMFVVV}XޟB;tC Zj888PJwΪU0]2W\NСCˡd$o_Lakk'1ӧOwjժԩS~Z}ڵ]w[K~ztΝ;'++v'|b/,u~{})::ZmǏwqZYYY;wiӦ믗wqӶm[~'bccڵkر{.b+x ɄwѼys.= lݺL֭ݝ.]I(ʀ_>�+W._~%AAAqq~r)( uT188KS>FСC9|0Gh4|rfϞMyO!)6l`ʔ)�2qDBBB0߿";k׮`0WEkҤ ӦM#**[i& G&((:oNNZ++WGPhKKK_U={SOamm۱c]vGGG6l_(6=z@QZ5KfͰgڵ(§~J puu͍ur5�֬YC>}ӧW^UMNNf„ ԬY{{{ׯ_)y̝;5kJΝK<e-::-[sѼyszԩS rZNΝ;UG%Kh߾=~~~888?βeԼZjԩSտk׮N^z�7NM7!!ANG~e6lM6TXGGGիG0;u//*U͍^xd�&MDFGxxz>m۶ ̟?qIժU?>닏*f;<=Szu&L@ZZڃkqƌ1-[7o ))Im#_}ԩS摒[oEZۛ!Cx=JB>P;w.SNUViӆzgn}vBBBP~}v틋 f&ܹ-[DppY@Q͛GӦMqvvVKM^/uָLpp0~mCO>̚5,ͲٳgׯC,XDΒTPݻ3b~}]�F#ӦMSۯ?>5jARregԨQǫiO{֭gggb(YlH֭9vX]5jrV^@{=u-[/V4(vvvݘ1cv�nxbeܹnnnc^L>]]z%))I_<Vٳgm6mZiӦ(ZV2ddܹsfۤZ@>|땡CeQm,YzEze̘1ׯ*һwoE+'OTt:(zf͚uWx]|YG׮]=֮]ze2ӹTR%uYϞ=^L8c7|zeϞ=2ի_-ӧPռysK.U4}tuy۶mr+z^ykn;k֬? +U稼%/yڵkgaʕb_h9O*999^o(ZV]~z5MS?Ftf {{{ի/Ywjf#FZƁ?d 2=33S\(:NQJ*/-$cWBctEԩSj(ZjJ\\חOWX.Om069xYP\]]t۵kwWz껊 F`5hܸ1�7oV az(zSdFFǏGQ:uD||<ׯ_7�`Μ9\p,۷oӪU+BCC}Unݺq _N||<-B Eu޼y={ȝ뱶&++K-G7mDjj* �رc\|moj1 ԩS:uj*�j̛7rY҈ʕ+TV y믿 K-T9p�zwww㉍%557ҢE;aVree�lڴ#GGguoF=wL;33[xbuƍO9p�}&L %%oooùz*?3�GaeRﲒٲe o߾ضs(8s YrU֭[hB [L۷ƪJۼy3[l)_-y&ǏG�?!;;7|+5ϭ[pa;׉uFƍ(=zTZzuí[ؿ?!!!%=GFF3Ctt4iii<E֖5k杅pM&L@TTǏӓ'._ׯ{̙3 S\.=zKTTk`ƌdddUl`0�X`QQQ 4gm@}v r;:]GUNrUKnؽ{7�uM[jժ?UVUۻw/CO>ѣ 8__"o`ee'k׮�?~lz$N޽;uyLLۥgϞ޽>}ݷx{BFSN?~1c0dFMvvVY2mbb"dԩlܸ͛㏗i^ڐ!CpwwG0rHu9^x}Yu.{ڵy7hѢ[9orrrصk{>]2g5MaRrezMNN{V/@Zf͚E׮]>|;S{<B�jPRG!11_Լysԋf;E2'@ռZnݺMۚV�v{<Sj?::5`܇۴nZlɋ/XWTI?>j(&LҥKqqq*?m25jGKpp0666je˖ <&MO Ӎ5 V+C  !!UlrA�5jİaeƌ]C`L~i&M^A1Eu&7nP #,,@Z^A*U̖?۷sYV\.WL*yxx.{3͛7�#%}}VhժYYY:tիWjժ;>2`0лwovQ-ӕ =zOzj~Wu K,a߽{ 222ݾ}[UXQ=1u4ܑ/;;;өQtIIIQx9&\/^HZZ]XQLم^1~ !Ľ/ h44`��� �IDATmxi&󿵰^f䗘h]PPPݭQwwwwf̘ɓ9uٽGYYYN+00ӠA yCy:ooo̎{llY?$++Kؐ76pqqã2>(~ЬY3 wO?O}:5qD ͉'8v}cƌ֖bߘoݺe,?4Kmmmi߾ yO>qܺu ^o6Y6 Z-+W$22+W{Kvv:_<<<s… f߁du }BXw ߼>$1_ sqqQիW͛7<IKKS={maN( {͍a:WsT!UŊk^ҥK lsm Y%o&66VcJڿvy;G-Y-`z^aoAll,6l`ƌ4oȝX`?VsnZx`_?w`0c&wӕd[__&f):hKJJRׯٛyxxx�o"ӓ {O>`fݻz3}t):/i``ܺu-[Cˍ7.$+?{ŵ7p ,M Q vc%v (5vƖW#XhT,јX5@7,% Jԅ}2*<(g̜i)CFXx1G5J(VFDBbZswf-m�.]Қ LHH2BN>Mpp0ִoߞ+~ۼb``Ԛ޿~fΜ4C)Vʕ˰ի9r7n]-yf+W\s: iԨ�vQŋo.ƎKݺu>i/(LMMȑ#$%%q5kN|}}{7|###={rsT!rC' �7|9r3gݝ8W<WZEhh(Z 3{i[5kܸqJ?!! 6(Aj_GKhhV7PfƾHRR~)#Fwk>|ݻYt)[fԩ@Z]^ ~zbbboy1{z9қ:u*׮]JUkkkJ.JeڵkMA:+Wm6_(i3!ުUv4SSSe~Frrrʰ*K988hlٲ -Q4PHSGDܳgOIŊ\kDӧgzjMddRi>)lff5:իFVknܸb2QVJkkku#Oj_uOzzzu)qӏ!~F4|Id6 hZZ֘i�M۶mmf8^gϞUFʂԑ.ӎ5 :Q@߯׮][ ӧ֨[ZwNf̴F8p{s$L24eʔ,G4>ԨjͲe˲Hi^ZO23ˇq̘1JW_}jњܔxٍ'Zy,Zy5oWWWMRRR=?2T*fѢEZdvҦp:OŋWF}<]YZ69qℒo622ҸjT*.PqqqQ ի!^nػw/M6Ғiڴ)ϱ? OqrrRJ-ZmGbsss>?%JА"EТE V#,\aÆQjUPTҺuk:Wrt-CSkkkmFժUygΜad^ՙ:u*ŋWٙ 6PlYLLLpwwg޽kssssΔ.]gϞDR9r$ ._UXǏӡCQTXZZî]6fF tU뭣 V-O>lƩS޽;E%66###j֬ɤIzix]S;v}M?:uD"E033_&PN8t/^32d}8G"7=ÇӾ}{-f . vߨ_>QlY&O?F]t?xb<<<044ɓ'(QΝ;3m4%… Ylajj<CY}733TT ###)V]ta)\0nnnӇ0*To[[[`޽9vvvv)OiӦ Ύ'2tPee6nܨ5bnn֭[3M_^8p&ce72BBBhӦMnK|sI9%Kꫯ9EB!UJn̙;H%}w֭[3Ji-Z}�B!B)� !B!D!xI5P !B^%O7j(?dR(B!�B!B!BR�B!BB B!BQ@HP!B! )� !B!D!@!B!( �(B!�B!x/ 7]ta޼y9}13f̠Atڕ_Um߿}RF &NHTTf͚dɒJ$f86yݐuFrڵ+!!!ZWXJ09sF?Jŭ[F_INZft9<y¤Ihժ+_~ARq %,..%Kйsg*WСCv+WWǎz9r$8 4i҄q)a/^СC >///>}NBwZf۶mߟ+ҵkW:!^Vό$V^MVX"CҥK|]kW^eԨQT^!Ch3.\i^DR{|xsaaa5k(qj5?vvv<z(:tɛWii]۷o~3t099`<==ٸqcҥ W\aԨQxyyѦM֯_r 95kd„ \|:[^IѤvI޽iذ!___8y)7ZKMιy} |}}:u+Ν;k.CA }L>֭[WZv2e #<<<۸gZa|7)S &гgOBBBիIIIN!oߪUX`uaٔ(QFq) g̙3Yn_|ӦMٳg4oޜ{=׮\/fff|ӬY3Ξ= VY|9�^^^o#11={hpmzE=ΰ.y9m#ͳg9r$2Un=lذm۶1vܹGyf *1&Me˖gdzdڷo^Ğot=wwWŵ {mnܸ޽{ӫW/�7n̽{طo@j +kӴ=t97mڄZرcڵ+?ּiӦ䄞�UTXb:uO?u-oڵk<x dرc^-[j)R%�TR+WPR\IButSSS�5j_Ů])SرcM6I&+V0tz-^v1~x!::E?km?ח*UWʷܑҷ^zfQJ.ͺuҥ<]f9UFz˗/"WM5lٲڵksR׮]K.]L2�_NXXX˄rE6l}Gt=C/X"&""5,{.k㲎 㧏QMU#|F TSU\犲\Yux-5g 26c塕 0W;z롨| sotztAS6؈FQI|ȼMMXv`efݚ[pi?@JJ /^ԪKHHp:̛7oooYb 5b…ӯ_?{?&!!hfΜI bܹs'4zM6aÆ).w}GΝgzvM6j֭[G\\FʰLbŴ VVVV�ZNPPJ2NLL ̟?ww ӧR߬A*acc ŅO?3 JÇ#,X@Vxmۖ˗g&&&@뇾+[EHLL.@ǎ7n6667)6Ҝ9soWWΟ^@=zgggp�"##3]kkk?'N<y>@RRR_>w}ى|{[Zbh6խ5j+"/P/IޓO, YgQ"jdA)CV܋:\X3|gХRtax4l `0alϯ뼯 ̙3k׎]v1|pر'>>7R\98qb4 Çg3?\rE+˗qsscу+Vl2�ڷoϊ+o߾cffƈ#8w'Ofԩ/^\©.F̙3Vu>No=J||vkf͚Iiz*�eʔ1.3gҤI|||ɑ#GذaC a…(Q"W)"o%&&r*We011a5!C{nτ tkic'''�<xawB H\\TZ*M!ck9 jN=>>CH_wWs;UVJTRYXb .LllV͓_ˋ+WO?eZ~#NV7W7X(NbdFO>tzz| ;\5bp�xͣzFqioSԪ(�E-w^N5jvRkիGݺuѣGZuqAٳ,]۷oST({/MZ_???j׮'|ѣG~ӧOVٴi-Rږg|1=]ϵkג!111u^׿u49HJJbĈ|w*UJk̤H^2{[ˁ[fBCCϔGFP;(~zi޼N5kFXx1/ՕL=Ҟ/I>r/^pB -򖋋 K.DvEڵ 1kLjvΝK3tg/L0M_ݻw-RnʐaLHHN`|駜<y27wC雷2$''2xM Mp1r!6QΨ<|Z5UW U̞=={p)lllؼy3-[d֬Y 0�6mh-C:uغuk\DDիWW ӧop .^#׏-[ЦM=JƍqwwGOO3f(o|}}iРN7]5CCCi7_ti,^o>Ǐ?cs͛ģGصk:t`ʹn:R+"7=znݪSٳg{{{nܸALL AAAԨQm۶e? Zs2i!E ?~8'N`Æ "ԨQ5j( s,�7%VJxgjߍTCCC֭͛7>|Hbbb*4DEEeڿ{.5k@gmJNԂi~d5REEE1zhZ*h" HJ}uɴϕ`Y<x0w,Y &ho޼96l޽{lٲ=z(=h ^JZ à .丯syׯڵk111ARQti�J.JzO?34C}͛שS231c**'YT*|gF!̙3oߞɓ'g?cǎm6F3*UbΜ9|̚5K+nV5[[[ ]{߿5?ͪU˴H/}}}ԩɓ's77{ݼ*qe<͛7i۶-*V&e.�j~Yf qqqJءCX"UVU7(Jqvvjf6hΝ ?jժ>eE_O&yn}֕g>RHU@ƶi7))M`8}4ʕ˰Ώ?grziK.xzz'dtҴlْ+Vn:GK.lذgggvޝzys֖0_~RA Sٱc=z 88XiWz!={T[hArrr(C[~!ネ:t@Ϟ=y,YRF/皃ݺuc˖-ʳEѰcj�#""3E,/p4icE.y3]100 (((CёٳgFٲeu\7 C۷ zAdd$vА֭[rJ @݉b,[Lɓi&|}})\0'Of$&&ƍYfZi9s&8;;ӧY`A]uaީyИlYIu;ƾ7c1O,yyGGG ** WWWܹêU6lvvv<ymҥKJ(ټy3**CPH۶mK߾}9r$wz ]wjX|9<yٳgkCOO/燝:رcSE3d ڧ~ʓ'O24 \h4u^GDD޽Ǐ Ȉ+tR&Mıc(QD#~˕+ o}:uD۶mqrrz;śHHHҥKR/] foo?,,?t  gy\U!ě{.]tٳg4iDk.Y=3<==[.O>XZZre{Ν \iӦXYYQ~}N<?Jyf7ni%@VVݻ7nnn/_CCCN8… 9z(:Xܝ;wñbŊ:tɫf>Kv'`No̝;Yf@r尵EJ +++l‚ 0a[N %^^^ZUݻwplܸիWӬY3fΜề&88۷oþ}24 x5D&g3g:V3co]~`L?[c[:TɴoZ*۶mܹsTP={  į35b̙Cۖe˖1|&N#]viӦ1p@P>? Ν;GR T!k}s:^^z53fPY&7f׮]899w0rJ矵eUDFFRfM6өƱdɒߟݻwxb *D:u8qDV#5j5… ff,Y{ɪU<<תT޽{Y|9FAڵKk옘{.]i׍"i4 oΖ-[ˋP帝<ym*#7)65z 80ÌGVNHHH51`7sSB~c9sFO%�:uS�� �IDAT+W !Bd֭:w/g-Z�w֕Xά5kӧ΅?H޴iӷ*!B!^C===v_ͩ#b]|;Yo$""-[0nܸWZYfZMTB!xHP1owoݽ;TR׭UVo!5B!B3B!B!t'@!B!( �(B!�B!B!BR�B!BB>c<xI bee[Ο?OLL >̣ !뱱:B>ļ�pyj5|P/.\{ϟ?Orr2+V !yf5!x}y�[Es2߷X܆aVy+ώ�c֭F|PT4hw B7dee;B>pN~ #.Atx;bxou;/`<xAHO!H]B͇.?zn4+5&%kՒ62#@!B!ͳkoiv� ]'O~=")^<}u*X{{Sm[?$R*B!Dk�snb$NGOO�{X<}J$ʑƅA.MJI(t5u !BW!uɏh4Gy .Sr'~߽jm&fLkI5]2sӧcWTAӧI޹groxg^;wJ!P!B& /c9<ެ~8&T4ȗR2i֬o`ݨT*?@rr2?FVtj[yzʕ?>}]v;iĹsSTax'Lҳg<=yke LJCe}K?&))I+iӦ,^8O!By9s<éWcƌDEEe~GZ8qbqbbb1ct֍_ɏj4R^c:{bmmǗmqY 4n~ծKٙÇg)@JJJ޽{"""j3;Y=dN8%&bZƋc`e>o…<}!IBǍ#LO>ggg82d qƌ7|/:um۶<ݦB!ć<\||<SNtҌ;/zEbbotOOOƎ˕+Wر#111JXuիW9r$uԡ]v_ȏjaDgl)F!#^Y. 9kt^*UسgOH71`i`ɜ9'3{{ Ғ/^A@�~۷m2̌Zj)l4h@:u]B!"KKK٣ՇzT^+WPR%0a/]v@ ڵkcv ]x*T�cccLB-077LU*.4 c|j.s> >*Ac\O~̷섆R0ݻ|ߑ5+TJʕ+�3g4h@˖- &99HևJb 8Um:2{[(, /+U }ggxFBAL=d5jǢtMG;w. 6>cƍj4 QQQٱm6zAJݻh4֮]K%HHHPw=LLL8{+5y!۷orza�6mڔ5k@J/v8s?%%u?RZ5.^HΝcӦMJs燻;;v?4i5jԠukJ7mڔ+VO 2]B!L|g�裏RkBCtW.\]a%짟~SN*a>>>\~+) ))MBO;f(Q'f8U}hz|̷`||<'OԚ rٱaΝ�lٲӧOSxq߿?QQQ̘1!C0o<oҤ *T`رfNc0JJBݝ j5J8nn1mÃ{ZfРADFF2m4ٸqX֯_Oǎ $**A 5j?>| @+++4M[oZ�3f *Ub޼y+V ///"""P5"$$D{E9BƍYf ̜9gҠA%ށY&K,L2hт{G-HJJb֬Yi???~t-XZjx B!xe>G%88C3 y%JpIj5jǏSxq8vvv�ܿ?:S=64wo"9evNxp ]G~ȷ&oߦN:Za#F`ƌ.gbbB2e�(]4ʕѣݻ[najj @bb"sΥK.;wI&:1oaA2*j(Tj~5`PP2+++ @AHHW^ڿӱcGeaÆQzu MLZ{....׏={Pn]�޽;zzzuhFïlR -GG�ԫW˗/vZ&OL^>}:~~~€pttSSSP:Ãٳg_Ė-[bڴiS~}ݻ7]t}ҩSL!B˪@JJ zzzDEEQX YXXKBB*i (YWPo+?!::;ab/Ѥ$#!V$&%0z43 h�)o�Ҧ ى :: eDveU裏(V5jܜgB^ZV$''̳3gH�`hJ(5׮]#::033SN\z55>>֭[zjqwaÆ[@ֲe MۙMXX�>>>?ӧO nH011ٙX�é_><<<<x22mNB!P5k֌'Op ~t,--uVWBBJ>6-,ݻ+mG5|IN#f-L)U(l;tAA)p5‚… &쭇e*$i4<{`;vT>s؈L#%%Ww/ff׮i `voW|||ԁb .ѣG144Tk-ٺ<==ĉܹs~akk'_-(]4KΓ� �:t({ӧY.􅽧OfĈJz[B!.fѭ[7Ν;iٲ%<x@"ݻwQzzzT*ԩ͛7<enG5)̿ݧyFn2ܑćX̳O(dnxw EF/Z|̱^x*UXbbb(V2IsP•+А)SxvR?<xQEl,1Æ3qs�tR Fdɒ29::fZ�lFFFݛ_uѺuWTT)㾪:v]vX͛73`�LLLy#uUbEvޭtjԾ...8w!Byn/)VJ Q?}͛7Ӿ}{%sά[X%?|T\9Y% cU<-yI [DI!6z<K|: g ^E0'O [[[ٲe 899QbE8p &MhѢ?GG7njAARUl,͛c5z4f:aQ+WFVQCҭ[<,T h~ cΝ;ǼyXd K| 4C2n8pZI&??*D&OL…ȑ#9rS{zzbaaYxֲu_~ N={6tЁƎ̙3& B!ٳg3f :txr~e… 3~xIJJŅ-[`cc5GVs<| j/YOҮ"nX>�96}Hܓ&EF$¦^>�ߋ@OOOFI@@�ᘚ~zҥK�L>~tR:um;7T "ܜϟ5nݹߦ Q]rR%wʣ[P!>6 [o,@ܹs ҢE VXZfj&OW_}Ŋ+/طo9�;קOrmtޝg2`�82ȈӼysʖ-q(Rzb߾}:mޞR<x07ofΜ9tos_!B"}>Օ>}p)Ə7|Cdd$ZjJN:b >̷~3+WLciiɺupqqaʔ):t 7oNG5dIyyT#$ǠIC#ScopCw4OK>58pffJɐڴiw[nWo/K¿3gPi4>EИ=33YCKxn߾M 8ydR^gY/[nĥiӦnݚdNZCUFBy9((t rʏc[N|5{lEajGNܻKJ$XJOeOoq)[nUEf'[h]eZ(w"Ybˆ>v ul,V^^XVJ;ZK Oi|ON<w~'E!B|�@(" Gd5O++Gg0*6.Ӷ�1o�k5E XT_fwYb@mls˖-{{\_B!DA&]xe2L Ko3})#m)�fC; 9z(W={r~@!B;>p~>-55GB5W#b!B! m~R4)a):CyRkkkR[;}}}RRR2MH!rܻ5/K~tDjv3ꭤUJм[Yv�+++.^Hrϋ7¥K25aaaTBl3B>PºGFzwFmR�B9wOѣG5VVV/_>8nnn\p<LB:qss!DPºG?tR�F ; {~'A!IB!BB B!BQ@HP!B! )� !B!D!@!B!( |АޤB!B|||t{Ffx%W\L2wNBlPY"׏}6 B!BRhcBLOOF#jw`+B~ R(B!� yC( = E'5B!BQ@K0?l0NJtt4-[r2ɔSJJ -[ؘ/ҲiӼysnݺӪUƍ3zh:y1Zeذa̛7/׶QL2=>cיٽ{7<{,_Y&>nݺ77nzj֬ɚ5k}^uZW_q8%K�駟#Df^޽{8t+-;{l&LѤ^>$u<d *D&M?~|k ؾ}VXn+ "GǎYd  ŋo}N^ok(ƌݻwiѢO~'탔}�ܔ0�TVLEnb޼yagge^>о}{j֬ڵkݻ7:m;7>+Wfǎh033f͚JX-hԨԩSM̙3'C؛C;^B䶜k͍5jЧO֭|Ҷ~B|(t7Ԯ]:DN^il;0!!SSS8�Çҥ AAA|g4hЀ@߿… i޼9M4aZo QFnݚM6_%ITTǏŋ?WZŋZVZQvmBCCtRxwTXg2m4Oŋ155eԨQ�ajjJddK:t(ӇSN)<x= _~%+Wfܻw5QfMXhuy!z�ԪUK._`jj? @Ϟ=={|-XjÇÃnݺ̿t&MI&xzz2~x߿P|888pM�2e Vq�sVXA֭iԨs%>>={жm[�055U֟ri"""jժmۖ_H4ݻ={RfMF͝;wމ})�N_~xxx0rHn߾̋ᆪQF4hЀ'*#]v|w<[`nnRi&Vg„ ʳ93)))l޼:P~}{j(ٽUKhغu+F_ԩS믹{. ___4hNNNL6'O0uTjo/** &(7|iM`fo/Ν;G͚5TR߿www%ۙ8q"cǎ{?}}}u?C\\#G,7/^aÆL2vqׯρQ�_=zгgO/_ΠAشi*U^Ym,Ãy摘aӧeҥߟ{Qpa4 +Wm۶ݛMbooLxbN۶mٷomڴ-Z;w`nnΰaHLLdӦM 2x tx'Oğ6mݺucّѣwC !))pV\s! 'O+LJQFѦMn޼,i&f̘ ޽;6lחbcc~>'NЯ_?Mtܹ3;v࣏>bԨQ<\i?]vtЁ@?~LTTR{"޺$Kdd$uպ^t)!!! 2JEPPƍGOOm{#_rj:CX[diOIIAV+�7n`)˝8q JSӄ.\vvRS4i< dܸqL4I\عs'}�oߞ5kЧOT*7Yf6cHcccJL2�ZZFVȈ#ٳ'ZsܹsYf&???Wu֭[~d4M׹%�?&"""ҥK/^VZQP!͟ZdɒPhQRhGгgOڴi@J;w.aaa퍷>'''4h?wmذae˖jժjRRR&00CCC� ٳ=Օjժѷo_<==)V�eʔA__Zٳgs\n׏}(M͞<y¸qXhz4nܘM6ѧOk^__?9jÇ++VȆ ?[.[laܹs(K.A%IF�� �IDAT)_ݷMY]?.]PBJXٲeqrrR/-RRRa̘1PB�lmmT#GT꽬|92*X{{{_FڵkDGGg屵7]- .6b||<AAAtޝ?Hȟ?sQre޽˵k(SLyfUر u_*нl…49[:GₕUeZpttUVk׎ RtLӐ]XQxqbcc*ш cVo%Dnpe<OYlRhDR)^zUbgg,YҥKsuWi Uo3\owΝ;Zlccc4i3w>g$vΝ;߿ּXo:t(P^= *+]vcǎtڕ sv!^WV׏ V`ʕܻwOO,k h4ܺuDԩ!~\\\c@Dۣq)\0MHĉ<x9s(9p�+W&!!xAj5bnn?AOO/ɉ]viٲ%]ta„ otڵk駟;v,=z 22-Z輮x /Ί+''ϟ!##^ܿ?rZaccce>gv###p裏�۷/ZìYcDzvZʕ+… r fX[[|reY!򒩩ҧPBԨQ|}}82� JҚW �|VO%::*U!2{C/н{wϟw̙]�˗J)K鷕͛7ѣ4lPNf͔~:0/-[|P�OOO<==m۶ 0�[[[yy5Yڵ-Z/36C;^B䶬fffxzzfiJ(AXX4%,,%Kj}R(i9ccc8}4M4~b0558::#GбcGk�>gv===6mӧӧOci۱}mۖvSlY )m2ehѢʕ̙32bxt7)S0sLׯ fffܹs$\Wߎp-ywf*>/_1cpppŋ888dzRH~Glْa^F>}:gϞUFa0a)))qVJrYz5ǏԔʕ+gXi֬EÃK.|{}@hh(Mϟ?YxҌ3DDD1jժ)R5jcʖ-9%K|tqF%5/!իW>??? AXۺu+%KB :-7x`z쉣#*U͛*Tƍ3qDӧO)Z(w&>>-[I lٲZ>gҠAi֬qqq 4&OLZpppÇ={~qm֯_O5䯿gg\7!D>}Xx1+۷bŊ2w\u۷oz1z*KfҤIRToѣGt-�6=rL4`V<X"W%J(RRט$ ½!x<8H!CEdJ P>gΘk?kk={]˃~/� iܦ.]nݺaȐ!|mFKK }s砣 k׮Gqq1 B0n8<~ӣСCݻw6lΞ=KQz+ �&&&駟`ll mmm$$$W^eΝcDEE#GĮ]ЪU+BpB^Ì3믿a8q"ө8b`׸KII!,, Xz5:w KKKSm۶a۶mPRRBHHgffݻwСCX~=1eB0k,!** %%%011#G(Ͽ:::􄯯/VXA2{޽qEDFF rrr066FMM ڴi555߿_zjիWhժ֯_| <l6***NbH3バ[nƍ?NNNxuߢE۶mѩS'DEEaڵرc6F.Cb!=I\30Tg7@'Qef`\0s8lq<s8""�20$D٨3~l~D#s( Ͳ7 Q0c# 4?2md$UVcڪzb`uXfZ2LNNndLaaasgᛀ'Qef`\0t᫧,'6G -&�200000000000 0@r 6TVV`'N]֭[1yd 0�Kܹs5k ???5)Xhttt[nѮ �000@pp0JJJ$ccӦM|Acpto޼dX,?^<[ L2 |1ehhhA`)Ӌ/ L:bJӧb{3|><ҋXUULLLd,Y^zܹ�3k999_2 ?(UUUhl4 K, j:غuи077G~~~rDtt'�8PWWG ρW^:ye% bzII ƌϋWXh `ZZЫW/عs'w|}}addkkk<x3w\ܸqvvv Aaa!k*?[[[EiiDyͅ9ڵk kfffTz-tuuQWWOOOxzz"&&^^^bOp88r(AC1m4899&ur oرprr'NÇChh(TTT`ff8*ΩS0}t5 7oFaff&IRR3Ӄ8 >>BgsΉ`kBZZXlJKKabbǏ10�@HH.]p\ECs󑓓--&ۘgϞANNdYMEqGCz7ILL,lllrZ:㛳4Y~I +//֭[1gX,:u o޼!++ �+`mm#88`X��mmm())!%%ҥK}v� 1x`Yl^l[[[b%%%زe oߎ;"-- ***= 055E@@�TUUʮÇ \) 9rTx6m^@/^@\\ɚ+U�0n8;v,� ** ...9s&� ϟ?Gbb"r%)Sll,iiim۶X|9бcGurpttcsgCbvXXXm۶-իسgy1uT\rvm޽gmm UUU\t jjj/z t%iLvv6믿6ޖ$㎆t]t%&&�,--lf)q̙FV�eddh?�޽;˩HMz͛�={Ҕ�ݻw�7n ''Ft8t|y7o5lmmc�m��>߾}{l 6 RRY:h _@Jѣ) rRSS%%%*m۶Ƞr_~OO&=׼<o☚Ǹqr&ЀXƼ۷oJIݻ;wFee%?884߈9ߏ `pvvFFF�֭[4 a߾}(..FΝ''' 0� .c9Rh9sSNŠApBP/\�;wvvv044۷o�9s&455ꊕ+WbTVXccc8qBlDii)ϟ-[@SSS`. L6 |ZM0VZ�l޼,fy}xxx@SS��֭C`` ֮] CCCXZZukEM\.X,N<I]X,rssԛ^FFF000ҥK)]n:cΝ1b<<<ڵk:u*4551o<ڶ4X,J[+V@PP `jj(NKKÄ кukYn߸q#ԤPok'OFZZ-Xb #**inn;w !ɸCc2|: ,fSBCC_n(ׯRRRp8r Ī[n�L[ӧ�˗PRR3qի_#2OO=�fjڐ,gϞ"8Qu LVcwwwjMKKCBB|}}#Rݑ & ..PPPV !uQWW'qwT OܡRRRy&#G/_n!Xx1ŋ3g\.FrstJ8oi,YDXXahh< 0�F@NNajj ~~ڵ4i6l؀O555 44%%%?+@`?퍎;"33FFF4h"""+V|r]9ׯ_.] SSS>}Z= &&&BljxãW^Ell,fϞI& iӦ}6ϟ=jdduuulݺ'NDV-[: NNN`UƍԄI,X۷o# �PQQYZ /_1i$餤`ؼy38 i: $$ b !>&J7ǏVIl6n߾M$%%aС XZZ�wXZZݻwXf &L�l۶<غu+ FTT&O,Q|={5(|uHVV߯T_tt4w5q@ !�ѻwopȌ3̙30ĉ�)//d=zD�k׮SRRBCv-RvcƍirZ< �kcccuAѯ_?� SN%/^q8f:�ry>| 555M*S~~>QPP ӧO' ȑ#ؘ� B- GGGzjB!qqq�yӉ,y%u{�rUB!VVVlNΝK O>mr^&fffd˖-\.L0,_BIɇ!lذ̝;BHQQN!((('OBپ};166vEEQUU%Νª6$o$006sL?_~Ą狽DWWB%8O<!JJJ֭[B'ɓ'~B#υ?>-͍Jlmmia7n$;r_,.KHtt4!=Rq>|H�{Z"++K+P^hh(111t׹ >| $88BHjj*@BCCM̙3ҥK "ny';;nYYYr B!w&G&M:uDDq2xwϏ|3m{>}J�LBH;�ILL$UUURUUw>pQM 㹹zٳgpqqAPP �޶@³g(ˆDDD )) ~~~T<yg.QUU]R!''=}�/|մy+<S޽K wI `۶m[.;;HOOGUUXv-N>7n ??DDDu4J2rAJJ ֮] 5V/^m`ooCfJ>tuuUI�@>}�ɓqQp\aʔ)FTTjkk)sfj$! WX,FI⚘ ++ ݻ.ߥKR$_~TO?cym.9N:Z6mHPZO?tbA 1|l޼;w:kPUUAii)cKTFfC[[[h�&ynN?Tn!!!;w.qI<m˂HKKBֆ())Z?QY.mBEE2#hH(//Td4,q0Yf `tZJ]FFF޽;LLL{5&pvvoF --ÇѣGŨ8p�Ǯ]hNWu놢"c O)*wwwhjjRp�3<3Į]Raŕ+W~va|cccu: :uf"vaРA Brr2233+򂎎TTTト̛7O`)ež={p-_<TP/^G6ݻw҆tؑd >C||<Fw OAj :t�P߾pYdffZ7֭[K@7jfYFFwr{kkk+++̛73fס.RƍSnȃ [ԩSgx1X,_;BΝabb@YeeeUnIx=^|٤=}aMDݝ߻v킶6?*ƴiFkL͛7acc#֜n.j>}Sɱ0 4Ell,z!r9çWٳ'.^P?o]N�߾}  ""222طomŋEI899!::o0UUU={ Ƒ#G0e*,>>f�H''';vLɓ'E�r\#&&ǎ2->>>4^$p!8::o߾|~۵kG +ow]] )!ƸqUbaܽ{pvv6TTT40QWWGjj*/J-UVVɓfBGG9s&N:#GPuZ:իW1x`o[[[ܹ111;w.q#z*UOiz YYY]]].^(^t.tQ}ڵk033âEk׮ڵ+wƌ̤f̘A7zzz|q6l�%%%dffbʕ�=iii(((|wRưX,ڀ짟~1_6"~SZ:k0S�� �IDAT鰶gRRR߿L%%%8::ѣPQQӧ$B?k?t8[p>''|!n)6m'# -q|%l. 000˱g^惠%a;xxxf#&&vj„ عs'̙ggga:˗1ydBYYׯ_ :;vĪUꊚ!** 3fDy)`ll?~Tp#88T>:u$pՐ7o۷GZZt555!(((<eddSN҂ ?u6~:F3gF׮]1d((( ;vYЯ_?nFQQPPPݻwcA^zxHKKKT ={ԊLll,bbb~9ټy3LMMPSRR!C0i$̞=-4鉁RfBN��cƌĉ1n8$V\;BOOHNNFhh(u]OO:t5kF>|CYY q[GPP͛j(++#66bo1l0Ҟrbܸq4/лwo0{8O<A۶m8UUUß}Cx) n>+v؁dO `ooݻC[[=,μp.\ǣ~~~iHN||<ia Cnжm[ځ044DQ\\ OI*((@EE -[>,HNNCnp8C"vvvXn,X�;;;bҥ #�Ac #D|K+;<==rrr|[KϞ= ===ɡ}0''{�>GbҤIFXX#Gмܹ8v;F[uvvFǎ={`رXv_+ DFFbѢE9r$㡡~?'�חv7֯_/RI!ذaBCC#Q4][cQQB[[[ꔜ )WOOOb׮]{.�JCGGl6w0c ʼb_~PTT&n]vx:nݺ.\(~9sׯlj'0zhQ ŋ!!!>|8oDFFb˖-Xr%0a899122P֚СCg۷oc9r$hmڴIгgO+{6668x ^~ kkk[N$]P[7op~cƌɓ'MCCC$%%a۶mػw/F ##O6A={viӦ 6mڄSNԩSx5 -[=[cÇ1g`۶mޗW.MFݣvQJt^z ./-|BUUUL<lصkn߾޽{cӦMhr:@#G ??8G癙Ջ8,Lȑ#Xr%(FRR6nwww 8ׯ#!ɸ{H] oi|={P&...x!ߏ ȑ#G/͍]d@#11&L8nS:-÷9Əs SWWCCCxyyQ{�6vk׮Qr5j\]] _u!55QQQ͝")) ...{G@tBCCQXXHڐT7 ySClq;V� ҐҢ6WWWcǎЀ.^jͺ~LVIHH[ِ0@fرcpqqi6AUUUZ='Nx WWWc,tA!-Q300a& д!.YNNK,%K%}OuH[IGG/Iu0r.`````````````�200000000000 0@f]N�SSSXXX`߾})++Cpp0'NЮWTT`֭<y2 �u9̚5 \,Z:::pwwǭ[h �sss 88%%%1i&,Y"׆c尴߹s3f UW999"eEFFbnݺW b[UU%0ٳ�pa``�5y`Xx4\c|(6 hjj/^}.]~ԘWڿ .]*6߿GYY-, ׯ_o\}^rsslxqss˛;̒%K`9n*zFF͑ߤ\.455-;wb, H14-i|UPP@WcƌpPVV/M|w4١W^%<<<sN*NEEq=8x gܹqBXYYTׇ?rssakkR򚛛 ssskAAAh׮̨F[ꢮDLL rEp88r,`݄su! ڵk!-- www,[ 011Ǐʫ\Bw|቉@y<%Io�J\nj3?޽YvQQQUVkw͛ahh 6_~1RRR��˖-{PVV.0=q1ӯD!((b~$$$@NNN>QTTġCIr>uo.X|3B>#''ZZZȏ>gϞANN9ww RRRhϔc֭3gX,N:7o㐕�`ŊF eeeX,�6 TWWcҥؾ};��<x0bcc,6[l-b`jjlٲ۷oGǎ" �BeE1uT\rEh(xzz"//iiik{ �K.AMMMj }yu҅wbb"�]} p0`�rrr8<-zzz8p ݻ'trt󑗗˗/Ȉvm:u*u !C@[[B׏1JTópBt]d<oyyy,X�vkMܹso5~F} ++K3jhkk#!!eggCMM '灁[E^^gΜil4nPFFݻ߿7xn޼ �ٳ'E*''/7n ''Ft8t|y7o5lmmc�m ZUH۷ưa %%1ӦMHy֭Ô)Sжm[k 7Ez=ԯVX+++tvdd$gѣGSϋu\PwOΝ%oR� X[[ؾ};,,,`ddPTTT�HwT`hjj#""0q&5߿? uuukÅjRSS(}Jڏ~%7�̚51SL7qDlܸݻwY+@SSfUVV&ſv_~/\@MKKK :7nDMM �Ņ21_~l6ƍ�jՊ={۷/<==3lc6md;w1e2#N&¹sꊁRz/^_bĉ&u-l"ҪAڵÇлwo6Yq . 'ORa=Bnn.Yfo߾={6ݻG]㵁8899aXp! jYJVV5)JJJzj�K.EAAz;wbĈ�ܿ^^^8p zjyܵk0uThjjb޼y--iii`XTݮ[+V@PP `jj(U4L0[ׯ%`ӦM1bFHZ|I&%%aԩObݰ€モ"};**fԷK###۷Ol:}H&qk<x+VAq#FPZ{Eiϟ󕽥-xΝも/��MMM>}&~:,--!%%+W �u�B_.>}��^| %%%((( ׯ_'޾ %%%Zx=�fjڐ,gϞ":fq}xxxUndUWWիٳ1i$ة%J=z1bFTT&OLyMySLYXXd>|:8pM#<<#G5m(!Xx1ŋ3g\.FrJiɡRlF\{Q]]$ 4HEJƈW'++ 5O`=zD>|j^±c`jj3g"66tQj5557):ƅ 兿w?IGv bؼy3� 33oߦMMMn݊W^aΜ9Ծl 6 :ir<xSN(1W555c̘1p8شilmmf=z' dee,TTTW^Xb5q_[n;mۆ~!))k׮ŤIa<}666uKƍI8,X۷o# �PQQ}]j._cҤI)++øqйsglݺ!##COIIyfp8l{СBBB{{{�8S{{{ܹsXt)/[Qr{5͔ǎ֮] yyyņ}F� ''055 }oOyL:\.˗/رŋ[ر�paܽ{KR/\QF}U4֎B6۷oK4b,_iiĿhRWW'̙3kr B)))!�w$""/3f3gRaaaaDOO/'�R^^.2_�)((?z� ׮]㻧Ӈ޽[7ž<yBȭ[!?DMMMիW 'O�/$$P+**á>v!ϟ'dرDMM>!Ϗ.K oXO>%w&gΜ!dդSNdڵ{bcciի !�ݻwNOO'˗#�իW !XYYQ򲳳:;w.#O�O~t/_$�ȥKƉ${_?~L�dL?jLS/QSSԛ7o,ILL$raBzMbcc !:u 2p8rej#'>>>�y !b Bir$%%� | &رcB �rmBSþ]TTW�y!3fZ^,X@lmm%Ie6v"F"T؞={H~hF'S"#�֭[o.1p8jCܻwB̙3I`` 3gRx@N<Jt\.#фP[[Kdee޽{ %&&&Ç""�Hff@9fff͇1 &J#Ύ&c̙dҥϟ?xH܃UUURUUW;w$oÆ dܹ{D/AM)!" v$c .�DAA\|;o<h"o rܒi㫧O[ee%@EcX9;;~QM 㹹zٳgpqqAPP}�@[M={&qCDDGɓ'|&UUUڵ+N^^rrrӧOegrݻwA߾}i+a/^ɿ{z_??϶y[UUHHH;h_ڷoO[E-F'СCirYfR6JڳgOL6 033_7bѢE}KKK˗صk&NGrx۶m= 6*>tuuf�@>}�ɓqQp\aʔ)FTTjkk)fjHNNl >C *1RZZ ]]]c锧DXYYɉZHIIAWWjjjqq!̛7HKKC]]/M/}gSOpuuELL gfff7^x&ÿ˷Oխ4EfNhqn߾ cccڊ՛>}͛7 @o"eQ"ܯ\.qqqѡiӆ/nö٥KRsJҺuk`ܹpwwɓ''򐖖:OkkkXW^i$-- ܸqC)))Yyyyӣ}nժU{011AXX$޽{҂PY I66kj*++iaGc1I#FҥKacc А;qDڵ o߾Çq)-CK$Վ[pvvoFs,--ÇѣGŨ3%;p�ϟ]vќCt EEE|. OMvݡIQ=-<]Raŕ+WF3y8|0Mغ9uF?~<?~ a)))t&&& Ě5k$vܩS'ƢG9rdÛ7ֺukt ӧO8 ŋq&ջwzر#5>|8ӑx1q=$$$xOĪ-VZ%prRQQM)USQcD7DTT޼yÇCWW8|0JKKqA1@ӧ#)) 4h`ooDܽ{555D%xc8|0?L6M0 i͛7|6mڠ}BX@OI\rA"Oot>gϞa͚5ׯrssK+ϭ>7ᠼ\V AnZ^"''|NQDwww<x�&###;w"..]t7D۴i$׸ݼy666~Gym(gϞx"BCCQVV? ۷MٵkWxyyٳDuuXWff&kjjܛX B�jbҡCZ<}}}(**ڵk8<|atZJ.vܒ.'o߾ ddd񜝝o>js(�\xZZZ/'O&VzzzPUUٳgj9r!>>[ҭ[7899رcR#ɓV.\.V5�� �IDATcǎ}Cff&a())!33+W+!�mJVWWcE;dff_~Ti&xzz T z1<|PVVM6|_q]dffPQQZHMM9xRSS�eeeL<l6/ttt //3gԩS8r߷H^^&M3fS`gς`|%I^D%SNHOOu}}}z ? mmmp8l߾=z{3338p�gϞ=>|89SN?TGܿԾ1 XYYaǎ8x o֔3ddd`mmw͛7aee%ީeǙ3ghwA/꼼<+ʀp_SS+V@CCϟ?ǥKyfQ| B)--1c=/Ao:Z^^WRkkkJJJpttѣGӧO7)/`و]ҰB ]]ݏ)&z')@>۷oÇ-oX|9كիW1عs'bbb0w\%H7!ϫ(}*In݂+֯_w֭ \]]fo߾$N-q|%LKvCиk;xxxf#&&xDEEݻwDŽ sN̙3(**‚ I)˗/cɰ2͛СCѱcGZ 1cHWoooBNN: ㈏ℇѩS'W7o۷GZZt555>O<A۶m1p@PVVgϞ iiihӦ ա?}ES"((m۶ś7o0x`L6 ˗/GYY&L�GGG/bbbЪU+9/^ă`eeŗׯcԨQ1c}v<x�ԩ=z�l۶ ڵ͛7pB888@UU@xx8al޼))){Ȑ!4ifϞEAZZջ-0k,ʤe̘18q"ƍ'@UUrrrݻwѶm[ 555ǻw0zh &:t�-[ӓϤq[$G۶m?RRR/Wۗ߸Ϟ=CEEeکS'̘17!3olܸB5h cٲe̤FII |qxQ VOY,OӓsuQ[.\###cСš5kDEӘce"$$2e ޾}u!55#jSDyy9nܸ!Vw kω#Э[7mۖp///y 6 4}c(//2 6kEbN U044DQ\\ OIy !--o  !!زeGhb===b֬YXx1ddd(%\hiiAFFgϞ0l0ސBjj*fΜ)R:t5k ''ﺨ%HI2)O!nlPQQwww](--N<v�Ǝ ///cСbW!''MT;n߾=TUU=z4j͛ G%K]]]?SlbooOI@@�)))iR~Ӊ߉7B[l555BCCחOIHH ^^^d_~d$!!8]YYIlmmɾ}!TUULCɚ5kHQQ%&M"/{333Fлj*bbbBCf̘A($**L>hii}}}pB%rbffF~w`jŋ6IHHz5AAAD__;ر̃<B={F�Puؒ99hKJJ";wڦymի�ݸ-ՏoaaA$Wۗߘ<ܹsH+#!m �利!W&{9Cn,4~rZѣG}:|Ç/o>KӧO:pm|9!K.GGGҿ2o<C]DFg2y<x~SsҜ|9>qJDȸqȁ?L<hiiS!cǎ%zzz$00P械� pQP]]MmF177'ޝJٳg$005r꺙 &ƍ#ZZZLJB'0~~~suuu$BU.7n""4{MM BGhNx6mmm2l0b k}BJھ7QHO;!jll22|pRYYIճ6!C 6cK[_ٳߟ_ v|52zh2l0r_ ͍M yqIHLLĄ $knn.loBƎ Өʅ 0rH<366 ,;wZIPPqF�{իWr0|ѵkW\vXHJJ ޽$3斚(BCCQXXfI)^^^i_/'Olږox&9y9_XDDg\X,b̙pss{ٳߞ={ЩS'?ƦMɪi&,[1Eоo9Q$$$ťo iiiHII(Grqqq011(rf;v,tuuIsr}DGGcٲe͝/!ģG0rH$&&RG]dggCAAv7Cc\]]%:[IGџkرcpqqb6c֭Xxq=430@ψ4?Nmo<ډw-}baBܹsPTT(y LK/I@FFw2||@00004'd% >�200000000000 0@r Cmm--NYYadd8qv[nɓ1`�xxx ///sa֬Y<x0PTTԤbѢE{D"Q"BTHtWJ"r'I)G}HR)r Q**JQ[$"{JIST~}43y>=~^k4q-Cyy?~3g"00gիWcРA<TVVbݺu077߹s055*??p0 [nʕ+\1 qW]]5>۱C]]222MMMhjjRg}})Μ9?r-Ǐedd`)W?000իy㦻ڢ|YQUU]Xz5ݿi:GFDDF϶...9`0+ɓ4hyko:/22}ł2bbbxkM ajjהּ#O7�Ά  ???TUUa̙((('aiiH*ҥKqu^޾}K`mmq߇5޽{'P^߿ccct6mB׮]addD ߿555Ŋ+bxnhh@TT3njj*x̄|}}9mݺpvvڵk;-ʫ._L"GxZZ1uTJ <<v�>|롤ooo̟?3gjkky>!MIMM$6//((:%Z]mIB,Xf7b!!!5lXYY}s;ȑ#wVZ͏YGG)㟮9׮]RYYݻwcɒ%`0HJJBYYU|||`iinݺԙ*Ƶk`ff&5k`Ϟ=�hiiA]] pppאX[[ (//GHHكݻ#;; ٳ' 99eرc8y$ׯ_vvv|r񢣣BdggӮEDDΞ_sd2!//ǫW^iii�Ȇ=#8KJJ|jhh`ԨQ(((U!Bg޽ٳ'>+Wh1/$Dȗyyy\C*V$ !!^^^_"ߔٳgc$wB^^&})S#O(&&qৌ *++QÇ1{lvccc<|7n��ߟց@ϧܣG888ѣ|YVV`XZZR鈈{Eii)�@5�'ux?""_3ł7Ν kkkmۆYfq5?x?~> ZuPxx8VXAĄz_@^dӣGTBII #333hkk# 2@=*if6mZ,\6b`hhYfM6 ;w mؽ{7VQQC`P�RRR+WPaW^E׮]9L5ƺqFlݺZZZ077lj'(xb3RRR0p@c�:pԩG֭[Q]]M]# 99vvvPQQzFFqqAdjjjܹse @YYX~=֭[bZ0d@WWN⫧;,_!!!PVV⧓|]6l�___`!<x�(++�G!//X,0 $&&R׋`0p}�&c7o6455f o۶ طottt5nߟvq@]]6lVz)n߾MTcƍԄit1"##PZZ 3SLAtt4M1݋˗cĈ3g_WbPVVƲeP\\Lݳw^@垑iӦ!<<Ķm+zzz8rGUҺ:8p�1bV\I^rYbذapuuųgϨk-c~}/Ǝ Xz5ammM.ʰ-+8tlSRň#b`ƌPQQM>G_JJJ~2}~ 7233ann444}�^zUƃ��C�|ٳ'-ޠAyb+iiiZx~�fjڔ<(((<hq&>>70Vĕ+WEa022ذ2ǏtttESM{.1c * )))b濳fB||<~W^ҥK8v|ep c׮]Ӄ%.^(xxx %%nnn'dX,蠲2Ǒ#GJN||<jQQp1}~qqq044Ă @Ϟ\9~8Ɂ<F?K.a-׮]zlܸG||<:N:u 0a5۷o>~X~=9Bʕ+1uT'N&{�ĉʕ+h 2^w\hkkCEEPRRO2335kgΜx_!~~~022A]tܹsxb۔) ޽ӦMCxׯʄo߆/6n܈&G7l؀/b>}zG>>>YhmɄʰqFK.�/4DY,֭[555TUUزe ₰0}Bhh(zMY077Ghh(0o޼BxxxaaaFmm-֮] bɒ%BN>qQa^^^n )))t҅{{{޽_ƒ%Kh{,cAt{HH�SSSL>PRRŽ;I&ɓ'm.JDEEx!saѢEشiN<m۶9 _`ĉ044DDD,--nRϾ NNN$//ݻwĐzɷ�!R^^N�p͇" ohh ' ,‚GSN�g.]D�ZxQQ@^qOyy9:t(9p�O%;w=yHKK[nB<_Y7o&fffO<!�?RSSC]" ︸8KΟ?ObbbȤI<GyB:u*aXeSrLRRR͛ٺu+힄Z>ccc9䶆ٳg͛7B9}4@jkk!˗�r ! JݻwYt)"R\\L�O9_2"..N!;v,^(**B!IIId̘1\x�edʕ�y !oooC!$00E>~Hʕ+*ihh DCC$%%q_@@�ܹb&IH\\!Rܾ}_jZ9СC "UUUDNN;wd2* HFsZ#y7oٸq#-lT=|DFFÇR\\3g555;B!ӓGPO' xyy˗œ/�"$&&w�@=By@ H]]'\'B*9x ͛7 $55 ۰aqww~9r鑺: 8 M bo߾k^^UgK_-ǎDKK_gggZܹs%{߼y� EEEX@lllh陙???OH>}zm>|8M'qssk1.\ �ȋ/GYYY;Udd$#ך,99YqTLLLhxNNN^|/^M6AEE@i$�20h23P%%%jӇJӧOc2kjj(Mʕ+1l0J؋/hݻǷ\mbCCC޽{Tݻnݺf N kkkaڴi8z(p Ra…3MgIsFFF?sNZonn:|Ǵi($Ax0sL;<MQ�CÇ�3f`XƬY`iihQ'�/0o<\p@Ml~zz:'DDDyyy\~L&GŲe0n8dgg044�#==O>E~~>z˗#''x0vt�Ԑrtиhdd~PPPÇw&MےFfӶЀû�� �IDAT$=SN=:K.0`�ddd+栯^mkt?$bUUj?ϟ?GVV:v,]HLLHIIATTƀ```@YBD[ ^e䦯[Iݼy:::*++Qs6+zk}EYY*iAAF YYY`i;nBVV֧6RRRxg{loڐPPPЪVϾ?�۷ppp!ChDEE1a◖drd9r˗/iNW7op#PRRqQ߮]�-8 j>}:xzz ;v&_WWo$%%!::NNNq SLǏ`0h ""=z�7nĖ-[ↄ8@Я_6HMM&Ən߾}1o<XGH^yy9�ǏoUYr8ڽ{w=aj (((@jjj6dcnnhرcPSS.;w!22:::�mΛ7{.TTT //3g"-- CMM q6lƏldeerrr@vv6]9sj߃;ϟ#66sk&ݜh홗U&Ǐ?SWWG -[#33 <ܹ>ĸq(???0 ޽պst/ǏVk.;PPPؗ뙝z~ݻmJSeK\v6RVV֪%d2:=l>%WZwAӹqNJ-H۪۹m}KxnRpPQQ'S%KS�߿ŋCLL ]wppChV/\#GfaooNpY*d"**"%%o˖-� !Xb5bXq2\&|GCC;v@ZZX~=_MᶿGTT={0LZhtjkkK#չKә!nJѣG,ԩ eKܻwppp* Ъh_x,jo,f̘x<yG,X$DEEAKKK4ƍׯ333PUUECCك~޵9gRG}L0QQQHJJœ9s: "";;;={IIIиd""y6y_~8shaa{"22쉭ՔC\\נ.\&*emLQQQLwx霫WV=^ [[[tڕsS'OXw |] C֥KksNNN,--~7Ҙ={6?̙3JGPNPRRߠ陚Æ ÿ*]Ǐ#G"99Z999 mEro gΜ!//c2sR ~t O>"g鉊?lRXn<͛7s,6w~c jkkx͑o>,Yx Nu.^3f<ǎݻcÆ pttDMM ޽{Tŵkp PqvoooÇT>$$$0l0oܸߣի!##CtF*x***N:AAA3g _~O>ƍi&teeePWWܹsn:TTT g'ƢCs.\ru }}}{Ç aaaڵ+nܸ?(n޼]vQf`; !qa\׮]21c`XhVZQQQڵ r .\ �)Mɓ'< !!㯿46;88௿Ν;i^***ڵk �PVV4<==96aҥ�W�@SSs�Rqk׮Q222`ʔ)x(O:gX[[Օ"?~!"%%M6aٲe`2EBBZ<۲9HII,...ֆƏr[s1ydeAPTTc{m>j:IgȐ!ػw/.].]`pww̙3!##UUUA\\fk흔8;;fϞ ;w�OOOhiiAFFy&ۧHKK۸w^}k;w޽{addht*|Picc 6SNEYY?~%K\bccm۶666x֬Y&W^Ǐ]]] """‚ ڜΗ^ tqqqЩS'CJJM~UZZ߿#GBLL gϞ$%%{ ��\?ӧCJJ  E:o>0L!..&ݻ#::ĤIuVg+UTTpZ zzzHIIfjjj �۷?}tj6$'';v@@@@:y###$&&"00IIIHJJ۷o1f`ĉ�MTUU:uggg:u ۶mO|BgggM7IIIhiiQ&L@ii)BBB3hjjtR$''#$$]v㑕բ 8ؾ};N:xyyQ6cƌ &~ <<!!!X~=$%%aee{{{Z:-ccc*vAۋg``�N:aܹؿ?2(** ;;;qRTT>FE 4ÇVueeeN\k׮!66*w6�1b:@O-[={ȑ#x-LMM(`0>|8zMu*SKK  CDDLLL٦v--:u⫓|}NGaɒ%DXXeΞ1WSS&m�e{M _|9PNN3f̠&>}c}6ȳR:֭ ^xCq'!!)S`ӦM�)Sё2<{,-[FG^^ ¬Y0h XXX|௿tܹ5joN} x{[JڹsgDEE!((ҥ ?{ Aׯe .˗/[-]=]bժUG_IIIѣG8|0`dd(P@qrr BZZ!Bʶmېh;wF, pttl`!B>Ae{%==/ƽ{Zmy ~WܿJF+|הּK ܹɓ'cʕBZ&%%GM8.49? !B| >E?&{BII pLB}y!n+[B8IMMŋw65 @LL !**YB.!B4ڵkwV ?~Duu5&&&8u!B#1?J:ƍ͛7G'&&N![7pA̚5 1 "UTTl  III^WY"`=%|t8 D8AƟN:ATTC }0o<t666;kBS!D!B"DoG ** QQQ(**"88˖-CRRΚfB"D!Blث:u2 [B !B"D!B>cÆ 6m=!�P!B"D!_=TUU'?�0++ +WѣaffC~~~ֆ-N:E^UUݻwcƌ1b\\\PXXȑֹspB o޼iU^߿UVapvvƭ[hKJJ ccchjj1fΜ@V^tee%֭[sssZ`08"""ʩG||</^ eee… _o˗/cȑ#իi+gΜǏN:k]b0<g津]Tfyl۶G vU)***P[[K 366ݻS,UUU;wn֭[٬^&~͛066FqqqgXPVVFLL whǏQQQCtw̘1pss)rrrZ%G_ϟYO7�Ά  ???TUUa̙((('aiiH*ҥKqu^޾}K`mmq߇5޽{'P^߿ccct6mB׮]addDu߿555Ŋ+bxnhh@TT ???q233add___kWjj*._LruCKK ;vk׮ t9UUUذa8Jqq1Ν {{:wvZZ|2<==!++kzQsҮxbӦMݻ7߸?|l|5u&EGx?:, a!Sp9rg= II6uDDD ** @]]zzzBYYLLLp޽ͯgc Fk׮_*++{n,Y III(++É' .. ,--ѭ[7AVV � iii\v fff`2Xf C҂:7!!!7  Q^^ٳݻwGvv6 @ӳgOrrr-ʮcpI_~ ggg-Ƌ+ 9kjj��:::SLurrrpi3/^6ڥK(̜9oބ|>wJb WW]pkGiKUs`0 3gΤYma޽ٳ'йs/K!lJJJ#F0騪"55ݽ{<xgAEqq10k,8::… 8}WGJJ ;ӭ�� ##JjT~a̞=!566Çq �@xnjב}}}*N=GgYYaiiI#""kkkݻ�@V>}S~nCDkfwΝ kkkmۆYfq4ԠO>-accCݛvd2�7n+!mmm^z|>A`mmmKKKa~|{쁙*�@DDz>PL3 Ŵi>yއ )YYYHKKk~ ڎvū7͛7M6pwwǨQ7oR_~@XXX@II X|\\\ ssWvލA}O5 ӦMChh(vvv�G8qf͚aÆϞ= GGG_Yu -, 8tF|ڂ`LܹCٌ38&i3طo\]]1b2ë}eFFF***Nd2  g 22Aǎ3}~ 2>>>ũS~G @bb"VTTSa999Xp! EƮoO=F?^*yRQ"Aʡ7o6455f�h;طottt5QGXF6oL3z*젬e˖Ѷdgg`Pem6`ӦMԄ!9V6aeeE _SSCc "7==vvvPRRO#rJy`08w1j(DGGי3g0yd�G4DFF177رcsNjhYtttd]]lll興оϟ?xoIQQtttvZTWW999}vhiuu5U:1m4ضm^z@BOOŪ9DDDЀ˗/st� �-~\<x��:t(�˗FϞ=i L444{4-_~�@35mJ^^п8cOu,)7^jjj`ԨQpttk ++*0LCEEEܺu 999?~eDDDp c׮]Ӄ%ǞÖ )))pssN8%KbAGGf]ϧRo-w~ȑ#Xx1^ok;jv%dbȐ!իWcԨQ ,cǎ9^~ QhkkCAAwƴiСCccc鉘(**[YY SSS444 00زe 竬Ddd${n~K,ann.P())LJoT>#F>mK.<qmyzz:ƎJJJ0774)[l\\\F׮]0n8޽CFso6vvv`XXn@Ν;5k'NÇ{0a\zoYBOOpqq5k`hh3gpܺu Çs֭[1}tرO>ԩSii"~ƍ1`�Æ pE,_ӧONEE&O=z`pttįJ;ڵk2e �---jɍ]v_~?fϞ3gRZ>}ƍ~7'm/ܜL8%͔ qqqprr֭[!%%EP400ȑ#CCC]444( \ ##+V_ۛ;<<<{ݻ�p1ܻwKdeeQ322W`ժUBXXu놹sRWisPXXxxx ,, ҨڵkpBD8޽{'_LL /99|K_N�B!�|(**P2|`*,((hhhp=u@*++yҥK�)))�իC]]]sNZؓ'O4u!yfbffF z*$Ν#dٲe�9<444pN[,3n?}Djjjh^|I�CFZZ@޾}[ٳ͛ !>}� \qqqK\rB%ݻDAA,]xyyB)..&�ӧOs| x6Ǐ �rҥoK;jNkߔE7"VVVdݺu\e|� iiiB/׸Ɔ;w� -歹}}}RWWG9x >|8i~ G޼y� EEEB͛G6nHKwTq�� �IDAT򉌌$!;vKRN;!.!ԩSB8@LLLh:)&&HHHw $9o###wC�ϟSar} e5IN Hqq@�*ѣG�)(( 4 ^u]eG!gϞ$11Kb!/:"..N"""  To)<rӼ.???B!YYY�Uwz) , k֬~'FFF999R]] w 4ԯžKs �RQQA]xV-�~###� 'N ={$/^.[ZmffF9"P$� ٷo!^zϟ"))Iih+^w]'qvAh%'' <iɉ+Ϟ=ŋi&j[n�@[M={utxyyQax IDuu5CSXX|ӧ�@57}clMNbʕ6lߋ/hT[WW?^^^_dرc1k,L8fff ¢EhuuҥKpqq]j麨w a̙;v,Q]]-н< �JJJ:t(5?c ?~, ٘5k,--:Fw &pɦ9-;q7`0G7nĔ)S(555`XٔǏ;v쀁ZKrP+fMW*]`ӣG�III=z4-NNU>Cnn.eZΝ;cx=F:::4JJGA7�Bhhhr[ׯD%***h߄|+f]t ##W^}5sPnX,>}ZҴn ZZZlO<YYYtXt)=^<̛7+WxZ! ׯ_٤ir Zyֆ D9r$dee[%!!A-hm}56oW�At֬YS?wڴiؿ?޿G!))[--[0|�ajjJ=kZ󪧂ЫWv�}2dM(&L"ZR0LS#G`ؿ?9D߾}cJJJ0n8c eeeo׮]�s| O>TX]]<==qeL;Fߒͦ$%%!::NNN)S`0㑙Rnn.a$5׿5 ?~UeU[[hİ;>&L@NN (((@jjj7~ﶪ ۷oAIkQsxB-u/��nܸ :HHH}/_َ)S&٦miGbǏ|i+Vٳg &&χM{L&#+^onq9mnn.훠LkuuuHKK-[#33ϥzЀ6Օ;[6PPPp«:;;Ç7nUUUܽ{U鈉a߾}8}4z777XXX< SNy'nܸSRӧOc^r . �8q"<==A[퍳-_׾Mq#`Lغ͸qлwo\zϟ3/oѣG1{lZڵk[[[BGGRW=A~ ~xb!44988СC.\ȑ#i3GLL JCCrrr8{,d2YfQa))) P=6}=(FAbb"VXA\X,!66qqq_+Wgᅆrssi;v쀴4rss~z2M)߽{TEg2QXXӧcpuu帟o~˽{ bnQPP@VV?~LxYYYRYYY̘18y$F ))),X�III{�pY444ĄZӺ$h;@I׮ p|+W@]]*1b$T.]̤={"((bbbpvvVCy$''ڑ0nȈ.�x�X[[c߾}ҥKi4V^9!''DZ?M ۷#i|co)7tuuiBuիWaddUVW^-vs^~kq[TT| �=r Ғ+6Ҙ={6?̙3JhT>ΈӧiΦNxBp%14:IaR!** MMM[͛QZZ 999lq�x/vij_lc-Qq-8::bg݊8twsm YUKwz#Q[[ #66xmbee}aɒ%ppp7opJ9\x3f̀5deeI;v,w 6555Gtt4z SSSCCCHJJRg8q)))T]v~~~ 67nb(..Fvv6zyyyO<AΝ1j( QQQgϞԩFc1bDEE\r@UWWܹsn:<{ 3gDmm-LLLhf(ʨy[nׯO>!??RCΝ1h աڌ~MHHH`ȑ<gV\044D~~>>+WϼڵkT>3f OEaժUŮ]J+wkkk`…I)Mɓ'<=P]]n444 $$&= X; 7]mWGll,LMMyϞ=CUUͬu޽;444p%\t ��yyy$''<x0G3g΄ TUUQTTqqqݻ7<'R$ɵ?\\\0k,۶mkn !%%6+q_~-[OKs8y$U۶m;lll;Yh^GmQWWڻ(gM)))i,LAs1yhVWgٳ}sXg]a|rTVVBVV&Ə2ٙƫ ---Ƞ7oޤy$'OСCԄ$9~~~ٳ' Tܼy!!!mzW^ҥK544`mmͷ"-- ߧgϞ$%%1~xnnnAVV,XS&/_ɠǏCQQ@'''z6miOwTUU[n޽{+V 11W&MŠ+cDII JJJ<H+^[.?�0??�1 rqL>RRRGPP(}d28쵃wh<x&M֭[9[BEEǪUP+${ܰ}vON͆`ǎ hkhhɘ3gN<80 hkk#''zΝ;CUUxYjEjAA._󺒒޽{cǎ())gɒ%��VVVbʔ)57oZ}P۷ԩS0112fxxx 0aCRRVVV>;ؘ c+^+_n}}YYYHOOz_oG@̟:v*PUGZM^>}~ ۷q!==ګ`bb]v!88ݻwԩS1g^sssJl޼jjj;w.G :G>aooSn>}p9VUUƍMl-ZZZHOOGXX"""`bbmmm+\m߿?ڵkQ௿tܹ5joNksh^G:v(PnNgcݺuիeCU__l8:: ?n,ZQӶag>999̘1OKK ϟHL4OL:x-,--m۶VU~iӧO<x0ߏ۷oCQQ033kU:ر#6oތ";v₨(ϟo7 uРATXNciiiٵs=zÇ FFF(DEE K.044;�kĈ8tѯ_? ~رظq#$$$dU7-[@DDK.8)@!C`̘19sf'?RW= '''qeZnJอ "Dȏ!&M&4SL>M6͛74Nł>[!HCKK +VrY̝;O>Eǎq?EEE-QZZ>}իnEXx1ݻU*^իWUJKkԯ 顲cɓ'O //wb4m!OJJ{[E? !B|/ `899qx{ko<xׯ?~@Y_db޽PRR$.\�&)Y|KdggڵޙSue-)iJ2J$ٲLBiM|%-EJJbldJM0AIQd)~ǽd|<<9>s,sΝ.0x%F*" bɒ%}ys<HNNƲe˺,00053�d```DLMM1qDdeeA__&444 88?.]D3]m/߿GMM  9s&?z6<&&˖-뒓>, .\@ZZTUU?Sr\r ]J~8:: u?:O;f{!xxxt&? (C'S[[! Fw ]aE^Е0?΄10000t&�?3�d``````````` �#|4Y033éS@ SQQ???`޼yWWWСC;w.B._%K@CC(--mWZ<xuA]]θwͿ[n1r?yvvvطopׯ]ʋJlܸ4 X,Pre˖AII õkׄo7n@]]v0G^XX7oQ~wX,oWdW\WwMMM@cc#̗*~ , ?ܪ~ JJJ033ɓ'Q__o'PSSNF~z^ƣN}s !puu,++۷<{,Fɥ_ZsUX,TUU}[t7oW_߀fCII QQQ|õ'?->_�0==9r$`nnWWW?~ S]] ;;;:::@xx8fŊȀ-K̚5 _oƔ)Si&<x�xPi}ѧOO>022UUU8q"777DGGcժU`|e755!""699~~~|ܾ}FFFغu+S7nPӧO)+$$6ك#F`sPI{-Z{{u@$𥸸(((t;:ыEEE<OALL {n̘17o//N'fܹsx:aii٥ vw={<L0nKs/O0wܡRYYCa`X8<޼yX>111l޼۷/ ++Kݩiiiܹsfff= [[[�6444p9888LammM6b8x = c3 !n 996e_ٳg~6ýz Xp!nܸfH\q.DEEٳpB!dff"11&MߚgϞׯ_/551… : ‚ 0`F/666 .DXXoʔ)1 p7EEEɁrbv$ܹs$~R,X |rss!//oY0߷)@111 ?eddPYYIO> Ўj766Ç� FPSRR?]ˣ]ܿ888_~7o`5;2� �`РA�>߷o_aԩ]l66m¢E`mmWޮ]0|: <_MM lmmA.jkkm۶ ?PPP{޼y0p̓",,,L{=zfffA@@�e&ݻwTx???())~0gΜ~ccc>}W p]?776lTTT鉗/_d\t ?,x޿KUUUBŋGGGlٲ7n\WY,._ !!!033:v܉J!:lقEyqwYYDCYYk׮y) $1�P/BII s嚬)++͛HʚX̟?cǎy02͛7CUU/^? ̙3AAAPRR deeeee:tHhkQ]]\~.c<vs[nbhT577om6ܹ077Gllg3!''lX,.x1ݖcсPTT9O6mڄǏCWW<… VϜ93gBCC>>>\Φ *˱m6hiiA[[3gԍm}atƱceee[!+WijgϨg;===�*߯^9s 88&&&®]K۷CXXUU҆`֬YPVVƚ5khہ.^yԩm1!.\… kRs[nɓ1k,DGGɓ'X~=444`mm7oRϴEDDpܖfŜoBtt4͛eee^Ϟ=CTTΝ UUU~ֿJII… ϫ!L=  /n߾ sss 7nX 2�:8Vcƌ�@ZZkyȑ}6iC�iKrrraǼ8c̫":{PQQ#\iiim"%%BBp=dffRJ<h"""?all@OO\{ۂ$%%fJw1//f'pX~=TTTYYYhkkShѷo_](,,#ً LMMqaL8=zojj͛q};v ${�� �IDATfeeAGG "6o=bcc5k`˖- ͮ:u ^^^Ν;͛6rΝ;acc={簲B]]PRR4*իWjv1..4]ɓ'ȑ#PTT9u luu5Q__;v8|0-]Xp!:W^amF&eظq#&NJ 탵5v7τaȑGXXǏk0z@@@�m5z*ッvN,Z˖-üy󐝝 �:ts̡ 6۶mìY`ooz.իW#;;Ŷm0|pHHHP>>>~:\\\`ccx�ѣGXz56oތWb=sWjkkaii7o`۶mpwwG^huɓ'/ 60h sss?GAaa!<<<ÇCZZذa,Y˗Vt!HLLĔ)S(7ooo ;wM/޾Mʫ###fXYY! �1c8�4[III! �&&&>gO>pUVV"""طo>|#FXt)|}}qYڵq�W*333f̀!BCCaaaA-Bz%899o Epddd�$33BHyy93G&AAA\MMM䧟~"/&WxTVVMWjj*@h?&�ȭ[)//'cƌ!!!!|efd޽4Oiiir=B!(ǩv-N._Lʕ+ �r*Luu5ijj)388=<RWWG WRRB�?KƥK�k,X@oN!$11� <gff޽{|ܼyBȬY(yDAAXx{{By@?ީRl6$7n>--� %%%=M]@ƌC?~f:Z%l۶fT{� .]"՞O<IƌCl6y&III}}=@Ν;3M2'22`�@!dʕdݺu #4]�$77+_Z꯺:һwoO!$$$̜9H~۷ot=w%�cA29ںu+'N}}}@q#466˗/SSSS/FXWWGLMMӧ !<z� =M��DMMdddt›}x{{W]@in{%}aZ:;6M455ITT!ZpK~~>ihh {&<B9y$巉W|K)7zjwXXӣSt#!۾:.{5WZsrr|$GӧB{[߳g֦ٙ'EEE49Ͼ!NUDNN\|r%jjjw5 333#~~~>S_ҹݽ_v� /^~"++ftUxx8#555\~!xճ .=jɉ|SXl|}}գ}�mh6(..9 BJJ .^RRO>2Ox G=�f^?8&f;sΥ_xA}Ѐޝyɘ?>f̘333bҥ d@sNMM+8@/**%h^xÇ'OF\\8q" �3f >|�;w.Μ96t̟?DCCez" pWrbAOO2&ҥKXf LLL:{�cǎmSʕ+vZ9r;?MMM8<ik'%%|455%%%7nz-ZMՃ9sĉ£Gpy+>|8u]!g5Zӿ�2[[Jdggc aW2h:///O=zСC- 9uuux!0m4I`وZaZ'"++]'_w%7҄׳gOcŊpvvFBBDJJk{E{a``@YBpyVff&M&tl9nCoSt9I0 8 2_Zc„ *^:uٳgJ^`jjuR|Kdeei}JQQQwIimHRRR(..9[ddd```@=yճ�~5jm(MF;hu2 N8A;teȐ!(--SL3�g/_5Sn č7H3-_i:y%ϟGdd$+fϞ'ObADDSN۷ʂ5|||`hhnOMyy9u�Μ9Ӯy 56m233QXX$BCCGrrgaɋ=z믿мod˖-077Ǟ={hr>b 3?l6~ -n3A CϞ=I)S`РAu\gggвU[[eصWixGd͝A̷EEEM)))q]s=8;;cԩAII LLLF/>|Vӧ!** Eu[⋋odddpQRRҮ=}˃׾<~쌇bʔ)8qԐۡxK޽-));who޼iWy @!ajOڅ?檪va'111&&wƷKwDw_6l]�TTT`ƌuF~u_� ˖- &&FwppShV] &V`oo(&hZDDD`[RR!gː!C`ooBUVQ,l6~~~FLL ,Ӛ5kh_z%0455E۳g-[^ $77TE5Bয়~?/GVVÇc4<yr{Ҩ;w.pYCJJ /mmNu͛�� zzz3f �໚0DDD`ݔ;?022A1@YYY׏Gu@U͛7z &&GGGԩS<7 &… v999=h9#QǏzN؏Ah:+++�p->F\]6+s9w�ܹsB޽y ł85 bh^za\W6_ɳLXXXP?^l4,X3g`*//Ǎ7Ae{/ǎ?Ó5,=a#UR999<,= 6 {F\vͳ:B߾}nq^w_ZZZظq#N<۷g~|q@qqq<2|pǏR^ 1w\X[[CVVvɓ!!!8::򈌌ĠA4spww!$%%{bccD9p�6m???{JG~3Ul6UUUx1p@SlO>8TTTx*,,DEEQQQt|WPQQO?qAYY}6<H>hhh`ѢEظq#aggz̜9T<JJJ߷o_իWHJJ‡hf`߇8FQ'ݽ{Ä &9g044D^^N>5k�ǼΝ;T:[4illltR[8p�hnmm [[[,Y�`Μ9^ѝزe $$$T" �@s' ,, F8=J=7dlذ֭7|lhjjN,SWWGXX0zhXYY �сNʰ>>>prrBMM פ]98}40uTV&OL4rrrFSS*꠭-vڅիWo߾ѧO.st^>|6m;w0b2puuQUUч�R|wرc͊Bdddҋu$gp2n8|qhhhs ..cc㎼gǨQp1W^PWWիaggݻ7ͼz*֮]ٳg˗Fttt$%%ٙ`bȐ!]OOOhkkCFFeee{.Oa㑖Fvv6߿f`Æ 8;###Jdddp *mmmM6 o޼'O|vKK:3 |(/_Djj*Ͳdԩ>}:aGJJ Xr%jkk!++sΡ TUUѫW/?FLL :-aW.]ƒ~ŋ III'w'`^^u_Y3gRRRC`` Ǐ111T;88@BB8y$LMMsNg+UUU`[zzzHJJf/OOOڳ^P!سg: ] ###\p?Ξ=X, 33zqqqQ{.^Hl^YǍ7+**bܸq4hz쉢"hjjRa8TVVbٔǔM1b.\ݻw#>>3g΄7q4i<<<L6�8x lIIIXZZޞ.ÖE�)p{{{dgg#$$zzzHIIVbVZcݺuPTTļyh^zyyAZZwƫWHKKKrrrjkk#%%Fhh(fΜ 21srrN<jq̙ZVVVׯaaa]v:_F¤I`ggGݻ7"""e`۶mB �`޽pvv vݮ,444ZGe</_ݻ555L2#Qttt0mڴGff&[cÆ 2£G|rhii077۷oĉh"555ӇEGGs]y^#G]\\ 0w\d>oʼn'ѣGc߾}|;mųqFŋ8usٳ >^GGGDŋXr%yyyܾ}?>FYf}Dg!a*8"""777 =Xr% 0~&&&HHHC"!!;v@TTfΜ%Kϩ%%%GӨ""" **Y �YNNNɉ˃3K.RP*Ƙ={6VXI ͆>?z+/^`С9_VV֭[<}ͥRQ vڅ4DFFvuR:DJJ -[4)--7|`ѝ"K$ �/_,Y:$zkסpZ�2000Emm-;EEEHJJڵklvF=z 44...Ծޟ+'OD~0tP<y.e`込-nlٲNC �3555?444`̙yB@ :ppp޿Cãۜ֙BЀ`<~zzztƏIc`B_2e ޽^?w9vNAF III_ׯ42[b111_n ҥKtҮN C71u:�OXE/1 ݍ/n f��200000000000G"iiiXf affSN~~~yO󯮮ơC0w\(++\q]|K,QZZڮ>x�֭:q=QQn ccchiiB o>֯_/Jlܸ4 X,Pre˖AII õkׄEgᅢb<׸qi *[+++ub?h3.A5iWm^xyyFX,$$$ݨAuu5m׮]vbw`uVW'<^7JQQGrrrprrƍ:zn1:ԦݻwallgϞ+~6 %%%DEE믿b-{TTTtu2abb+WtuR �akk#Gpuuǩ0հC~~><== SaVX /_ĬYk*okkkL26mƒ`mmo }􁯯/###_UU'nnnpssCtt4VZ6WvSS"""!bd sma֭\~7nPӧO)+$$6ك#F`sP2gmmmdff huƍ􄬬,; jGHW[SXX___ 4H` xyyuu2U$$$pȑ#WAȈ=�#" �Ç?: fܹsl!SٳgÄ >: ݑdHJJv(o|T|q@L0w7|CUVVСCX|9X,Ο?7o 66{| ͛aaa}\jjjƝ;w`ffZxyyѣ�hkkCCCΝ<xشiX, Q^^ѣ@zz:N=3`�b֭TGuuu_qYG޿z Xp!nܸfH\q.DEEٳpB>dff"11&Mߚ*sٳg(,,סCTjjjl66m[Gp jGHW[kBFFo8V\uAIIr_,,X� h󵵵oH#۷o SS(**BNN?x:z yyy|/)))\p!_ m ��222FOƂ �l"C�aÆ>�Y@^^0~|  *X[[رc(++�Z'�틸8L:""ӹ^h۵kϟqqq.: <_MM lmmնA.Sjkkm۶ N+sAp̓",,,mF{=zfffA@@�eRݻwTx???())0gΜv)?~<BCC'Lٶ$-- .]j~ ێZvů!5TTTaʰyfj$22kҥKdee� j*`֬Yؾ}{f18}4V^ 888ݻ�#Lmۆe˖[nbhg~{�� �IDATɓahhH,(z͛7ZZZ<eF6mǡ WWWPWWǖ-[h"$wСpwwJCVVIIIPVVơC ;>UWWii3a\fΏ?ƒ(L,YcǎҥKOHLL=vZ|K{ɡECyy9ohii EEE�ڮí[na…PRRʕ+i[Zb=Ғ@탮.tuuL /ܔ,\s-aCCBBB0k,(++c͚5(--EYYX,._ GGG 22f2 4K:uJ`�zxqUcnnɓ'c޽$9ЬñyfR"~tuudCClmmPо7׻ww>U[@s3gabb---ڵ /_ľ}`hh==vgg �yqmCDDMMMqWpȐ!�ǥ��0f�@II fvG۷oo8{[ �4SӖ@AAÆ +@?qqq(((@OV]]޽{UUUёfYWW4*[S[[ O{FtFFDD'q‚Ǎxxx )) @ll,/_6 ]]]TVVRJaaaˣR w5Aee˖A^^GQk+a'''5jvixyy{VWWرc,--ÇSaSSS>|'ND=PQQǡCoׯ !++ mmmO>pttįJuqqbѢEXl͛lP2лwoܹ ,T98p�_5dFX>>>~:\\\`cc�8uU;wě7oꈍ(ӯ#G">>aaaHHH*?*Μ9EEEۛ^t$''?c)wtttw7nRRRha*++sN`Ϟ=x9hFFFmP$իW#;;Ŷm0|pWn0zΝ;={6ߏ&hkkSM^j$&&bʔ)owww\|^^^i}afffbƌ044Dhh(,,,hވv )))ڄ&L�oooB__.]򐚚 CCC�뗦&�Bvvl&** W^ŪU!!!شi-… fqFL8Q>@UU;�_q}j{II (W^ ݙ|N+A5111(,,<<<paHKK6l%KzNɉp}V迨((߅ ȧ$##� B �=z4 rojj"?Yx1H455�oRSS �RTTDs1@nݺLyy93f +5ӧO'{=}HKK{B߈@Y۷o'fff4[npre@V\I�+WPaISSOdm/>:;̅edB �R__wff&ݻ7)))'�͛7 !̚5KȊ+7!gϞ�N󧢤� Ga'O�iGiOU[r"//Ol6!wYYYD'�ȹs!3gGTTׯy-!GYsrr�%0bddD<HfҒlܸ&Bȵk׈<!M\\\h2 mˋ"\ mh2 ȇ0޽#|˃B._L�/_򌿽466˗/SSSMp!ʕ+$**yyy>n8� .$/^4 MMM\y#Bw۶m/^LRZyBHYY0`�IHHo~l6$QQQ@zMBCCyUy#HѾs>| ӧO'~~~B�= hy]TӧON(}Ӓ'䟺ZGSgb 3׹s6.III!�HEEvuZ[ҞF]z� dTؕ+WuQHXXPitz;\tOhٳg֦=wQQQQ999/z˖-/rh6 (..9 BJJ )7III<}$),,D^^s�fZpf69&f;vދ/h߿/0_?ۻS6oO<nj3`ff@,]{ۗ*Jjj*\]]qNm42o/^ðɓ-((ĉG�PTTĘ1cC�ܹsqlc@dd$(aV;>..ӦMC{QkjW۷8q"^\\b7 ӣG3wޅ.}L8x9PPPcrŧؼy3n޼)j?łe=n8̜9ZILLĊ+hӂd+a=/Zh2hEEE())[�('xNC">|ȥxaeekkkaΜ9_ &&XZ\{}[&6Dܿ+-ͷt%7҄^yٳ'b 8;;#!!{Һ󊧽z@TTfffZ I̫m񓫣 00f &@VVMY600@NN(}_´299}?�\c3]]]xyy k׮6vΜ98q#?mCw0pvo�_~ 5WETTӦMǏiP[[eJ8qv8Đ!CPZZuCQQLBu朝D8p�?{isL5447n@`` _o͖?prr={6<yaL:o滇'++ L2ߚ*(//�3gڕWҖHHHPiӦ!33HJJ.444PPP|$''w{OaTؽ{7R_(ھΞB~\̜w>ڒ?D 85kV.ѣk�OFEE;tH0^tT&'vr_@s=n{쌩SBFF%%%011+6O3>}@UUHMMv5m_SS*++NVgϞvyyyPPP:_=rvvÇ1e8qjjjmW<_}Ut^ʊU>|&#wذavPQQ3fTUU${Xj.^, <5UPͷg/> L8Ô)S0h ܺu W\3׹ݝؿjί 0I?_� ˖-N:Eݽv&L@LHH=Vŋ)ZDDD`[RR!ߎ;�4 C)5Bj*jfшZf MW揦&h{쁴4e2ZK)ؘ赵QXXOpssz^cc#YeΏ#++ PSSv6[4<yr{Ҩ;w.pYCJJ /AT�pE455a̙\~-뒰�m&QPj^XL*}Æ C޽i{ Z$&L .s^^0|pjF}qqqqHLLviN�p鈛7oBCCLݻ2N0^Z{GeReāGwO>|4}t%P?h"cȑڵki3hW둕EG{N&FDDǏos[TT&&&\w{y7olWdff‚*AAZZ ,3g0|p�&!HMMĉ;�IfggѣG 4 ---lܸ'OQVV999lǏGtt4VX5W8yǯshySAAz#FFaٽ{ݻwsu+&&GGGԩS@s⋻Ctt4NgǏR^r~:Ν kkk۔ɓ'CBB>>>pttD]]A =CCCHJJRw")) slڴ ~~~x~4 kɟ 6*<{ 8p 幎)šSVaa!***P\\ 㫯 ~'7۷qAܼy@EqF9s& EII | {{{z IIIhjj2`yyy8}4֬YS;wPl{ҤIҥKn:pss廵5lmmdʤs@MM !..#Go~hjjeӺ.µÇcӦMsF!]ݾ} ߒaÆՐ@ '''pM-vڅիWo߾ѧO6luf7 ;;ӧN:---HJJ"==lBhcFeHHH@SSHMME@@�`ʕزe """hF±cǐ^z 5y"lGrLJoy�̓cBO<w};v]EEE,--`1>|@tt4z+++�ҥK1x`L4  @EE;%K]ł !C@\\UVAGGRRR:u*<x@ӧ\\\PYY YYY>}ZZZ:u|IJJ3͍_>455ڐAYY޽K;Sx>}ڦ`HNNݻwq˗/JkWSSXd <<< &&FZ",.]ƒ0aŋԄ$Nӧ AZZ/^W&kرyyy\3g`899Q+[n/_ HUWW;w;޾}UV!!!155ŪUɓ' Lcws_񫧂NN|q<Dֳ gΜ @x{{CII 4Ǐ111;88@BB8y$LMMsNPUUErr2n:!)) �1qVH<==iϺc|P!سg>B,^SSFFFp~={111`XAff&PSSNrx"5ze5??7n믨qaРAٳ'N)sA1.\ݻ3gۛo3i$xxx6mqAlٲ=-yƔG<+hjjR/_Ǐ ,۴4ïu]#yOCC}]IJJB[[21mds?�rJ'OD}}=lmm?P|7HII޽{ ޽V^^^ݻ+bҤIEϞ=}v<~'OFrr2?- {{{dgg#$$zzzHIIZ1c =z˗CKKvRi[SzϏ12 !!'Og�>|k5:::6mZꫯxڵ rrrׇ?sĉ>0o<XYY u뿍 u2ΝK;O[[W\#GSSS>`eep~صk'l~J^zWȑ#4w~|8q1zh۷fffG��WWWDDDٳg044ĕ+Wڵ%ԑ#GRn=z@pp0ߏ7BZZ .ĭ[+%%GӨ""" ** QQQDDD 00nnnի v+`ذa<W/eee:u СC~O<۶mC~|rU/;v@DD+V�|ro>QFaҤIkn]W4�d999'''.h\.]KKKöx/BLMME;4`llٳgSFee%?Qʺ/^`Сرc͛gj?e,LL&JQ)T*J.-MH%Jʖ,m"X[$$AjDh=?zϷϽ-Qx|9s>s}طo,,,:;yZ0p@ܸqXHKK#<xI+a˗4́JCCrJ|]t zٳgPPP�CHMMyA"))M7[xx׷YX,,[ //7o ""qqqO£GpeO֭8�ju %%ҝv8/×}y_r<8w;->`ԩn b` �:3333999044䴙/w_ĪK{  ?�+++888P{SSSi&U�`ٲe"{#9HLLg-2&>�DZcZ=;3ݳ(6U5ʴ3Jyah iiiͪw>ζ ೫7~ -~ŵ{>{ _6�銃? T>%_9 0@ �_W9�Ȁ'`nnh}ׇN8A{^UU@MM (((… Xd 6Ç_gggܹs6m tuur?}عs'߳cܹPVVΝ;'TֵkנE;h:Xb]߿(Smddt4/^ZZZ?>l O7 A奡IIIptt*peY~C?ŸZG^UTT`ݺu>}zf͚VQܾ}&&&(,,$kjjPUUEstX,*++?IӧOcϞ= dee ,��TVV"!!-*ͱ~ur;'''[Ѭ^��---BJ!*++͛7[yq(**ᓤo߾d�'O9TUU~m՟CAUUSRW055ŋ;;) �333amm EEE`puu޽{)?UUUE~~>KKK:t|rdeex%,,,?P~l?ׯÇfCW^G^`llLu+++1f444 nnnHHHʕ+pnllѣGXN… ahh]vaȑ066Xh ===dgg=]FZ2򶦦|鐓t)�XXX &&Fh^2 a%** v킞oyyyL<�k}?ooobkҞzuMcӦMAA ё�� �IDAT!--ݪߎj{1b͚5L~W_~Z�vDϝ;G'O�"11ضmL 6 00e88+y{Hff&>߿?=zI䷇BA]]1aL>ÿ/zWgT]]3_***KbԩSx5T6l�KKKTԄ annZY;�zzzFrr2ZMkXXl6֯_####,, ;L : 3`�aӦMF޽Ñ#Gpq^yll,xb�1o?SL-,,DAA\}}}>YPSSk˧Z(((d066$֭[ /ʴuWFXy9s&ϟO!5uTdgg#%%cǎ&?iҞz 777 33S`ժU<xPf с"N<s[Ε+Wyam;�IN?־`pqqARRig 555g, vjLBIb>?vvRDBJJ gΜdW(!!wQQQA<yLǸu�`Ȑ!53YYYˣܯ_?888íصk,--xfg� ZUP{FRR&L�11,))۷oSr8߿yyy>#Gā$0W^ 5{KKKjnnnw:+((ah+###<}YYY-TAYY|í{077>)_~@UUٳg)ω"--M;@XLL rrr(+##ϟo\:Q/mW[nܹs[`"88 <5 }6UkyyyJEQVV~!%%PSSêUKoff&X,͍7~~~ OcǎjشivEq´iӠ P!8s ϟ Z EEEK.f#** e"L]]]\pӣVxbbٲeظq#nح[7,_[n(8YYYBMM "kpyآCꠠݻ+͟çX,<|rƒ%K0b/ϧR"B-[ôiӠ ___rsܽ{T644077*\\\p DGGcBҾAll,fΜq!((gqQBB젦">>666Ѐ?McǎfcԨQXv]LKK󡬬LZk�ѣGXr%F l޼߿`ggES5y&-Z555Tn݊cӦM7n,,,Obii*))aԨQ9sH[W"OgXpٳ SSSb֭x%v ###=;n�(7obCcc#]4h�sE >@SEUTT͛7(4Ml2224~�hͅ "T>�?.ɁRRR:p8x1֬YyyyXXX 44gϞڵk|'o !s[uu5⠬I&LJh<|?"Λn݂ BCCa``�KKK=-ARSS///;v K.IPQQA.644 &&yyy4Uʤ.PלZACCC8::BAAA#^ZWOnn.jkk1l0ի1j(@VVzzz(((@^l29r*uuuػw/]QQ ̙3۷o1k,jH֮]HJJj5 vvv{.\\\hr>} ///lܸ111ՍXxzzb֬YFyy9LB;v yyy(Qd޸q`aawww999ׇá 6d1RQQ'N@LL N< ݻWdwޡ5j-[FG7ѣGt닰0hWΝ;ѣ{n --#&++ͪ<<<a\t sΥ-] Ɩ-[={@GG}1JJJAt b8iiij")) wޥˆ>|+WDpp0rssoQEEE= ܹ?<.\_~8~8nJXf lmm}v`�& L###8p�n {-f̘~!""˖- <x  ݻw)Uׯ̙3 iii̞=֯ ajj9sؾ};444`ffgϞhz0m4466bΝƗԿj,Dbb" ///޽222ڵkdޱqrr"|כ7oDICCHי3g$++� لB �駟Hxx8{cc#YhYx1Bttt8q� BuUܟ<yB�7n)//'Ç'QQQBe2ydc[cc#ٲe @]/^*� '{nr rITTT&uuuB8(cÇݻwD@.^H㉙QPP򸰰 0,\;w=zL<� B߽%͛G6oL!$%%�ޛ>;;ٓP �ruB!%%%|rC�vs"I~'ӧ�zjSxiKڼy3177o_@8fllL¨{CȺu!�ѣGB._LHMM )--B)++# 'O$A�Us ͦر=p8BqqqOKKkON8"rrr… ZIEk2xiLpBGs[x1U!˗�M@.\@̈zƍСC… ɓdŊf$YYY探� ɔ_E�|BHS &!:::$>>r$3ؼΝ|}}ub |rdŊ_ILLL={F�Sn_&={$999ǏFP/_L�/^Pn&TWWYYYJ=+/EEEr!"''GKֆq5樨#G,,,h߬P$88X[[˜�ÇdTYؿ?144$b+ae� ϟ'm߾9sFqT|||hxNNN˚2l#ptt?лwo�-M+YGHKKϞ=YSSR //?�"wfɥ1blll(/^?x@ ӧBc)<qqq,]3f)Sxb 11+b 1y+..NSɛ5kl6 0{l>|8v&/ /^ݻakkq!))IZs=z1cP�Ç��Ł 33s΅%bccQ__OĈչz*\]]*Pu7)) 'Nرc[zKKycƌSk~b```@`ڴiGJJ /_NSnテUfD[n1c ''"/ RRRGcc#PXXy=`ff&:jsEf؈SNAKKWerٳ'm_67//_׭[7PVVǏ97nΝ)S!!!_(\|}};<~Ƙ8q@K]V es###C?Q:t(NJiBp)j% �̙d̞=īWggƾ}PYY NԩS[ 'BbєEZjK=6:{Vy'S__ԩSBSֆ)))a…Ć puZuuu8q,X@7|<+80wɓi+ßʠϗJW_VEk5OW;�aÆcĉx YYjkk111pqq}h! R 0~xJ {iṪk׮!$$ȑ#4\az V\ --- :NJ+>`r*?6o[o߾ӣtcsmF &EPSFjצAC]]@s}:>'NDvv6 I&A[[=B~~>Ν;?E!''l6022{^UUm۶IO-aBD2Kݭ[7jRba…8x ޾}H 7|n޽{D:ǻNBBBh> 29޿ߪZ>Ammm޾}KksUUUiG�M=gggL0FII LMM&L7oKRR?#|}}1x`={?5-ecc#***Z&ز)˃G-ٳ'O())Azz:IE!==HLLd(HZZ7nŋLEt�MVi?N h AmCkoݺYfQ!Cpe۷2e AچIHH`޽HIIw}wwwXXX۷޽;ddd΂U\Ku_ ]R]XYaW9�#$$$ sDGGf._ uuuڌɓ'aooxh?Z=zfE.55ڲe A='Obʕ,A@@�YYYZ:<==i[@ty, Da*Yxgq:"ohmJ]]rrrh3i?rf̘<x�999ppp&& JJJӧO)/^ ##(++ $%%҂/^SNѣ9ήHAA̙EMgϢӦM{ּ.j=ez_Tlz[/_mmmرcl \~*3M{ii)mF޽{011gXm>'kȐ!ٳ'moECC._,R]k[+kw o{jI[4%`֬Y�&,X�CCC(**XjmVZ:߇ X,bARRtN0r'ajj7^;lXZZye@׮]2 *ia[#44k֬ݻ4 x-[$DGGh)eLVV x BZ2T걠~ӧi@Sźu~l޼Zggg$%%!%%2%&&Fk0}tܺu w:6rH9sguo+ZW\WWWWW$%%!!!vСC1x`XYYa޽Xt)PZZ DFFR3W\ l6deei7}􁯯/-[wAAAyFFF߿?uڱchBCC~zJG߾}1boݺJ"33}wwwBAAEEE՚Ӄ )) EEE`rbŊTlX~=!//!yK=^zT ̓<>|t֍ݾ}gϞ&qy$''#!!A6װȮ]`dd<<x�^UUUcbΜ9_믿B\\pssèQxl6d�`jjٳgcƌB +/޽-0m4UUUU|hllDXXܨkhkkcGQol^ݼyHHH) [۷Di hqF:::z*^`b lܸG!tqqAEEdeeqAb„ ]&CV̙3K !!A ={իѣڞ +VHNNFUUU[LWWWCJJ &LÇiu@cx)F-[]}nEAEEjjj͛7FYFZZLMM!##z$''CRR&&&N`޼y A ))wʕ+[&@~g ggg ޽^v- ))={ؘzǬ,AeDDvMU +Wƍ'R8iii!!!cptt8444PXX2,Z]yԯ_? 555|@իWi7?>:$$$pYP|-aϞ=Ctt4tuuѿdffgϞT8a?~Opwwǘ1c(vy&<<<mmf# �;w.*++iq|I+ae5 ]̭[hM_qqq/_իW1cƐB~4'))ڒǓM667;;899ѣGwwwj3!MZJ{SQRR"VVV$::fT w>FJJJHxx8%gΜ9$&&&'99[___B8! >-[PqBÇ+QWW'חǙ3g1=z4H@@�ˋhjjsBHEE'Ǐ'fffdϞ=&f.\) �$::M {e�)((]]]Ml6_>Gˠ(*??LB!|~WPP@�{QnޞdȑŅ<y/3gHϞ=iekdӓGPkMMMo#Gy 4H"(~B!6l ***dɒ%Ad2rP__O<HرcɆ HYY_x{e6fccCTTT111!6mݷo_EEqx"qss#:::dرdժU\XXHˆ5QRRڹ �u&ߟ3fN{/מR\#B>c[[[>#0}%;v zzzDOOj*IHH?QQQ!Ǐ'7o˗cǒ۷)܍7ȴiȄ ȥK(g{{{Df̘A"##ݻw �3-55� zENJHHH)#0.\ JJJ4)n"dرDEE\Ԇ???bhhHv,^ӌ�>xuuu2ydZd1.Ǐ'{?~L Yp!c/%,b&&&wqrrrGpyXYY췫T2000tAWW2ldbb3gbB!!![YY7nf֭[6p`hhe˖̞=Ç_g'S?'iiipttă:̸Gii){<|?={ܿ-]`|%! 6"552III"SQ�� �IDAT2000t, ˖-ŋweKy)jjkkg(++|2jkk){رc2|w0Z))):ujD*Νq c`h CbffF`hh(R/wޝj޿?A}}=M'NP4{xt20ʲeg1?onӠW6Vlko:Fm9z !޽cg3T@c :f ebo�200000000000K`  ÿr�OOOhiiѨy-;;;8q \]]QPPׅ dhkkmJÇ믿BKK θsyQQ6m" �")lmmsNgϟܹs ;;;;wNk׮AKK W\ۃb]߿(Smddt4/^ZZZ?>l O7=Cq!뎢UUU4[o�[SSSSbՔ[}}=.]wwwBWWmn>пիWƣ�h2j;v�h:Sb͛<~8 ߥKbPYYo1|Bs8TUU/_[sP^^SSS\xf՗_�033PTTD@@�OWWWݻSUU[[[ġC(?˗/GVV/_?�~z<|l6o޼)> z ԀcƌACC憄\GF=z>u.\CCCڵ #GB[UU___!;;yEEp5%...0m*okjj`ll̗999�Mсbbb%ÿ@Y%!tcժU/$$.\UWWcƍPVVh";w?3:$} ǿq8$''CWWSio=̄UgQXX<wvRLG -WWՑr@DD.] SN8veMBB6l%z�RhjjBFF077Gmm-֬Y��===hkk#995,, l6ׯł}Aff&J0`�i&j`#wȑ#8~8mKll,xb�1o?SL-,,DAA\}}}>YPSS򶶶 -#99bbb󃸸8!))u‚:3o<̛7l6u؞={ su߿4ŋutt0j(cNPTT\uZ<, vjLBI|QHII̙3/)@ � jT~A̛7f?ƭ[��C u.�rVVh=8|p|5vKKK*111lٳeee�@4�ÇBIII0a?$n߾Mp8>9^z%ԄzJJ ,--[???QߵZ �Æ Lӧj1ʰSn9LI8p�Cuu5? �4p̞=M~nLLLpApmܾ}�p>\}}}DGG CJJ UV˗={]@^^RGGGbӦMصkX,M7nK}&5 6e"c޽4i\]]4u;6 ---lܸ ,ɓ'[K~GA``  ϛ7o₰0=|rU0t,УG UUUL>r燠 a8v3%77p8X,Z{ X,>|iuׇ.֬Y"�-mx1|0m4hkkחO{.mPY^^???BOO4 (L6 ZZZ BMM † AiYaÆ ;v,fΜXLٳ...PSS?,ܸqPUUŊ+PXXHiO%/]ٳg#22֭[KܹFFF000@LL *i}}=`aa555xzzϞ= ;;;1nnn(.. kϜ9CCCVEp6m¸q`aa<}W6l6_NŢ4*=׶6W+`gg555xxx񰱱?ZK_aPVVMyXr|u@AܼyӧOq5ՠA�lΣG��Ç�@FF STT͛7(4MAFF?��M9PRR!C p999BJJ 1`�ڶIǏf4gϞڵk-6[BܹljBuu5⠬I&LJ<x0222hv.Zyp- 44xyy!55±cǰtRp8L4 111ˣ&%%fggzj5 !!! CCC?򛗗W@Ӫ}PP̙۷5k޽{ ˖-Ñ#G_WW{ ,#p]P2322гgOa޼y־/P| 2}}}q`Μ9�hY ׯaŊχ`jp- pssî]Zl!o_|Wȑ#pvvFxxI,KKuѣGׇ"""0{lt֍ vZ444GRRRgFj ޽M6CMd �/xxx`Æ tΝK3ǻU[[ +++~~~~pwwG=hO 7nDLL pn:3UUU>}:eXYYw礪 !--M>}:QUU ~D ^^^ؽ{7dddPWWkbɒ%Xt)mE?rAbb")))уz{{{DDDիWXt)mo OOO̚5 (//ǔ)SILJJ 055Ŝ9s`hheeel߾033ógڝG8z(sN<~p~غuk�Uvv6L###8p� D)g޼y#ODΜ9C>'YYY�&R^^N�LO?DɢEŋ)ĉ�W�ܸq/Lyy9>|8*ɓ';vɖ-[�xP9%%%�?)>ݻw'N .HBl6#pHee@>| ޽'&&rEÖDždd…ܹsѣd� -1o<yfB!)))�޼٤gϞ O�ׯBݿ())˗�ϟ+ cccFs8beeE֭[G!СCdȑÇBoN/_N!'RVVF @N<I!''Onw�y!˗/RSSC!LJd9s/^L֬YCkke"̩SRyI!DFFRnuuu�INNn1JII ٲe ۷/_[ KVXA !(((?d̘1͛7B7dZ@8N"999M6 Ll6mǎd_7p8DGGBou"�H~~>'={$(OP!hjjSM6y/9wK<<<b``@Җٿ?>|8wTT6mU !$>>ۗƴ?×VnOZҿ}vGK3-Ohrݻ'/G|}6@<yBpV[[K4559 ?|@Hj?Bȑ#G M7qwwo1/_&�ȋ/(ÇYYY7jСCDNN/47E-ggΜyߦ11W�hhh�hR@[VÑFӋ߿?={ƧRSSR //?� _mm--<wɥ1b/h<x RBaa! ..Kbƌ2e /^$&&"33@VK{}>&o!))I=5kl6 0{l>|8v&uڋ/ /^ݻakkq!))"Ga̘1j3�(++cx1�qqqp8ܹsaiiXS'�w6UX, (S"77999 E}|UTT0m4hRS^|PUf^@S᪏}\/ʔ6***[hO>PTTDϞ=w>|ׯoQ˗pqq]ZU>}:QRR}aٔJ!Cׂ )) --cƌANNN,_w&7222D7 00˗/3N<?GCԩS) BN:E[ĉE^eB~~>MWۘ4i3B�4OܶA(|w|emРAx5uϻJuuuʊ7BZݣGYo,O)..6߆$%%⏶W-1x`L:!!!o{ *gW;�aÆcĉx YYjkk111pqq}hFW R>EEE?~<ո9;;CUUBCCo/_s]pk׮!$$Zpɓ'7^o+WBKK C'ñbŊ.F5ʏ۷/hjؿ?ܹm۶QIQT唑///ŵX: ݻ�&Nl 55&M6=z|;wPDnݨbʕ8{,rrrP[[۪o6Y,.\۷Ye6}[K{erՋZ-[\*++q!?QQQ8tN8 e8p uLӧO1sL\;c?�X,DDD~74h.\+W2V}(߿GIIIqzae+%%%}yٙ*탦&߿߮xJϞ=-))Azz:ͲׯD1 U[[wwْpArE-i5[na֬YT+++Ίn7$$$h?^Z/ќ1ƥ+Z2/_Fpp0޾})S[|J8::BBB|DGG]|ҢN< {{{ ttt ''gRn8z(ΝKB][l4ȱGbb"U !8y$V\IͲp8 !!|L4^j5s w֖stx,Is8 'ކ999Y4޴9r3f̠V:th:&JJJӧO)/^ ##(++ $%%҂/^SNѣ9΄,^~=޽{˗ ϺVTTT>[Dرcib8Q(R2eeeѷo_ކtZj&#ZGF\\v܉'Nnݺ!::999KFF۷oGNNEYh>c 466 tޝoŁsC=zɓ%ĥp=j- KKKjp &&#G:*##y!..Cӧ4_+//ǵkנ JEEEψ#z&`[PWWǙ3ghu^^l3Q=>}J*''\aȐ!ٳ'444"UDwxy8K+šuaؼy3mWw D]]\]]JhRy<x0w^,](--"##N+W`cc6 YYYAƍC>}e˖ݻwPPP@ll,ajj*RZaddcHOODZcǐJ j*}ň#ʿu8*++QXXL|wQZZ !** |VTkjjGu<x�III(**�۷o{#44+VݻwcHOO|-!xRSSQSS+++̛7Э[7̚5 @ٳgz?HHHG5гk.!//'CUU~ر3g~Ghh(0j(*6 kkk,Y}�b٘1c?7nD>}WիoŖ-[ &- Y0`�VX7ѣYaÆaϞ=z*zAj Q0^~ mmm,X�֭k~NNNP}}}WてN4KJJBAAz&H&qܺu V ..۷o#44Re\!bē'OгgO]tVZ3g˗ABBB'褦ٙ6o<`РAoooa(++۷i>EGFFwŃZ]^^^Ğ={`llLYYY|Jkkkb5k^~ObҥmʗX[[c֭5޼y5k 00zj\^{{j~ 0ydO%&& ꈭ %%XErr2LG=z 00FFFx annaqʗԿ:<>|uuuHHHٳA[ߕyyy;=Eqqq3g@UUGYۻw/jkkDLX߿fff yRCCΝCdd$~W 55{��޴ضmPs̡fC}vBRRݻ555,Zfe˫WCs4 >'OF\\ݻ ,Yl6B[[j;"oY,TTT --oݻw3N8[BNN VS{/^4hp%i yyy9s۶mÉ'0m4P:cǎ'NHaƍ߿?`ooO{֢ 6n8~)ݻhݻwǜ9s0dYf¡C?[n8P- 4}9k,_Xt)tuu, o CRRtrrB>}~TUUa̙�ZVm۶u ˏ?˗̙3 C^0addd<2P^�� IDAT#M>j7oތ1c`TMMMՋ%$$<{++\t i...B999P ?|ݻwO?aΝ޽F~qT)Ri+ IYXIBT$^h1AVF3 " 6|%Irfg>Of֬@59zh:t(FFF[xqDZc""=:;;~ztww6fʕq={ĪU?]6CCCq8p@]6N:J}vIϟ Ė-[stwwDzeŋرcG Vt7)۷o}ׯ+6D童KƋ/… ӧغukƼy*�guuuz`#]&Je۶m9_~7Ƒ#G~;~ݻwbŊw?՘??~===SDSSS 0N<>K.)x/,zoFccc<{,+3щ'͛7]Ipڵq<UQ:sãGÇ8{l\|qhnn9sĹs>"01S^^-3qFttt ʕ+wN˭[aʻ\.Ǔ'O'b׮]w/߄sRG<ׯǏW=^]__߸wz_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@��$ ��@�� ELzIK,Y ��c…>ٳ0sOE!iӦI{nlذ ��PY_|9V~zV3B\.OiT��TS=J@ܹs���g5E�Rmmm��P\.\AR)F}l֬Yc��rk}3WQk.l7T*ET@��*?vǺqD�D��Ԃ񚦖:&5".���X515---Q.ի.��XsjE`# ��POl M��@--�k]j.�mL-��@r|U ;p^��@(2�V)$�ܹ3��oxxU=�7o<S��^����C���$!������I@��$ ��@�� ��DK��P uUe���E*2���T���$!������I@��$ ��@�� �����HB���$!���;^u���Pev������I@��$ ��@s#"Μ9S:���]S!8����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/screenshots/opensnitch-ui-proc-details.png���������������������������������������0000664�0000000�0000000�00000225106�14401326716�0024267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��i��G���#b ���sBIT|d���tEXtSoftware�mate-screenshotȖJ�� �IDATxwxTULfRH'@^Bht(*mEtź*  )"һt 56 )$y{O$9\5jm""""""""MfgSFm ^z> Ԯ]v!Nj�0�M.!W^eܹL:`VFDDDDDDDf͚�85ֹsg Z""""""""E7NNN2ZFDDDDDDDYzz:)))fh4b4qttѨnܴpݰl$%%jo Պb!99'''+`-"Y4"""""""rl6׮]#55F%(QnnnL&, ׮]ҥKnnnwxSFDDDDDDDf뤥a6Z*ŊZF<<<ϏcǎFbb"wx˿Y"""""""Rd"((gggVk/jԨTw;l6HHH\|DDD$?H&:.}>leٰX,X,*V` ==/HŊX,wU w<b;UV **\]]1wHv9+X=)[&-un9CئU-H1W6=Um)]ƌ!ē~fLZ&gGLy9kq_dW^{es+Ǧp+łQ~ߎ>t{ mNN y8Ww۞e=ey?Jzz:899 ggg<<<HNN&==R7|CʕiժM6a<4ɸ^^^Frrf'""l,>SnE>v˙D~<pU Sc16W+'rc08PO28v3vlqO,-;r=9rnw"""AL//L#ImFtttmgϞe۶myyyveOHJJv~WF ֬YCzz:6ln<4)))/_Œi/ḻi"""$Ë)7 53Ve[Cz߯#ڣc�͒L*yĥ:_?z3?u`H</ZNOP[@\ݩ[mA!""rμy?~<~~~\p'RJ4h`_H rG Z^tLBhh('O;ɯZhlfԩ;v~#GҲeKzSo&""Rlٲ;\&el2Q@ {EuuEYuxڗ7)iqm#`v % W,nْ}St9 Z[gS\5 Mtx�Xm`l09Q~_vSKN.|憧G_=/ ]"9`DJ44m3laO`*EP h_lcUh*[leꦼ+3pt-iSD|KN{ѷ$fZyzmK%` -ۗkO ,_}ǹwٚx7m+aa渙o2]wb{$ ]yaw&cGY{ދϾʓhzΕ}X־p5_WcO.^MO%c*n:)?׽@zԧ\<r? ?FѾJzz}q2d=*Ub̘1$&&ڏ3 X<1r*/==3fp&L)S2e =\iFU^bb" 6]vYVZѼys])w1ɉh9hn}u`XZ|JҠaE6 Q'1FXs΍UJpgMeQX XI,[PKL+%4(Oh,TQ݀Z&k"3H2&<2s\185vkx�%6MWcVP�Nג-~8];eEDO08Rc4rv6\hMϵru`%elt8II:NXD6 -'Ҍ[E6~1kBwLʮ$Dlg᧟^-1sߗ7~["f9/� sKs8U^JxEgLfm-tY"wOn4.{Qy# DDDh4b0HMM4իTT)S/LBBBRSS1 y MdWb᫯ɓ/_wy(fΜi_7x^uѶm[lŠ+s]qvv&!!0J*ŋIMMT')Pk$\ɺD7OLHNNs_GQM6bMc^:&t|;= b]7!77ۍT5_ۋNq$u?߯`œ]^ϟ9a|S Δoڞ;s8zU}-Yc~b;J<8D[{1[!-"<*eR}pp.V-Aeڟ|藜' sN=!X$:">2wo<8q&58A,j =b$T}�/CgV|gؼQ!,m2OY=?2gk z1Ge\n߹\o+y{'j94�F ٠-mFNX"sl\?K"""wh4qss˴/..^zɓ'/)GL&FcfT^-6l&ׯc2xw fͯo_FIVUSNfѲe˻nEd2Nrr2Onqwwj"""'l$%s |:6pw-Q fg� xס!/̅ ج^lHy73&-pԴlpԁgقKko'K7y5%nGnNg3Rf0%r8N#%|1ΐKFjg|'A=62 ~3'r<5cp8mcI8ͩUFS\)Lzu)i/窵,],"q�@lsetM~2q H15[ 9FE 8WoBGر>92uhӵ┧<rn5""r\]]%ٸ8ƍ˗ RQX{.N:r㋸tf3u4*;}4G^z\tu2zhۗiZݔwQll6k䌈*(c2pҌ7Vϡ'H78QBi͚xcT0PF+h3>,?܅LӂΔք՚б*>'N?ɴ֔.]2`76E~qt/ZN1?4fOvV,XqO*Y2cބu7/:֛;dʦbevϱ{GV9vrx WZb>|-W˶(_<H=}^!<ʴ>d=qıʷO <ҋCfӧ~:ΙZVΟ?md)W\ݔؔ{&9r$\zxԩCƍt]w&HaeC Hg9j*g$n@IbMBl x4S |` ^Fq'NV e*]׼|z>CM|׹.w 쀃H#--<Jlr.w<1lqΦЌ5`x+!ɮ7[n]J<*W#(I/ka/N+Qq)ƕPZ x+RMsY5s]{lDHنi^ eo n 0#}e#mDE]Ú<ns䚿ݱl J,ItttkdJMM%**RJa0L*?Leq{*N""""wl?%0ĝDU,TwƾV-f 3>Dp98s0D2tP2~tq-Bt-4`X`nj|Z\KWk,Qg.sDvmjyo_1K{c;?p-:7 FseӉL +:6N;V&?Ntl*C9ONu5.uV"4 cTq7`,Du_ѿ3unD_kv]ٽ\ʤNP'cZ,Nq -po'<o)u^{7 ӹ"ǻdI gkfTv7-XNpK-Q HI:ա˻c4K.A\kBDDOiiixxx`X8}4~~~>T(%%SxqHNNVy7粈<T CxW)Ji䒵՛7ҽk?W%zv4Ru6j]˚K^ax:L9Y,)$a34'8q>Ztxr u*M0qByn�DfK y!p9Ȗ-{9l%=N@9p,O~1޼']@Un<T\1`d:8It5 q wb+#i|h?MX/nb#$VUvQ,7O5_3T9K?Y|,ĉ=Xq?1UhW01rnȽINN???Ν;ǹs爏'%%BJJ ={s9<ig5jmԨQwUQ,f}x6n]5떜Yw_?"?[S=O1o<1LGRRɄ ^^^/^;aRʛ6m;ߪYgcn5[lML>U JdF#/^,BbbbG]SFDDDDDDDj%999_F&H!4""R4B] ;))))r\88 ACDDDDDDDHvF҈ ҈ ҈ ҈ ҈ ҈ ҈ ҈ ҈ ҈(""bbb%?<==Xb'"""""""RK&""@֭#;={IJ5_&_;%$$P^*=_&_F`?/98{'ْxG8\y<ZklEDDDDDDDv&Ay{~q0}vزnum6 ,H1U-| d]=qP[}| C~VODDDDDDDב46:߿,,i74*GDDDDDDD(ב4~ߕ 5=9˾culYu >Gg~~TIDDDDDDDP/#iOZlL?:'7i$5%HsqLNbYYR7<Ul_U1WFA[_SUNkܝ ~WF6é\?{7""""""""E}IcYI;NK1spv$",|A!oUbcx�8S9"W[/)t&G6ijkN$)1ErdC<2lݱQӝDDDDDDDȺӝJdr߱l8qp0fI%\iRI}.Դ4ثY6`?WqJ /]d2c0p4;㑤$/hYu$3IaZHŽH֭0;:r…,.|Epttӛ *ѤI)H .0g(~yX Ͽ<rYDDDDDD2{7y "e`oooV+!h`0`ZΏ*<M CAWEDQi<== jeZ 3?("""""К?>SNUF/._;U\cǎgbcc#Krj#DmM*UWV""""""rʕ+ǂ ۷/&+P#t_-""""""l&00Yf1p@۷5"1 ҈9::b6R _5Cd2ѫW/jDBe`)8ff3UVeʔ)K]5 ҈<2F8::RfM>CF֭[ j"G ҈<24#j֭oO?͎; z"ZF !!COOO*VXU)`MHH  bѢEԩS("PFh4ҶmۂC'22J*tUDDDD46nܘcҷo_,YBpppAWSDroA3gpu]_Y>pss# [Qls'$$(@�ԯ_ 7h4b61tܙ'Of߻wo~g*W\0\K̙3X,*V``0G%͆f#**p~+mps5˫\gEnnwTTeʔ)*HN:Ŋ+8p >,ׯgѢEr�Պj-Hv%Hsu/oh4RLËL6gh{dddAWH)SL RQZ{3d]i׮Ջ0i$:DjA)N׮]+2Պ` %%[QlsßFu_@ RQZ{3d(L+:z([f̘1ٓ/777 '|Pyۚ4LF~+mP\VQZ{3mۖ 4H:ts=СCiڴ)t5E6e$ ȻˠAHOO'00;2c |||h֬;vP]/@ oӧ/2]ve˖-駟p-E$/ ի陶UX+WrE֭KbŊ:j"e$R}F[n|$$$tDDDDX"M6EҤ/.]ЩS'Ǝˊ+HII)?fܸq̹sku҅{SNQ>;vdkܹj*}ooFիWX,_~ d˖-lڴ2}tFAZZZAWd0`�=z4Oi^y>˙3g[7|/O*O?o(|P+yK) tSll,?8y$u֤/#<R+ʔ)_|f#!!Eѿ/^ٳgiР7ojժ];WWW5k@-^z<쳴nݺkdݺu�lٲP] 4JTT}o�� �IDAT;v`ڴiLw1UXihfʔ)8::`+VUKquuaÆ7nLpp0۶mo߾X3yjժ?Ǐu|$$$PlY{j׮ɓIIIcҥ$$$ЫW/F=G2uT֭[G F^�/ٽ{7:t;�\r3frJi۶-#Glٲ=zӧ~zʕ+Gf;v,NNNXV.\ܹs9<}eȑ:Aٰa{A̟?/n~Okܸ1իW]v 2) ,Ho�Mtt4_}7nzo~�l۶3gұcG-[ƥKxG:t=oʲeXh/^{ <777bbb b…O޽ɓ'ȢEعs'qqqtЁgyRJ׾S,Y>eL4M6ُyINN6n.oٺu+eʔaÆ<8::Yp!lْgy��, -bɒ%DEEѼysKҥiР�-[`֭T\9_~CM39]ZZqqq۷ <>W^!**1cƐ|={={6FGҺukx 1l6oΰaØ8q"#Gdڵ׏_OOO^z%RSS KϞ=y'ωŋ8::b٘>}:Wf̘1ffΜkƔ)Szjiœ9s3f +V7$<<L8po]vѨQ#N<}rr2gfʕ�ԬY3Sӧ@@@� , ((í[2gGHHڵO>�$&&2{l~gxٳ=[ڂPF |}}9u=HάYضmuaπ'LtakoNr;wVFl2DDDDX&22___ʕ+wccbb۷/?<zĉߟe˖QF �VXA`` F")) /�Yذa>,;|r|@V(W{͍QF̒%K?~<f`0ܟCɬ[K.Ѽy'IHH੧wL8WrҥILLϲŋfK,aҤI 0 ҷo_VX ,`[xxxpI<<<a޼y<̚5@|||f Ѹqc:_b6طo3g$""???�VJ5=z4M4aɌ=~d! ӣG�ׯ_vM֭?>ӧOM6�>ѣGݻ7kT+W+uVԩ?UT1119}POhh(5DTT͛7g >|={2aq89sf1aΟ?Ϙ1cX,̜93KY1g,YӇɓ'3|pΝ;g/?oL8J߾}sM[P, kȑ#tޝٓ;vеkWVX)p{xppptalovns7i9HQ`Ah1\xСoyFŋi֬O=�#,,5k؃4�ƍ}LѣfŊo}}}iܸ1/yѶm[{~m۶sٲeyGtR sj՘;w.e˖bccs+WI׮]xPڵk|CÆ ѣ?C4i҄va02M\2�*U_qԩo{hhh6=z˾H  MՉQF,Y3fd)/99e˖l2i_||<&?{;wҹsgڴi++WfȐ!Ջ!CбcG7nN")),e^v-״ʯJk;ۗy1|p(3gd̘1iӦ9rĞÇ={6ׯTVVO}A2eٳ'...޽׳o>/o߾-׮]cTZ~|3!C�7<..ӧ_takoNΞ=ݵgZn""""R8Xߟ;wb_obر5 ^{~7osɔ?c;vtHJJ]vYKLLx76M6n:=JDD@?u\r|W�|w={~پ]TP'x~'x6m@xVZ[n~Stt4QQQTV;ٙsa�zɨQ|2]tM6Q ) m/Wsh4G2enjJJ YHJJ Ν6#ki2x{{7>}n:^~e.\H͚59s&aaa_qQD ~\]]XzuE\ӟK{s@~v=rss#44Pj׮jeڵ|g ;~8 zt>lҤ ~~~׏޽{ӦM{PD \>(yM'Z*K@y1T ) ͋~<x)_<p㛽�Snˋ_=S gH,X /%K$>>><ܹ͛syWyꩧpBXŅuߪU+֭[GNdxלܮf3~)ǎcL0ŋ_k򤧧gdV6m͛پ};֭cL:ݻgSOTvĉTP]V...nݚ={dd4֭{qj? t֍UVQfM AAAѻwoʖ-˾}hذ!*Tĉo><sJ1~ڹs'/_fL<9Ӿ7RvmӉr,--r.]Kw^6mD>}߿?onnnvL[53NFRJQti{`bf3w0-zzz7ڧosiw'""""^iy'8q"yӇj֬'|;Or8}4ZsUVѭ[7Z/A¤Zj1O>VZ쌛QQQF)&/c0Z*UV[nԩS:8- �8`ζmxǹѱcG:v숯/}ݻwvy+V7_~1_~΋/h_fܸq<.]"##quusΌ3 H=gǎ7x Zh?/_fϞ=='OӴiSٽ{7...1bf3ԩS\|CAX|9dҤI̝;#GHٱc:tsƁrI{0:/>4L4lؐ R~}ȑ#X" ڃ)mh'ɉΝ;cǎLSw#<k&{ [bE�~Lx"۷o_Twz3 r~¡4F3vXzɈ#>|0 @noyW>|8f={Ю]<TX1&Lq0ԪU3gp?t+ٳ3www'""CQZLR?HÆ cƌX}3SNu֜:u˗gחÇs1Tk9sӠAطo...wśok)]4Wڵk"fݝ˓Ν;^PL-[Fzz:$''%~+W_}ŴixwG駟ӭ[7~Gf͚|@Æ <x0p:wf >ooo:vHrr2fJ*rTg}Fn8w}͚'NФIV^m{1xQ}Jl5tE̙üyk۶-'Nԯ_#FгgOhԨ,X{fV;v,Gɉ~uTnM (('''6l@zD1W^yW_}???e˖-,FesNʊ+rM=V-[,o6GfTTW2k,|IH~'""""Ci:fbҥܹYfQ\9Yh*̙3{D4irGٳ',XI&QJtkSR%޽{{Ν;?ɧ~'U*T`̘1|t̙ԩS駟h׮ … 4/o.\`ԩd)Sp)4h 6l#&&3g#Xbݝ.]...̘1>[SBmСC,{qeÃ^{-N73 t֍nݺezY^0bFe{@@�z7n/[lחFeWV-UƆ _>7fѢE|wC7$3&Mb̙|ck׮nø8N<ɂ HLLu̘1þ̈́ Xp!}0p@Fp3AƍYt)gf޼y4k֌+WfYVӅ< ˗租~b̙Ԯ]ӥK۽gZn""""R8FeXfynݺ 988o߾"oEX~}XӨ(Xhm۶-rYA*jwQkovׯ_""""e۴i v$HQs'DDDDDDh1tDDDDDDDDDAB!ߦ;YVVk~eX([Qls˃ST z/-H^O/EߊbuV0ZAŒ_EߊbuV0Z/AXoߞY9EߊbF-*U^g{Qko5ktDDDD :s6w=i޼yAWADDDDDŋK>j_DDDDDDDPFDDDDDDDPFDDDDDDDPFDDDDDDDPFDDDDDDDPFDDDDDDDPFDDDDDDDPFDDDDDDDPFDDDDDDD0D"..K.ݗK,I /9t-[rÇY}-GDDDDDDD$?<NqqqԫWラBll}/GDDDDDDD$?<4.]f=DDDDDDDD dM; ?Ɇ (J)]:>LDDDDDDD`ӝl6[^Gf<X֮˺ kaҥK9tPAWEDDDDDDDA~cGٰi=k17L&&sӾ}{L&QQQ.\diӦ1L.]:cvM߾}y'ZwT?L&~<D\\wTNvz@ZDDDDDD䯦@4yA19{.5Ҩa<ɋ .qF4iB@Fua�z`*Uģ>ʰa04Z <?o<J(E"""""""7&MnAcǎiL+ӦU[ʕ L�t'g�5jty*U qT-r#"""""""VI[r*yE@NFaÆ�Ԯ]ą � d21`�{ݗ/_Vw!55{b2ˤIh޼9&Lzj,cфСC^uN:e?>99 c?Zݻwc2X`.M6wlٲ%S{3R]v`00sLٴi/ugɓ�|<3�<ӘL&mf5m4LBV9`}YMsAѣlܼaCv#@s K-mT\]wgԫO|7n ?̣>JPP͚5ĉݻGGG1O?={ŋ[6mɓ'ԩ�:uի|嗼+|Gxb/^̊+xGr_FÃ]vf>Lr倬A)Lv >>Cʕ+)U͚5c۶mhт+fO/PzumŋY|9;w&,,͛D*U9sfXDDDDDD(*4#i_eKhҸ fǪ_~RT\ƞƲg|#i.^ڵki߾=y{F㥗^믿fÆ ̟??1kf޽$$$0rH,]t <<+W0`�k-[ŋAdd$�L81uڕ+Wp FRRvJլY3Sz .M2eذa'Ow :#F7PvLuСC,Y?3f?@XXϟ^zciܹ+k׭%*4�OBhaBc٨W7ϦL^ݐl MXX�;ws[n Ç'$$̙3iذ!j///N  >s*Wرcٻwoz;;;`G�lܸj ˗/p%֮]KN(UTt˗l6k$޶?A7o&>>kбcG�J(m) 5 ̝)[&Qώ)bڨҀ! y5pr3Œm-[ɺuذa&L`ҤIrfiUl߾M6m6kL?LfͲ7ɔf6g~\yhܦK899)[nL||<�wHQRhFdWA0{lΜ9a +V.ppH [2)˗l<|9+Wp˹|2W{�7.]Gwᣏ>T.@BB͛7D=pqqm۲w^�ׯG26111Y%$$ ӧ...k˗cوҥKwUw]infP0;wo8::ҸY'ɗ2._̪Uٳ=p7 7v)_|P?#iӆ'|ٳgSLUcY#X:t`߶g*WLڵ:t(oyTORR4l>ehw<Gs;ǿ[ 2M6ecjhРݺu_3bԩCʕ3#"""""""* F [+N:lfЀA,Y(_FdGss`|Ԯ] dzm_JNbb"駟fŊ.<OOOƏOɒ%ٸq#!!!,_-Z؏yHIIᩧbNʋVZE޽3-Nu 4Ü={־ÃW^yT|IlBF7ոqc֮]c= cƌawUaԨQQFeL֬Y)ŋӹsgVk`/Ax͛76h%3ydƍǖ-[hҤIrzʕ+󾖕-[ЦMNʳ>{%&&IR2mDDDDDDDŋg[V6mZZ88;kl2lrۧ_uףbbbXr%?xh<!!!VYiTAԨٳgc0pww|oɕ+W<iA6lͭ@!"�� �IDAT""""""+#ixxx3LVVn\]]=S."""""""wi|||Z <KsF#V5SDDDDDDDD tŋj܏j%44ŋ?扈wqYd? X+vbp-vbck\kVWL,̵L t"*!~3'L9HޒZj\~+W H".\jժeB!B![ISzV!B!"ݝB!B!DBRI#B!BH%B!B!D 4B!B!YT!B!Bd_N_;H!B!BTe'5k ;w`ff$ROLd4)cY!H[pȑtwB!B!2%Jʌ`H@GGJ%e2O'y&2IBr^)NZ!B!BdҒF|&[;O'y&2IBr^)FZ!B!BdRI9)iԨQ'9͜97oжm[;ަ/_O*ULoCBBƼ[ҫW/>}ii`ـ9sf燾>>S}\ҩS'f̘A```טFx-?V0j(,Yn{pyӱˋ1zbDEEezd{~W$SfO'///{QڴiCPP*H޾}iiϟ666L2'U۱fӦM/2ɂϱc8v;�[lQu9S ɓ�ܿTq޽[cիW/;޽{ҥ ?oo̎W %J 00Pc~z 斡a͛7ciiI޽h ]k׮`ǎ!*ڑ#G(QƼ9i$<yO>}dL,J*s @ڵ155XO/eR[ҥK9p�nݺi SGG󿖗/_d\\\\/=+ۖ﯑q266ZYֱcGqvvN:|Q8Y+n^,\PJ5Xbcc!k׮-Z#;Ǿ\xgϞ}v:uDPPPԶԪU}%X7Y:Rț7=b)S 'NPNVOkV$-ic``c�xŋgǎ ƏϣGرc={ښ?+ۉat҅Fh"Bٳg666k׎ 6u>ƍqpp‚ &+իǭ[ݻ7ۮkҢE *Cѷo_8q"?V<yGGG6n܈=cѢEDDD:=/_dԩ0uT^|ƜI'No߾ԨQ,{/Yf۳m6cA}l >+++\ucccvܙJry͛N:5�Sܽ{}`]vѧOjժŘ1cV?z(...X[[ӢE <<<co֬Y̞= ?~< m1`gg'3gOOO M5kYf민{Nc[=:uбcG٣,p‚q#ߍ;+++7o>}Jqߊ}ѫW/4iB…9u'OҸqc;F=hڴ=t׮]8::R^=~B|K._/^tyb``�ȱ/_>/ϟضmuXMK<{jGZdqqǯݍ??,,m۶ѧOrŲe033c׏ϟ?3sL>}ɓXr% >===֭[ǔ)SXh:::_9#5G^zTP`.]DÆ uMF@@�}}}֮]Kǎ0`�-[xaݶms͍^zIV8~8EUnLMM9r$͘1cN˗/qssS*f6ӧOOEMzԂ'UqZjʕ+2d(P�JEtt4Δ*UYf̙3Ҩ駟XhM4L2<|0ɰ;wn4h͛7(AJO)Ϸlŋ={6 @-Z]vѾ}{(WJJ܇uVӷo_V^3۶mCOO[nQreڵkdzgXt)QQQ 6={S7nyckʌ27?(p'NdĉTT&NHpp0Ç'22yڵk%00[[[ƏOxT*Ν;5k[nʛOtŋ ˗̙$etec_d^M6a``@=?qttTT*ϟgӇܹs/_>l:u* fϞ=l޼:R<K)=7y2u-<T*,\ѣGsy:BvNɡCx666gMR쩝Lʌ`%h|S?&&(Çcii >>>RD �޼yԩS7nL4 ___W@bŨY&ƍx_-EWW7A&%::7ңGbbb([,߿_i  }6%KiӦ�TPܹsSdIuATT111T)S|r5j%͛7g۶m 8P)ƍ#@lˢEpqqKy/^0e5*jROɉBWW!OpTTnؘvڑ'O>**g۷߿~SNJw؁ma׉f|ŋΝ;iNszYF6)˗_>M4AGG e;˗Tܫ+?~4h@PPeʔaРA񋌌?dȐ!QhQ2v[^TAW\ISLU?֭ڵkusss K.e~VS?>cƌe˖�ԨQOOO.^H M6TVM 3**G%,#ec_|;N<ϟSQQQ4nܘKr=ܶj*$44Sb >&un-2Jzo&%G}/H".]�333tuu3X׉hnݺE<yy*Ubǎ*U(e鈉Ѹ{S[Ғ&Ӷ%Mb ,ľT344$88?ćhРApÓ[~W^׏/PjU6oرcɗ/wܡZj%ͤǏy1fff|}}}Zh7Z_pa޽KTTzz)Ŋ~Kv!RWFFFk6mJŊwo޼IP>+݂ ,t=5j9١O);ty%vvv4k֌bŊ)a +8@@l7+JEDDѣ<|۷oSHoq2vST̝;s*󜝝7nb,n޽K:u*TbŊܿsssҥK%22|||2dƲ0ѣ]vUqXXX#GLMM\ñ/ԯ__JSlYf|?yϟk/O7s!Ο_~=LK*{>uBRQL֯_v!*{T*yLu)Ubo}˛7/�vXO> >w%7nLxxR+-3uwp7o([*Use?N۶m޽;nnn˗ 3gNǏ;w''tfv?)y pOÌ9+WҮ]tӘ1cȕ+}H"9rUVioZwt522RZi&ӧODFF&?~rŧOxy7yrD=;;;e D__~;wpI&NH"EXz5 Lv]pp2ӑ#G4yyyѳg$sar,_.Dv Jk<yPn]|}}iժNj{S{Ғ&KmK&5Ȉ2e4O,o5[˗ӽ{we~LL Z4jcccx JJ]]]>}dsҥ100ʕ+7RNk׮I'7/=Wr/QWCl73g0zh%9r`eetؑCR|yx kN4;qIΜ9駟4j)?< o-hѢ%J`͚5mVYIJ\[ۇ9[qw="##ݱ%2.]ZLU!~/[,/_Ç)SgϞqe*T> 6ʕ+=C~mٲ%W\a 8ᘙaff+Wիؤ,#dc_do'Ndɒ\tIKÇܹ3wUZyȈ ܯ$vL.DzJ4%w-rRNc!;]'^gܹsiԨs&o޼<~Hj$Ϟړ4߱<yΈ#ӣf͚<zׯ_ӳg̎^r>}Jƍ5舻;&Lښ 2aݛ|Qn]GJȗ/2�!?3cƌ!""%Kr޽{M/0x`&L?HHHk֬wx$Ǐ'00UѣGS Xb :ѣG3i$J(A@@�%JH111|Ǐsy/_˴IjՒkג?~LMMΝSk-ZcccvEtt4%KLuSvm֯_Ott4aaa,]TcrʱaΞ=jX[Y%۷r̩ >l0틑5kɓ͛Dƍ111uքsEy9[nnݺ燁&&&<z(eBdw }jT�ԭ[ p)J ,ɓ;v,>|ؘGjܹ]ozLooo*T@ټy `Ϟ=tܙ5j```J&M b/\J(7JcyԎң%MgooOb޼y}Q&J޽{ѣGc_",, .`kk˚5kXr%Ǐ'w4i҄?7o^Ǝ˜9spvv_~/_>A @B͛7kk5'm 2rN͚5۷/mڴQ044xyy;4iڵkɑ#* wwwXv-ӫW/ZO-[ sss&O7=,&yٳgW\!iF;wn֮]ܹs9y$SNhѢqH鍗.;gX[[3tP1Lڶm˃5jVVV,\0[[_k1m_GWW%Kzj̙!vvv8::*hтupBԩCQTTX'Nzj(T7Ç#G.\HPPx{{cbb³gϒ\y}ݺu'N0}e-VyW޽{O?\k; _0kQJVX+022bܹ~,dDbq455e̘1̝;WiQůΝ;W^xBݸq0aϟ?gŊtTXWҡC׭["#rΝ"&ɧԓ<MX$EdWϞ=J*?>8Bd[qy/&[u!E{|˗/4f&ɧԓ<MX$EdӔ/_9ruV D L?t1iB!ΨT*n߾ 1=("2K>Q,Dz2ɧԓ<MX$Edʕc̙ K{9~/i)4?Ό`H GDEEILSI&e,k"i?t˔JSNeFB$lA)$DF25~B^ΟK:03>Uܹ&~lcQH/uecݼufG';wS*I&e,k"i?tfwΝ#44m>f 9.E2;B!B! dy3;B!B!˔4i&|J^!B!ߨl)FjiDz2%B!"k6ݝB!B!e٬zB!B!ķIZ!B!Bd٪%B!B!ķ*Q)ӕ+̜96m~O_ۉNXX(N>|X;6mڈ-Z4xu@ӧpuG۶v̟?ׯ`/̤M:u nh,4%KӫWO:tߖV?~aÜYvMe)A)*3;U&o߾Q?Lɫ&M2>b.]H6?qyeޠA)S$sܹL>e˖&<jժrzGGG?&&$$ٳgcccCnؽ{wfG)K<۲e UVjW^ùs\ߟ-[d%lذ!Ϳ&$$tU4iG2l$ PjUmۦ?~$$$$Ua$ 77TFK2{UjәVv\r_ܔr\My02Chh(nnn%XYxqet333uÇS Ә+󢢢fРATZnݺqĉ}=T\~qT6 .0fiӦ 7n_$+=<~^ 7ohݺ5GMi9_fGYٳԫW/8s+Wqꄉ ]Ӭi3~vϭ:߽g0߻3 ?;u&Mĵװkc y7:d1g䈑sLhHVu(=z \c{p+];9~}ѯo?|0}11n;:*ݻv֮-{vI\<;? f11oj,;|0AkcI)iT=zrD{ܸ~CcpvCcrvrƨUTMt[.i9FI.]ߌ5'~ as4 ё۷o3qDlllh߾=[leYY5T*111ɓ'*TwfvTHLL {~& QV>|B eJy/_"L/ҡC2|jOV:o~ײsѢE f̘1?::OOO,--J}ѷo_5kҥKV-ZHA:<<ӧӰaC4[KҰaC-ZDٲeiܸ1ϟWy;vȑ#888d,,,xŋt҅r1{lpqqa͚5_])E عsgZ-ejwp.]tT*U ;8vYʕؽ{E6y&z莎G~}�bѢ4onK޼y?###ttt�V*\OӦM9sf+5זm_888 O9r:::4$$-6gΜ9˗{PdI7 ңGƎCRvdvիCZm /P|9-[ʰa4׳g%�lm>4i8Aܞ<ỹ}6:uꤱj4^İpK|y_sR`U �� �IDAT<=j_f ZjR[A޼yo>^~͎;ȓ'�Ӿ}{˗1zjթSG7l"{x17nܠz&7objjJ8M˷kSRgNaaiFe^^^1@.^[ne׮]L4) 4ТE iӦ  "00eSy֭ Uș3'[lQk޼F?ŋ+BCC`"s.\dv4)ڒѣGܻw__T.W\q*hb/^0gǎ+' _<|ׯPdI`�Jk׮H u (vJ1o߾e4o\ GWW֭[uV^~#"E�ӧd7o^V\E'(m vB…,}JEdd$?2oҥR033c…-[64�\zSNѺuk�"""prrҨ1:o (1? �Əqa'F}֭fffo>Q>~ʕ+iӦ 666̟?p�6l؀!޽S֟={6UV͛7ʼ˗Wֵi&zT6@l{qLY֕<{Ǐf͚sA111xyyaoo5SʜڟIVd}VYvEttt7^899Qvm4i²eА;vн{w*Ẅ#xI?~8]ta˖-ammͯlb+gϞԮ]ccz www֭=^^^zkڲeK֬YÈ#^:]vUnz?~VwQ~i&FM͚5ݻ7ɆR\[lɖ-[pwwvJk/^LFeʕ㏩nr 1&&ݫ,4̙ gJ/X5kШQ#\\\ ݻ95kҮ];̙ٳٓUL``,~\`̚5Ӽys̉ ~ڶm @9kw>>>Ջի3vo0`�+Wfܾ}[}"%t~ŋh,1cC IB-ȑ# 25k*a?zk׮)&Ǐ~9r.]аaCBBBR,OǏuѦM͙7o߿׈s.k[jQ0Rرc3m4飑@GGG㜾k׮$+,X@ɝ;|M MH;wn{昘n޼+ejժa*T`[ŊxեL2DDD�<'Of}}} }v M{v1z쉙{?H>+)Э[7tttضm7ndɒJ肕eJ%L*UXjrPϏ?IO~4k ]]tJXXbW/_%5 /WTK~( igble?P%� ;PLYJLn]RIm+tU>7sQ#z *PŨpo(Pvн[wK "?Dr?ӫx"vm3g̤tݢvg4.6\|-[l24iB5&GR5jرCF UN"QQQl޼�Z,ќ>}%J�ߢYXXZ"::ŋӵkWN<#Gdܸq &hC߿ѣqwwt=>TRsҧOVZʕ+塡lٲ={/<xp}m9r$&L`ݺur///ƌCǎ?>o޼iӦz m}iggGdd$С...X"0l2իfffLbŔ7A\vMwҤIԬY%KPti6l0qٳ'111aaaJbԨQ9rɓ'3zhv{RK.kѣv3f_~D| >Ν;'NHHm۶ BW?<,]h6l".e˖?~ΝK=pttԸ TTP^=e^reʊKp]yѹsg-ZģGرRxUlllU+VJ*;vLM,!엸RsMg2d ?n::v-5j`ԩ9{,3gΤ]v5*Kر�ƌôiؼyjӴ7Sw\}0qF&O#_4fٰaør :tLJQFQHJ .ht;In{qEDDp1j׮ ӧO؊YfѰaC\!saggu|oϏMҼys6l@)VXYٴi̝;k׮ѸqcZhgϸpႲi֬YlLWOׯ_gӦ�*޽ '<< Arq֬YM׮]XG@dI˗0>|Hk:ܹ5`ۡ,YaÜϕdҬyHK$-uƌ\;t耮BG,YLtt4zz9.Z'Oq/ey…8|7׮]_~C FQ<Y34onbiդ󕿛7oΥK8sL柉r WwŊTÇ^zk׎SNѠA޽˧O:t('O֖GڵkS /ۜ+VԨҚg+V9s(7͟>}no]]]9s 5kȈ *0uTrĉz)S'N`kk ̸o1B7n8e{Ԯ]Ǐ+?jhѢL"E0ak׮Uaׯ_ m6믿(\0fBWWF7o^ @144L2ܸ ܤZXX`h۶-*Ub])'Nȿo U۷ټy3ӦMKq1cSLQ~ʕ+ VZY,Y2U7qi~TT۶mÃƍФIulmmIݻwСR9rƆ;we&L͚5cNJ ҥK nʒ+xիWOp2k,bVZTTGҦM.]ɓ;04jԈ7o& ~ɚ%igg TRI 8p�-[ˉVmڴҥKEԩSJׯ_3gƏN4Mu]J׶f5޿ĉY~=-Z�bɈ_TVCHpQe-Z7nh֭[UPLi޼9˗/ĉjՊǏӨQtJU`ܜ[z}6eʔs WrYR h\SQ[no߾L]"***oZgϞ1a\]]@q@AٳgJw6mٳg5@ ,Ǐ4H"J{6h(ST>~̙3PŋjZ@ܺu [:}zzzoq֭KZ?A֬Y3߿ϥK7oC СC@;wdȑrp>}+V舵5 'ݻXXX('H�333*U컮]ŋ޽;۷ˋϟ?+ͧekP&!=ybŊeFիWiܸ zAZAGGGi˓ I iӦL2/_&6uC9#?~LPP~IyOF4q BCCyQ&%wܘq㮣C&M좦m\㟋dVpp0.\mseС 6{&8.\X" p*T@߾}i߾=9s$͙3'mڴҥKZH2S@--JÆ w111`nn~\M_ PYvm,,,8s �Ga=6 U*hm.\۷o+eBrf})_۴fJ(a<}T|ܺu 3kTJZF7oҥK1|X|N|R}|)\\\XlRĎ7tP7oΌ3֭7nL4=yA1k,Ϸ$= PdIlmmYdV]PSVNXv-aaaܿ}%1N3)^7o^3jHLM2p�e^ֵѣ?Ә7̟?RJ*+MgϞbn^GiE6[fʴ~:@E"WW/5CgΝåK6=Nw~.E)y)OV\xiXzz&t+V.]:ӿ?vϸM{ԩS[Ǐ8p�eʘ6z mڴ!""WWWOv躉L3q PnG`` QFXZZr]n߾ Lrɿ/^"""##LY֕<LZ]a|a=z1%7.T<y|qrrȑ# 8UVl2pP___3?J7Tʑ#rc_Z/d@@�*TH\aÆq=իڵkSN⇣Ϛ5khѢ5v%Y\rjt1ه/TROOܹsMhhh)oRK˰aèZ2-[,;SOOk.>~iӦ {gϞqy1:i|i=o|mVr(alݺU|ҫK)OŋL0#Gbnn cƌa8;;+Wppp`Ǩ{Pn]KwZ>;Ɋ=_s ϟOHHM6eĉ)^RVz(VgϞѣ 6,EYi];&~.}fΘI4;tt{e޹\2իUW=#Xu]kmԪYR%KqIeއ޵+6mD e4qbء#}+VŨ8{S@APALt - }>XQ #x 0Pc~6>G%C7 AeO?i͓'NMF,U T$9z9)dX]]-\ā4T7?3M=ͭ[r {N:mi p<P=} .(MJ.M׮]f׮]SpaϾ}aÆZYzƍ5Zk8q5j$x)bigqߜTVh BWtiLMMy!S 2o޼Ә%۷oٰa=znݺ/_>$'`w%O<-[cccɣ|9**'N(ojԨ (SLq~n#Jl/^h},--7-q(_<׮]۷WnuuuVZ7FFFу?ߟp J* 6 ooo|||4R8uiI&;lb- ~(MM=?v?>s *U"gΜn:Xy`_<"0~Kgٲe {Pi){ uIr]Oʏ;o& k$~O>&ƌQ>a.] j\RdI[V&/ǯL+S }ueQ`` ;w_~= 6عs=zUӡƠAgnd珔Ηə3'͍cj Ʌ: 2ooo6nܘ`dI?+Op;Op|Xƒ%KRxqZlVOO&LHǎy53f`ܹGEl'ggZnM ##ܰԮ]1z&NćHٳgq&I�zAټy6`BƏ%Ot_NLL $8+WRpaʃ^zӧOw>*͛ϟʕ+3qD)Bڵ)TlݺTGbT*FūWؼy3>}"00oߢ#>&&&*Tho@~ȗ?F\޾}K۶mܹ3#Ff@�3gΤ}*U9srMo؀':T\9AקLϞ/]͛M3f .+ϟjժ [.;wf79sl2F1]taJ֭[өS'ڶm\H:fL޽y%Gf B big+V-[fϞ ݻw',, (̛7/saРA̙ڵkīW4q2e ΝUVѢE ש(P� VZ7o^? jNNN899nj35kn֬Y8;;Aҥٳgt.]`FM.]I̝;W522ڵkܺuJ*ѫW/^xjmSX1ʔ)ۉTRʛiӦQ@8uNR;wf͚}vZnU\cii\]]gժU*sу%KPD rͶm۔eL8 RdI^zqR ÇlܸSP!.^T©͞="E`bbÇ~KUԞ?ΩSXfM~$?XFwÇJҥٴiW#GbccC…iРwѸ6DK2kėqM:˗/:ubJeGJIt֕&ɕ'm%VNSyS]9ܾKڦR=k C[L>'''޿O2e.UT)Zjܹsqrr",,, ɓ'pErE͚5|2111ŋ)Z(e˖eĈ5/_bjjǏYnGx~KKK郛߿' @Xus\r|GGG"##iժFwȪU?~\]]iҤ qܻw j 5i$Jnv?-(TP۾ܹs5jϡCPB˗/4hЀ]v)ebŊ�O9 *(㝁=YBYUO9x޽_o!0.۷oT 2{tE@aAVZqi,ϙ3Yl)-[Pɇ {:YWΞ=… 06.ҥKSV–- _f͛7Q)"2?*Ν[HqC]ܸqիWѮ][دuĝh׮-SNTѝvF~}ɛ7^^;oowˁc*#.+ڵk˿P1qڵk˝;W^3n*A<sQZUK*ʔ1W1{,/_`]ԪUSlٲ8ps1|p<xԩSuՕQFo===V^Mf͘6m ,য়~bܹ;@+YY…ԔSrQ<==eYYҥT/ oooF.]m69VҨ.X ̚5 777YjUMsMaСݻQF*Ӡfmm/¯5={67ofرɓ{*x/αccذal߾ j4vssL>(TBjIy�� �IDATմ~+'O<==t...{ŵk:t(O>رcJB ѰaCe\!m⚘9rrJ5kSh36͋/8~x$NNpuueԫWO>'ʗ/ڵkҥ /fJ7 mə3'zzz̙3Ν;sI>1 8;;{=ma_ٸq#/f֬Yo;vȖ-[7n]V); 6ѣD``Ƶ!9_R%1ڦ{�ARHʡCn\yFR4%ɝ7w)]-õGڄNNN̚5+(H}w[===V\IB8p ^^^t-׭[˖-XYY /ZYYǢEbǎ0{lػw/ bΝ׏_~[N:ʹŋXYY)ʊׯs!x 7^zʤʊ'OQFqek;W:uӧhYIvz(Rׯg…M 7}'9sL|)SPP!%uѺ*=YRtTNNN kK^W]>|`X{j4:|0JWr.B0WsI>?~&M.]~m-[ޞC~:D>}xQʦeرc 4[nV8ə?>ϟ?O zjz~gϦ1+%k8Ot&˗/^;w(o3Rzwy.ӧ*U�*'Ң[r3#=|SSSn޼՗ܹsj=(w0N0oݩL2$N+!"9rϟS\9˜5kKƓHNr>̠A2-ԐLk '+{.'NbŊȑ 60|pe!ロ7YI*ga!" \pڵk3~xN2$՟NNrmۖ/e&/ߞz)P~2J͛7Yd JCT&H?ၫ7y fB|7nja5mڔM JV8InsŊ)%m2zėkt_IQk333_ʹ3;&{7B!B!ԯ; !B!BX٬B!B!ķ)S*iSTRG#ҝNJړ !B!2&***տW,n ,2*s̎B!B94ONoݼNQIܹsA)$DF25~B^ΟK:EeJ%}߹s';w/-H>hRƲ&/B6{IgvYi2B!B!D 4B!B!Y@6B!B!ķIZ!B!BdRI#B!BHw'!B!B,@Z!B!BdҒF!B!" ȶ-i.\1c077M6lܸϟ?kٳ[n޽[cyxx8tڕիB```9€dԩ|2UqsƍܜaÆqU?fƌlْ3{l޼y<x#/N,</**͛7ShQ^~V̌ݻsviᅰyaaal߾~QjUڴi뉌Lr;߿GGG'4p@|}}?<6YR(h6 YR!X55c_ݗGTP=uvq:ss?}?۷/ЧOX\D"Aff5-c$PƵk HC B^ :ʇ1cF{AqyF=Kcرj BCC %(]rrr`nnm۶)|ff&޼yS4 3rHL2XȧRRϒڵk{dee}tSN͛O7ܼ+S^?6 UHNNƔ)Sйs=^,p 6 ӧ;V`waҥݻ7ׯɓۂyکGBB$L[dI_c(*Ҝ?&L v튍7yѝ?={9ss3f VZ%.޽{͛8q"ꊍ7ˌ1.\@Ϟ=gϞK.x?www`ԩuk֭[pqqA2e0{l)SbNIIAf͐///xyyaFNNmgggch޼9lR`2 bРA0`L�vލ{gϞ D5쌈JMMŌ3`ggyׯ_ǎ;Ю];,X�m۶ŴiPJo@+Wɓ'ŏ̾}/]vXd ~G8;;ꬸe[PlղeK?'={IG"y!ШQ#~X</Syϒ:<u ѣG۷?sΊVΜ9ggg̜9SfzQŭ+YK,.\ZjgϞILL:www,^͚5óg>jy#S>YVGSJ;]x>>>HKKСC ]QF8{,T"NKNNFhh(D}իWعs'tuu�ژ6m\]]QlY̙3ƐH$��KKKٳԩ0i$,_={�١yسgX.]:u*$ Kb(_<Ο?5jTXNNN9s&j֬Yӱi&ڵ {N8333l޼{!'�駟kܹ;v,Çq￰gcc#ʕ+&LNIVVVhذe 6 C �8;;8x ڶm+w8-c:Eׅ* 22R٠ !!ׯ_/~XZZcll,j׮:u|t>7 `Ϟ=li_ϡUmٲ^^^sΟ?/N/9bEu]��ADDZh�Xb455qFq9''eHXTڒĿ 8::_|r8mmm �Udqף_~# {..^�011@… Cve*T.r^z%KULTRpwwNJ+�2�044�^˖-plJ<TT7@A###qڬYA?bڵ055-r[@1]6�@j߾OiKy)\tI<n999EZ ]G^>}`ffWWbwѩS'#00�kעB x9s`nn.-$$ݻw/V▱oBt,Z[F֭r˜9sFÆ SwBBƎ+6O>t8OڍP:-66'OF6m`aa'iӦaٰlR:11*T@DD  ׷j=z46lA̙32]O(*M6E\\D"v[ ¬Y0|١sعs'AI&EBB8ѣšCгgO!%% `ΝpwwGӦM1}t 4{Uz?~D"C"֭[�r*ϝ;ŤI|aԩXjZn1cM�n߾ oooXXXK.;wLSӧO077ǨQd?D,ʖ- enΝ۷/ׯ///<z@n~ �PtiHٲC~_qMH`;v,,,,0p@\tI:LOOG*U gΜ "bz4wyMѣpwwǚ5kЩS'4mǻwd`ժUBÆ ѫW/1X ֳ=wO>ń `aa]"**QZ4 qqq.ڵ`CPP[\s@QwޡgϞbgEuCCC1>ԬYiii�ׯ_cҤI:tr2}_/#}�9L|v.~:ںuPZ5CsSIF�--- �###$''#''G3gΠs(UqU@ͮի ndd+,̙3V'5y�Pzu�Vajj  DǠmuyDGGcĉ><<<�+W &&FcXn߾G"$$3fҥKu^z(1c߾}.5nѸ|2u놈bŊZTRx"\\\6mUCA???ܹÇGNNZnd&"(..Nyixx8݋RIo2[|||p!L4 &L(Ev)ڵ+V\ CCCt2OF:u&Mѱ_=zes<<<d(WЯ_?[K9d̟?=z… 777 дl)lllpi8((ׯGڵWA\o¬YХK 0�ᅦe7nnn DRRڶm+sK.An:L4 {+]V… 1vX\z3gĬYPF /_^?c D=OPBCC*U@[[[\ٳڵ+,Yl+ ؈ӒqFx9,XYYaɒ%�˗/ի2)*W\=7ne˖A8rRWRs^aaaŋvvvr+̿ʕLLYnnnJ-ѨQ#v�֑wQΝ;qaذax` 44fffܹ3?~ CCCp\zUuRRRо}{dggcѢEի?^>4 y>}ZY U/NOOǹsd([GIKKÑ#GФI�'O�fϞ ;;;8 V}_#&&m۶֮] WWW*_F^O>Źs=v}-�9rpׯ_+ٶm'22RHKK+믿 Çm6A .�A!))I� DFFXnݺBHHH!C/^,XYYXv�!99YaN8!�d߿_� >}:IIIBz5k(v~Ÿ) �/_נA�@߿qt>}*�?^`[,-- .{|5kBTT0w\AOOO?Lvv0o<q�ÇRC~Ν+ DDD� ӧOo޼)�N:% tE^ll`jj*1BA>|(�%q2(l)..]$�?~\`ڃA-Gaaa,ABtt (CfӦMZj% }V� <xPnΝ;'�r``гgOe "L4I/^) BŊ{ d;v.rӅC̓nРiӦI! 7rrr *ԬYS8t8---M.]* 9r۷o###!**J!�S~ 2#XYYߝu=pMÇvZi supeqvv/^ppp̙#B[BBe.q^7A#�e)C f͚U ?v^</y^,^{,u&L2ESz=z$Bn9022޾}+wu N1c0vXyS֯„zGz\tAWWWؽ{ +.̹ ڵk'|A&? 4{yQtג!ܧ󋍍.A� כ;wЩS'?rTkN\R[Xe "8p@4i�@8zʯzy/՗/󽒿>qFf͚»w +~QyuQF ǏԩaӧO J:mV88ȑ#~.\+V`2y ٳg\8ys=z$v'+$$G﫯h;T\YLΝ;?� )�i* 7חy#˓'Od Ǥ@x-bbbfT)3f ==wޅ3Zj%KH_LLL0h &LNjMϟ… x!ѹsg{?<ye˖wްFxxx}65k&(�333ԫWw� [nENNΟ?}[lʴRU(ם;w`eejժJ.-ݻwcРA444qͧ[jǏ˴ĸqСC>FFFE.o*U;;;ܽ{YYYصk,--ewppT=�ЬY3\|Y x!4h NбcoS[?ƹs5ICC1bF{@SSSa:_iӦԩS [jjjSNp)3H �WSXYADD6m*2x^dVgD"A6m ?4if͚ԩS�r:|p)Sru ===A}dZ%I)zFܼyS,ʔSԨQC滦0; LeZ7%OU>7ee˖-vk'N`̘1`r�U8bql>}`ݺul=}MeժU#/^Tս{w^)))wGGG%PiwǏSLAÆ e˗8p ~jjjUV򉉉HKK+hÆ իeZ*^xQ ojG cH\KIT\YL8'OŋesnڴIfyǠ8ʔ)&M`8q._\mN:1cU(朘ϟ~76m5j`ܸq QJ$qh???lݺUkȐˋVZ!&&wATTZn͛۸y&պyIuZZZ022ŋ/Q4PN^%u͚5>}::w *愒 اdggիWPlY.]Z$J:Р//mi?.hkk+/]S$..}=wޅ V^ KKK+mmmZ T|||ХKU*ŋc[VϋucKau”.]Z|�/~jhh`Ȑ!صk233N:(yӧ8{oRT +OnE稸iV.}VsU^˗3f54yyD-Z(2 {ZGqR&&&8vm'N,8iOÇ1zc}Ԣ#߫W0l0ԩSGVj 6  gܤ�� �IDAT[ �ĺud<v5j$޽{1`�l۶@ 5k֔ a۷8-**J&4o<�A`ǎM]ݻbK̙3۷oǎ;`ll,qlJ#e2]z#BwEےH$ё9x=X4HnE=7n8p ,--QFb= ܹs'N{ Ν;'wdll^z!<<vBӦMa``!C`߾}ؼy3NSU>e իWq=˖*U ;wŋall :M^|:uۼv 777iՓ;xqAիW8uիmmmŋҥ 444Q'RJŋ2ӯ]_±cgll ===>WWW]T)EVʍЯ_?lݺ5j{/oРFpDDD aI8q͚5+npV^ E +СCZ6RXM*ԩSh޼mO'''lܸ{E иqc�%[H&-OEW~%UعrW,9R& s>]XY~k([/];wG<x0daeeU*kE/Ԅ-L0̝;Wf| EiɫikkXn] [�1c@[[ ,V7 3ؾ}̀W5j@jЭ[7Z Ç ;+W#/zwww˼Q˗nj3Qk׮-[:tPj_}||}}}888ٳعs@Z:u*̙oߊCnŋAJJ >|ϣRJ]R gϞ!!!AҥKCF0|pT\-Z@ŊXCE  ϟ#** ߿G\\| ?#GC022‡g@`ڵѡC,_wޅp}̜9˖-C2e ///ŋ]6f;XUK,~z7w9{, ߢE C|[& 1wwwC7wݻwO?ꢸe[ݻB �h޼9 )S :t(+Vl͚5CZХK̙3;vžk.̛7}6&O\C^Faˋ^)OOO$''ׯ-Zl �= `mmׯc޼y8y$�(u=!::/_F zjL:gϞUn-[Į]`nnrʡnݺ�rE׮]3cr�gƨQcccٳpss+4 *`ƌ9r$޽{Wǁ⊊*G~xbTZ:::ĉaggjժ!11.]y[2<x�֭-qyʜ9sbŊQq%,]D38qB_yI;ߺu+֭+>D˪XV[l[n|7E1UO 'N'~֩S޽;BBB`GQu\pA頉,y(Y&oߎlT^] (:w3gƌ}"%%AAAv--A=GʤOy^:ڷo�9)))XlY߹so޼G?ZZZ(9VZE֋)w@eCJ7ЦMwł d/ e_111/𒝃֭[hԨqXYYA__e˖-p(9:Zǎ SSSX[[}ח򪢒 TFF&L{bڵ2 T^qqqhf[nE=```p,^077͛eZ iiiرcv!ih(_<lق0t/Ye~M4Att4V\ǣM6i'ʬ (~=ĈiLL .\@7NcRf/^كիWƍD>}&~i6h�@BBL|�r_L޽111Sڵkɓ'>.cZBbb".]Gaaahݺ5_̙M"<<׮]CÆ 1x`7^(VZĂ {no;-Z|||VZ舕+Wbҥ>}:ѭ[7 0@&{{{�S�Lq*-cߺϟz]4o<J.+Wbɒ%2e пG�P~}\x!!!_```�'''dddAܾ}k֬ ТE Fff&Ə333Gf샔6n܈/_AAA?KO>-544z聓'ObرXjѼys 3ydL6 XD2eJkW5 +VĆ Kt{-;cȑ(_<*ދ7Rϟѣ믿d{zzo~Y&z%1SV^WnݺXhMCt455sFtt3f 6oތ .Uʶ0ȯaÆXn-Zիcɒ%bPza_ظq#:v(ݠKTq^0`�^5k֠M68rg߾}c E- s5J+*O(EWW7oq̚5KEѹR = `ȑhѢ_Х-uK)eϑ2iǧOkhh`;w.~WXYYO>[o[f ̙#oeegggDEE)|(^;LQb+=n޼ 333XYYXb|||`mmK.}vy|I+VDXX؝iذa2 p=_pvv͛ MM4~h{i]9\rrVw#G #G,0CSѭ[7+W7bHYgΜ|<NDT\O"CPPΝ;-[i?yիWG\\ؑ#G0l0ܸqD_H`` ={V/*ϋzk׮1bD~,̋/PJܺuK|(>e\8sS>Mԝ<xڵk#66VfPO̙3HNNVbXBBBTݩQFxu}n߾cǎnݺ(]4֮] OOO1Ic:DGGN]|},iR>}>8::~ARޅTLbŨ^:u놁 8nuKGm۶ D/.]ς&P~٧ODhh(kPߗtђ033ß)8<_B:/_msU!y_POOb):w9GڧOƵkTF-^MDDDDDDDcH PI&;;[-iT,RIɓ^̙3!'DXT|<f'"V~R$Hӭ[b-kkϔ֝9sK <NcF˘zy!"*oNU4݉H 0HCDDDDDDD!"""""""R  ,*%"""""""R[lICDDDDDDD!"""""""R i�4DDDDDDDDjA""""""""5 `H 0HCDDDDDDD!"""""""R i�4DDDDDDDDjA""""""""5 `H 0HCDDDDDDD!"""""""R i�4DDDDDDDDjA""""""""5 `H 0HCDDDDDDD!"""""""R iԀ*A-!""""""RSD"AffK H$r � )) :tÇ\7;;o޼AVVA"""""""*e{<z}gїM%ݝf``HUg–4DDDDDDD*M6O>033+v]mdffbԩT�ڵkQB}V\~Μ9077GRR8-$$ݻw/V޽D"CiGAaffFBBjժ�077D"[ַA""""""")U.^M6puuſ QQQ񁟟v܉Ç#''[Frr8LVV6l؀8Ĉ GKLL ڶm ''']044&<<W^Gbw'"""""""F``.\SN޾/_+W޽{Z*�nݺW<==acc.]ĉhٲ%n߾cĈ8~8b/7oD͚5ѣG̫W |T:_3!""""""R'O`ٲeݻ7w)۷ѬY31@�fffW޽ �իn݊?}+lق>֭غΝ;?JժU#/^7oNIZ1HCDDDDDD"IIIԩ燭[W^J˗/---�@V;w ** [Fqmܼy2]F sssT^LLLp1͛7h۶-&N P%MkNDDDDDDD*r \|ѨT�eܹsڵkms91zpڵ Ð!Co>l޼'NUԄ-lmmamm ?| Ǥ5bHEW�Xd cܸq�e�Ξ= ssh=zCSS򂅅;z쉡CBOO�СCt?j֬Yd^@__+VAܺu 568�+++lٲY&oߎlT^UTȣaw'"""""""U"##qxzz-Z|||pҥkhh`ʕh׮O t2H!vqqY[[�zT^+V0,X�;v(0�/ ;;7o&tuuyf\pcƌQjo[L$/--/_'N鲔_ժU Szu?ϻ%,-- ]/Vz!"""""""R i�4DDDDDDDDjA""""""""5 `H 0HCDDDDDDB޽CjjQ$t߿5u  `ҤIΆRAPuj�?DFF:_5!""""""R3f`̙Xd $ Ǝu֡ZjHKK{1$ \GgϞظq#:w kkkHOO-[еkWX[[cJw:r333 87nY�=zݻwʕ+ѡC"((Ϟ=âE6m`Æ �$&&B аaCٳg 󔐐�___XXXo߾8sLA"""""""4h >}ի􄳳3>}sΉ=zڵCÆ �۶mѣG~ k֬ԩSCCC~:~"+Ġm۶prrڵk CCB߱cܹ???aٲe022BFF&OCb2-p1| .D||<dLy%&&m۶[.VXΝ;}vRKNDDDDDDD*RF TRb��F888��6mڄ_~J[@�*U{{{5 ʕN: �LMMU7of͚ѣtuuڇI&|�`ǎ0a8̙38t:v(N={6lll��7Fzpatԩ7m 6 �`eek׮a߾}2k4DDDDDDDj{Xz5RRRp=۷2H$osss�@||<>|44n� vysO||<�ժU#/^7o+ߕ*UBRR̴UիWRJݻwnXr/syܸ̾qXyV'lICDDDDDDflll`hhӧO#>>GF*U ]^F[[[l 333夭hFǏyǎqAm?3fϞT_MOCC:::rc?dw�r[+Wϟ+_u  I$dddLֆ[ @�_ݾ}Uʔ)ڵkZHEEEMMMpqqʔ){)+o˚d:u r̙3Q\9TPqaܸq,o ߫NDDDDDD~+p |whڴ)�cǎ) 7rH99s&fϞʕ+�ΝaÆASSM4Ç+q-4j8p�}Drr2~zڢe˖�e�Ξ= sss!$$#F'455q9oudyRlICDDDDDDBnnnw[[[,[ K?-Zw.5f͚=== >}X~=ԯ_E�둚 gggl޼4H憍7˗puuEPP44rC-Z|||�GGG۷8q"޾}VZ} @!""""""R!]]]L:U@Ξ=իW] n NNNʋ%,--+SL`@_~ׯ̴w.u�hii! @fZŊ1ydL<YRNDDDDDDDj(""_BiLff&BCC1p@ގD_vw"""""""R3ڸvy}) ؼؒH 0HCDDDDDDD!"""""""R iظq#U/4DDDDDDDDjA""""""""5 }A@TT6l؀Xk^^^011�!##ZZZصk*T[nH$�l۶ 6m³g+W\2<m۶ 6mڄ'O")) ;vV �pqqAϞ=qU>| 4/7o.ӧ?ڵk|%  mٲƍm"11Q\f¬YХK 0�PF`` _~ 8::QFGڵ#-[ǝ;w!Npp0lll 333t?�}ƢEЫW/?~?8b_/!""""""Ro~իѶm[�-m69�'�Znt̜9ݺuׯSN�`ddSSSt�Hi_fMXYYbkn�� �IDATaÆw�f͚!((111^:ahhsBC#7{̛7s4DDDDDDD*A;vĕ+Wi@Tf퍤$#-- 7.TTP�'3O:tG\\nݺ�̔WԨQ)))�+WA ��<y7oވK$ԯ_cHE޿�ʒJDKK �h,WjU I^k֬5k0c xؚt ##@۴i|}}+Wϟ+\[1iTeeeرchҤ8ŋb@�]+Wƨ]6<x�fi޽nnnhӦ իJ*+?#"##eZ�ƍA4% `5j`ll={ 55nnnrG/vgϞ۷o�)SsŰaà&MÇHLL MQFذa~`ʻ;̙1cƠo߾HIIAPPP`HF+bÆ x%:t耽{\r2(S `aa۷^߳gOc?P~}*L?~<ЧO;wN|WRGł 0rHh8#0HCDDDDDDB߿?_23g̙S2NNNprr*0а@7$ O@@�iz*NlluҥKeυ4DDDDDDDDjA""""""""5NDDDDDDDjlܸqGؒH 0HCDDDDDDD!"""""""R iA(և[D{~233͛O+LkÖ4DDDDDDD_h#''Lk `HAΝ; L<2 3gΈ󒒒0w\&MBBBߏ~ �PtiH$�@bb"*T 0� 6/={&fll,F bРA2i&&&B"СCGcH֯_I&wXp!d'&&m۶[.VXΝ;}v�`رz*fΜYfF(_<d�˗qUqɘ?>z聅 ">>nnnHOOiٲ% SSS2ysttDF;)GC """"""V{&MªU �hٲ�M6Æ �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^{NvGTp]deea׮]YGff8MOOt>|8ϫW㷆A"""""""y=�(|>x�`Ѹ{.lllzjXZZ"66yЀ+dggBٲe |;V} !""""""R"&&F&28uʕ+SR%q###[nE5~�Z&++@y[$''ԩSWǏ,Et"/=CCC qss+|8& TPgȑ#;ԬYY !!!1b<==sΡ}U&N;;;TV tƌ�Q�`֭[.7n,n066akk-[�|}}aooX[[7oN<p䥧Ɏ׎A"""""""5j ?8lٲطoBCC1qD}Z x=ԩիWի[.-ZN:wݺuXhW%K@GG@ngƍx%\]]$_!,, [ӧQ~}"/=t>  ALŊ1ydL<Çcᅮ;`� 0@?11�н{wX[[=ΓvaR&=RǤ!"""""""R vw"""""""(DŖ4DDDDDDDDjA""""""""5 `H 0HCDDDDDDF Hw^RSS?iN<MWxx8 sssǎ秤`<x0ѩS'!##k , �&M$JMMŌ3`ggyk֬%K`gg VZpppٳg�ׯ_ǎ;Ю];,X�m۶ŴiI-+�CܹswTGU^Ć]Q 1B'(%Dybǯ FK vEc1FDAA?z:ݝٙNVZiҥ }`�mۖpBCCi֬-ZB occCٲe6l04414B!B!D.R 6^^^Z/\֘4i'Ofh4ݳz|rWn]2e |Qreh4$$$`bbBt? 0iI#B!B䢕+WǤI(U7nZm .ϟ?g͚5 4ǝ;wx).\88U/n9ٿ?'NZ~޽KDD~~~]Vm~ꤒF!B!%qqq7ŋӾ}{�Zlٳ0666ب+W9<RJc``@uS\wիW/_>촖Zɓ'ШQ# IzB!B!r۷}6uUϟ_+(ݻ۷˗HLLtŹ{.O<Qk4ԩs>|87nhѢZ&Mرcs-/̙3ԨQ'Op]&&&Zݨ>eH!B!%/^� )))0K.eĉ/xl]իWcjj~t3gppp`ҤIk.0TVI&gk.5ntMMM}iIK!B!"TX###éT &͛7coo5�/_ZhHHHQQF1jԨ722ݻӿF˗˛!E*J!B!"/^___\]]rj 4`ժUԨQCCC,XF,\0 ,Hƍ8"""x�/^АUszIBB<yRgjjʺuؿ?:t||-[`hh5>\THB!BlȐ!+Ve˖#~͍DFMZprrz{{{/\\\`̃077W�~]Fxx8@Q.]]v$&&yféVmڴ cc9D iI#B!B"===G~2\_dIWC|||tUfڰʊgWWlB!B!�B!B!F!B!"J!B!B<@*iB!B!�B!B!F!B!"J!B!"FС{l߾FCbb{M'/J!B!"3nܸΆNE,vB!B,Y;vv6դ%B!BK&Mɓ={6wwwVXg}F||Ν;h4O8#AAAѼysf̘III].]мysMsӧOjժ3+-KѰw^�8@nXh:t‚�߿̙3i׮֬Z$�(^8ۗӭ[̌^zq珅T!B!_~ 8'''Ξ=˰ah߾=jЦMׯ@pp0͍K㣆 _͍ӧsy~l+Ӻukڵkܹ3eʔ4oFdd$cƌa̘1̟?˓?...Z-pbbb6mݻw_~͛kU2E֭Y& .[[[Ν;1Hw'!B!"TT ccc !C�W#_>}:ŊtҴjՊ!CPHƌÇ133|T^oooʕ+i~.]Dʕ޽;FFF:øq(Z(�ׯ_~?ѣݻZ]K-�/]6cǎ鶿zj8p �;wm۶iiI#B!B1ݺucɒ%_m6ڶmFѨpMnܸA||<_|FC.ODDD7oЪU+>3ڶmˬYtR]ti+WGeҥKciiիW33gXh/Sܽ{Wk_.^<%ҒF!B!cZhA2e8r7odСg>fԪUK+\J+Ck.uѣ矩X";'N`Ϟ=nݚo___%eiahh%JO?`@rKuyٲeyNkF!B!Ee 4.\j7+W`ddD*U(T&&&\~=];w4?XXX`aaAͱaȑ*T-R[5111>|aÆěɓ'SH/nQ5j;[nJ!B!"ըQ Fiܸ1�_5nnnT^͛ꊫ+zzzL<___ʖ- ԩS8p 4lؐ7nE˞={|2 4]vannN%x;aÆC Xr%l… p1LMMg޼y <aÆDZZj,OyT!B!ޞ ,,,?>F4k֌={b``.^͙2e ŊŅ^z)Q+W䧟~N:t9ۼ,Y+WSڷoϚ5k4?йsgKh֬cƌaȑӶm[mF`` <{/2ۙ>FRI#B!B"###|||І䙒;ƒ%K27b3nvh׮]ҨQ#5jB iUXYY(ݻ7{Z6vt֭[� (ֲRJ兗NٝB!B<(44mRNΊ@F!B!c YّĿtwB!B!Ν;Ẍ},ʔ)C4B!B!yT!B!BRI#B!BH%B!B!D 4B!B!x'055l|rev'Y!B!BB!B!r%B!B!)Ν;Yj.\M61+@BB `ӦM/^ӵkW4 �IIIzj߿ H"DEEQlYCpp0Gaٲe(PիWs!1bʕGGGΞ=˾}[.4mT{1c;vĄE~/"-iB!B\vZF=ӧO'::֭[իWL2N:ѷo_BBB미1}tΟ?Ϗ?5H۶miРޘpm .DFF2h 8s̡ERV-s�kfΜI=8x8b^ҒF!B!%Ϟ=dɒ%n� ,-- �<==ꫯx9'Ok׮<~1cpa�(_<իWۛclllԴmllW\sss<x8p ={I&NHHeʔaԩ%W/x}5B!B!rɭ[quU,X?S]RђI&͛7/HORxq�+NQۉ�$&&fWCCC*UDll,�'VVVj 7ݻwy]PN'N*iB!B\ �^z f/_K (�wVZZʕ+UIҥKYt)&MbܻwOm͓]� իP-[dS&c!B!bŊ.{; 6T=|P8w666)R *`bbשXG__?Ӵ7oތ=Ԯ]ҥK(cǎZ-oRg0j(EQ?RA5iI#B!B䒒%Kː!CB lٲOboo;p�t҅(TSNeӰaCnܸATT4 j*jԨ! ,QcՋX@ [Ҽ|2f:3ǏgԨQ4nܘ;b ^|ɓ'ѪU+ؼyOH=_>Ç'222]Z{~iӦx{{˗=z47fСZ !ɓ???uMϞ=9sfu^zŪU(]4=R߹sG=VjբW^޽;ۼ~TTj Le%/o{L}v4Z[&eSK8ȑ#___S0559 ?}4666ܸq#Gk7njO<!!!(w;ww<*IIISL|甫+ǏQ9/rojӦMTZ5.JNׯy;[Nҥm!&&cggn]V{W^ԪU ''':Dƍ?e^"$$bjj{qqq̝;={RNϡCro0dXjuV)iԨ bĈ~zZjwtt$88{… ӵjI͍m2zh̙+W98p�===\]] -@r&..)SPfMƍ3{eY$8qGGGVvvv >ŋa>}JϞ=tjՊΝ;<x0'O߿ON07n-Z˗qpp:˗PBRP!ڷoVҤI^zň#1bׯ͍,k֬YCӦMYvmnܸA~۷V $7믿pttdTTitIۗׯСC >192{'NVZпvͷ~ײeK6nܘxkIIIlٲ &7-7n "" ~ݔ(Q"Wʼyy97u vn0!۔(Q+We{vQڷoɓgm6;ڴiٳW۷g߾}S&MVHdXZZ/PJ8v&** {{{r`֬Y4i҄oG>}غu+G']ףիǹsXjVMvڱb "##ټy3=�eʔAQ7odɒsE6nH=~ZQsN… [^Z5Ν˅ Xd ~-.\x'@�� �IDATSݝ/Ύ;h4겦MbnnΕ+W2kРǎX]C`` ...h4mƣGذa:&Ls.\???*TߨQ#ʗ/ϱcر#7 %M6e˖-8;;gs4 ڵ#::s`-ʉ'TTRk׎ɓ'gYsV^ͦM;vl¨Uk֬QXK1h 7c6l_atI?"";wr-*T�@iР7oޤjժ鶛]^昊iԕ(Q}i_sss̸tR5xܺuS~&7-.\Ąjժu79/>b?Ea˖-x{{4RP}7]#Fɉ'=]۷Ν;ٳ'AaoܸAdd$G.]ЧOu�۶mNhh(͚5`…k׮[.}BiR,5T>9ʕ+ݻMz^ʩSR_D �ɓ'M6jŋ̯=zٳgӹsg5|MRLZN@kP.\Zl5v ]AjNNNx{{g8XT( ϟ?|)S0bĈ7B߾ܻwST) 5G1999QV-:w.<,X;ҪU+OӧOX|9ŋٳgjx???LMMd͛7nݺ(-eLd~K1#?ffft҅;wjOJJbڵt҅͛3m48r}Ԕ!Chu<qF}۵g\]]iذ!̙3ϟmllXx1#F~C1HPPvvv4oޜ3fhmvJׯGZń h֬]taڵ9zc ‚vڱvZոqc"""prrBѨ2e ӦM;;;6lؐeᬮ)a|W >Hmذ7nĉׯ[ny?Ο?>8&%%hq54:ftt4SNU7[ne״@ 333:uԩSfwRKiaEٰazN:1۷oS yӔǔxۗ pxx8?ua\tI˿༤aʕcff3O2N~>cct'OfРA[DEEhػw/ LM͛={V49p�ݻGGG,--yI888tR:vHƍ6mqqqZyN[~ߺu*U�`jj-(my}42h4.Y6mʴ! ^zahh<�CCCN>&%%q|ũ͐<˩^zm)SFk|Qrex1ƍ~r&7s6^I>̺u9r$3gqkGbggG|x5J^\9LݥTԮ]H_Yjժ=zׯ_gq&RW�|�ZݪR;<իWbŊYn 1l[8qݻwɵk4hΫWtN~5 {{{ONpp0FbjEݣGعsg<g:u ̙5;wꛛEQ3f ;wdȑ3 6BRR_}111jq"""SMD c :y7Mibkkׯ9s&=zZa_qssc?Qرct҅ٳgk,--Շ"""[.׏ DnΜ9hт@jժwt9p�nnn,]u hٲ%̙3իӢE 9$7ö#!!]2|pϟNkΜ9)RzMϞ=ٻw/\hbb?gϞJūW2e :uo߾dFv׀&M?`ذat+V0n8zɴixsupwwٳL<)SPR%-e^Ӧ/N``  5|N!d} XҢE uYLL AAAӇ@<x ^ܜٳgpΞ=oڴitޝ_~7obooV&矴jՊ/Sn]߯xBˇ9/;333f͚E p´YH ]211KbooE۶miРޘ�9r?:ubȑZ"+6l ""QF1qDVZ̮eʔaǎ@}ٳg{Ν.o9z429yVʙ3gڵ+9RJ?ǵ^,�Y޽ $WbiiWe'sߧdԨQvmB}?~'88XyN;v(Zu)YjVrIPEQhPvؑ.l͚5y[k.5kbnn.͛@2_aaa ܺuKkk@9rH8JڵKf3fhm' Ş={@ҭ[zܽ{W] 9NV*WnwmF:1<ջwoeԩ(Jhh( ~W{/]�ÇEQN:ۻpRzue(r Pn޼m>mا"ek땤 .[LiӦKeuUEQ=z)gΜQ_zU2ؾ}{ JSEQ?> (VVV{bddl޼9W�ɓ'?Cׯ+(W⹻+(ҥK[[[`XbǏEQF)˖-0(ӧOW}ʸquUV^.^J>R̘1C/2<o\OmVyٳgJ啝;w@ٲeKQKJJRׯ__�ʥK/_*FFF3L#f@,ru떺(>LziP](Rx)GQ%**J)UuV5)SOu;59/yd}ܹsIIIJ׮]g>~۷o+\ʗ/<{,-R5I&)kjSx節-[Ԯ][-?)%k@pFs[F;Gi뛤7>g… Z(7nT,M:Uرc>(Jr矵7ۧӧZs(S�eѢEJ͚5ղR�e޼yʮ]q)r,+>}tyȑ#ʎ;tQ=JF0J~%::H͛۷o3p@|}}ܔq0Rw'f~oV6o<߯߷D\~=]S8ʖ-IDDy&V#)`7)ݫR$$$0j(ԩC=wŋ=.or tq={FxxL2eզ lllpss#**ݻww/7DY%Ef4;weٓ͛9pf\B&M%�jբv\z=zn:8qzsά]/_ͧui[WKݻǒ%K֭[XYYiL7/@ѠhԦi<קcǎ<y2]~֭[ TTT,;uS5n޼ɫWشi5 oeeILL|WZo͚4iBLLz}˗ik}oҤ gΜ0.�HO___~-ݻGݺu3MWw:'?fСlݺ5.m^3Jz|wtܙ &p,[fW3|2HJW´ת.KҒWDhh(7 _@.E[n2JK`mmiݴٰaC4iÇA]\\(T۽Em6VI)tm=%Kr%LRNuC}eٝ=\+k۴iؾ};'OƍL>;;;͛)\p[1|p̙هmq Xb;ۧ74|466O>Ԕٙ5jhMϗ_~ɵk״GEEѪU6lK,fU\9>|֭[hB :SSS3g㠤N lٲ겗/_ɡC5kVիWkm?uDBhذ!q̙7Nݺu-ZCRtiڶm˚5keݺuoiVՁnjúurTL=EUoKÉdΝ|W4mڔ+Wp%vޝ:-c===ʕ+w}V0\BBBӚ{nn޼ɬ $?d?δpB&L&/%_ѣG (@…ɟ??CʍvڇԜxSà.׀LA= }СCz*-Z`ɒ%4j(YҦc``ŋ tҌ9N:e9rV0#iשSmАׯ_m7y:NNy-3ך6ϦMHLLdٲetx{{q1gʪ<e$yȬLRdQNMٕx?#nnn4nܘJ*1j(͛ǐ!Ctz93gINyI?hh֬N/usFZI /v dɒYƍe0o<�άXBMN ޸lݺ}ܜʕ+k.uY||<k֬W^겝;w(矁J}o( [nMmIׯ~Sg9J1j(?x cc/_[r!NyC+uI˗kP.\jժi M.yfŋ9sggg5jDJrz?~[]v]?wTBzAHH6mqƔ,Ym۶f,--uN32)ꚗz^zرC+K 011TXQ듺"M(ѤItyy1˗/w4k֌jժez󛕌Z=~Xʕ+QJ ܹsqN:ENӣACDD+WN7.RvJ|2`ÇZϝ;zl5 #*TXbZcSM|xx8;wV^zޔ/_޽{n:*Us$ߔ׭[CBhh֠C]m߾=YIy(ѣG111>|ڵkOҕќ<yڇ:/ݳH{ua6m2v֭[)^:kߛ[Td4ejdT~tYf}sKev&ZʽjLLLFJeʴ)*oWv"##޽;gĈZ*T7n͛7177Hs6:ٳg7nݻwW(Ξ=iNHHׯRJ|gtڕŋ₳3>ݝE5=z *h$Ҽys-ʤIQMLLXv-eʔC:ȑ#i׮%Jʊcǎa̙~~~<{LGbŨSN?uIIIr N8Aҥ111v}nݺpi+F pqqlٲ4k֌RJ.\?@ݺuQ}v܉Nwԉ 2]?'O_~kӦ ׯC 0 ˼1Myٴk׎V\ɨQu9v7k֌ݻ3`�F>saĈ888?6oСݺuo( iԝ9s\2>}9s]-1|pzEll,6 *ԩS8p 4lؐ7nEp~~~*UJ*{nN>ܹshѢ4i҄ oML)\]]quuEOOɓ'뫾}UV,Y͛sy~g:#$/_grEԩرcY*Je˖lڴ SSS)B͚5YQ<<<ҥ ۛ!M6_~?^k@ZŋgҤI%vСCݛYfQ\9 ՙ fKKK>38}lAsuVX%JĉjE\ ]ˡ.R^xqT[5k�Æ #&& *rJ,,,hٲ%�nnnjmٲ%/_mȎyIkĉ-Zsss c:gjppp[n̛7O"3'OԹ$򤫌ivʔ)CʕY~=_?W+:weto #]ջ>CrYWWWbcc?22'Opm<y‰'(P�fff>TR#F0rH>| nbҥcll+..bŋRjU?NϞ=IHHV;d1cdɒXYYqUj &tyo&::޽{3z e 7VՖ4UVe8q3yd<x| ޭ[7Zh~RnK,IHH&&&x{{o>֬YC.],^x~7m~+LPPLJ*Uzj,7lؐݻwcFTTV- y|hOOO<,\0wsss_077gÆ :Ԏ;9C ˈ#(\0K,Z '''fΜ^zꍄ.׫WRH&L jeJ%Te=UTaǎ=zaÆkUԬY3ƌȑ#9}tzzz,Z6m0qDJUV@t)7o׏ANاZj <G2f>ǏW4-8p�===\]] Mkw^Xpa7cÇg͚5 28ۗAgʕh4֭[9rd3>h޼9SLaƌh=XZZrAN>͠A8wGQǮ166f1tP֯__ǏСCL4/_RB6mVˋ%Jпj5jDB1b_^644QFL,^2ꊯ/˖-CΦkдI 6nݺ1fhѢ}ŋTV%K̙39sMCtcԩtޝ{nP0H~X3gVޞ F K,Q,--ٷoḺې9/ˍ󒑾}rYݻwٿRgJkA̮]tnUyEf4;FFFY'O2|p)г:weto #]ȉ>c(QvZ2 tR͙3gܜ1c�?h4?~<[nelܸ3e o׃077W_`nnιsصkܾ}+++{딙9x #GԩS>}:)-]ě{ěӸ*VbϞ=j+]ZXXmH֭[G#"srrNY`mmMLL;C 2ݻwωȰ`2~ŋߨ>T:Y>}: zNDEEQlY9Ngt .33>ؘ˗/ާwY~׹9z߲>7;},9z(111mV!!!:Ӛ7o^8X+q%�� �IDAT!]r Æ r,f<Pde9$yT^(?y^JRRFkk׮hԡ(:u*Z‚qq-5ѣG߿?gȑܸqC]gccCPP&LaÆFj9|=`ffF.]\B<BQ.\Yڵ+:τ3hР7;>T:Y  DI˿χ-Zݨ?P~򲷽> y'OFQ\P>|0}ӦMŅ{ҭ[7٣}٧O?~t)bcc]v̜9{陣nݐJ!GM }jbƌoCHwuL2﵌yy37릁]ur^x^"88@uFbkkku?cǎSN�4i҄ N8v<y2^^^j3`PLNVx",xJ!B!"ߟsqlmmPB$$$yf6oL~ƪ*U*4OZmNɳަ'F!B!eY@:t(W^E,YFq (@5k7oe.; z*0CT̙3G I%B!BKGz2\ݻ7֭RJl߾|aggǩSP+VT?E9cǎ$&&VܹEQԏtz?B!BzͬY(W^~'|gDEEqiȑ#iҤ UTSNrQ)PN;88ի}Y4B!B!D.6lݺuc̘1ТE 4h�$[Z5,Y#3gd̙t:up)8q"III$$$蜾1@OOWWWBBBpss{/*+-i>Y>B!BwPBxzz.ݻ ...Ư]6fp]F3*T(syj՘;wֲoVwGZ!B!BRI#B!BH%B!B!D 4B!B!yT!B!BRI#B!BH%B!B!D 4B!B!D+4 [n)|\\O>}yx ~~~j '''6oެsD<yNJ!B!#ϸq>}JϞ=tjՊΝ;SݻwSD Y>z!B!B۶mѣGlذ###� 0a;wp¹/iI#B!B"EQذa奵… xyyammܿI&1ydfϞF=8Yr%{V+h�lllz*N2o�ȟ??8T˵N2_r%ӧߺu իGi>&RI#B!B䢕+W2n8z/B||۷oSpa<<<&22A( c899qY m~CQR%ʕȶܜٳgpΞ=KB4hWVOHH`8::qǎf͢B XZZ @TT[f͚,\;;;lmm9w\v"ݝB!B\ǸqXx1۷e˖jE$2QW\sss<x@J066'%#PP!zzzԬY,TRTR/_rېoOOO^J58~8�j\OOO[�K.]ĪU8q"WJmycnnιsضm~줒F!B!%oԭ[W]?~0w^oNDD/_gQLvq޽5FN:x3Ϟ=^RRoߦL2@Ow cbbB3OݺueԨQPեJhmmmΝ;V9K.eѢEZ=ztJ!B!"x ˙.]ҥK4i ޽{n:fgxxx˖-˃/ڵkZۋ">>ɕ&Za\B52̏Fc8::h"~,!)R%JO?&R(Ș4B!B!D.X"FFFҎyfvڔ.]ZkF!!!!GqF('u3+Vӧ иqc�+VФyVmڴ!<<3fдiSC@JÇӴiS�8|0EbŊ'eڟB!B!rIՕ8*W̮]4hЀUVQF Y`5jpB(X 76NVvŋqqqٙ΢E0446~ʠ֭f͚|(PRJ1d&NȚ5k7L8EbnnNXXaaaL>�{{{͛6l?~[[[UuRI#B!B!CPX1-[FBB��nnn$&&2zhjժ:.$W`_`aa󳍓%K¬YԔ5kжm[ׯ_+V0sL>sfϞMŊ4sͰV߾}9{,K.ښ.\m۶'Ϟ=/vT!B!HOO~ѯ_ ח,Ye=zP622dؘ)S0eʔۗ}[@Z-<xp,U^^^xyyQ>2&B!B!޻ǏH.]r;+yT!B!۷o y湝<K; !B!spp!u;wɛ%B!B!D 4B!B!yT!B!BRI#B!BH%B!BEGGӡC^پ};ȞT!B!y(RȄB!B,Y;vv6$-iB!B\t033SNL:Dh4ݻ(/qrr~s ѣ 6חO۷aʕcff3O2On333zѣG1ɤF!B!%O<ox2h 1000|LL k֬ٙ3grUT޽{0`�lڴ�xcǎ̌YfQB,--0(ZnM͚5Yp!vvvrܹwB[twB!B!ܹs+WеkWtAc˖-{ʺ? c*`U(QHȳL3D_! VF+鈖)+4]]ܭH2QTDP){sg|u]̙{G'11Q?56#))Icǎ$GR~~Ν[gk@߿_֭oh+i�����N:/"""ڱc._|%Ik*--&dsHH>zܻw^uL&+33S@x?>7J�����^K.տo}ᇚ1c7o+WW@!666rqq1c^(jbQ;r:w�����+2L"##նm[ٳG'Oxcu޽޶JKK6mZ̷6&4�����X_7xCAAArwwק~*'''ogܹruuU``mۦm۶);;[,IOH-ZH&MҔ)Sdggݻw+<<\;v%Τ����JdkkL 6L}}ugڷo&MǏkjݺ$GQbbf̘={Y֭4qD}WXI����mV={:&MX#-Z׹6`:zI&;ƍ+++Zf͔<mXI�����`�4������v'�����6l`)������i������ ������i������ �����+WEH�����`�4������`k �����p7Ն M&OOOIRNN.\ƍ6mzJCd$]rE*((Љ''Ejٲ>jΝZb7nm߾]z5m4nZh۷O~|}}ݻw_Vqq:t WW;�@XI����ۊWddUQQP$''F<xF"s}z75}tegg_f͚Zs~k׮JIIQTZZ*ggg%$$(%%E_~&NhW_UϞ=xbhРA*++$9sF|^y裏>O쏋4�����Xٳg5k,-[L K$EEE)))IԷo_?^iii:t~G%&&jǎ $s=ԩRRRdcc#I*..VXXaaaス'OWӌ?^O>$[nQIIڶm"hB/TWWk�����`%ǎ7|#___5GGG=/On4}tUTToUUU?RM6$Yjkki&_^Ç%I/^wҙ3g$I_|񅂃$%:}dR.]~ w9B�����ZTSScq^ k%7$ߗE֭[[$?|r-_\zwߙW\メt…:%$$߷lR'Ofg�����`%rrrRIIZMMnݪ||7:~EڵS_egg׬YHWoj<-V<IWmmE@sm����J<<<4o<ũJڵ{ァJEFFmٲE 2dN8ZJԤIeffj񲳳Ӄ>oFzꩧw׮]Ν;AM=**J:uF3g(''=H"����ԬY3~c=k桇R&M4m4hժUݻ-www^P.]qN>]/^3<# >\wyjJ[lK/X=#>}:4�����XFQF5ئSNPFFFm׹ޢE:ېxecyÆ u8p}ǎ׿رc#*!M}������w3�����0�;����``֞V�������! ������������! �����������C|N>aaaZxfu������C(--9bX! ����������`Eaaa̙31chϞ=-[M6)::Zzҙ3gT[[b5J>t1+** KYYYr$رcJHHP@@F]v(???M2E;wn-''GzիW/ 4HWVmm$*((㣈Y<Kd2ta߯SO111Oooi�����{NZ`ڵk^z/4wܩ^xA֌3ꪷ~[񊌌Tvv***rIRUUSN)==]3f̐5jrSnn p߿_oh<yrss(77$)99Y555JOO5zhI5j?\aaazW}jѢ%IEEEڷo<==n޼Y=z7hРA*++;�����ncJC)??_s5Sv$IgϞլYl2JyLaabccUTT$͟?_/((PppƏ/I n:D}UxxL&}U(%%%I+--MCm>L;vP޽uK>Z;uT1Bԭ[7䨤Dm۶OxXI����L&CBB[quu5|1}7Aq})}H޽{d2_$ 6Lx ɓתInݺi޽汏?%K'T=TTTsrppΜ9#I:}<h:Mi4�����\\\WWWKR$ԩS`~]cƌѷ~kJLLtuSOO>ѽޫ׭էQCƍUQQJzwsOqūWkMlw����~eǎ޽{===䤒mݺUF$uEŪ& @iiirqqQӦM뽇M6Zd[U]]ƍKzoXX\\\}vݻW7o.Iv?wҥ> n0=!����Ν+WWWj۶mڶm-y)..NUUUj׮{=UVV*22RT͙3G:uj„ ԢE4i$M2Evvvڽ{ձcG-^Xر.]۷Gk֤F!CĉJIIѪU$|~… տ<xPyyy$hBZj._m۪UV! �����V6zh۷O˗/WHH6oެ֭[_O\\5k|zǴvZ6:h׮]Z`F{W֕+WuiJJJٳgէOj6mh榈M45Iz衇ԤIM6MZjz-Ij߾K/i͚5 WJJ$INNNz뭷4w\m޼Y4�����ٳEs m屵ըQۛzk͚5Srr"""Qok$SNPFFF05ؿGZ~ŵ 6iw`������ �����0�;����`EmuXI�����`�4������@H�����`�4������@H����d2iڵ7ܹsͳ@H����XVVfϞmi �����0�47����VWVTTlQ?p�%%%ĉTi…2L9sul=�����fyyyPjj5kw}ע^ZZ*ggg%$$z뭷4qDiܸq*++See|>&7�����+9wfϞKjG}T .4 SXXuIyyyUV iݺd5i�����R|ƢMmm6mڤ:|$ŋ {>ǏӧM&tr>~B�����Ztʕ,_\˗/Wjj~i}w SPP-[ɓ۝�����OOO999|_fEFF*$$D7onQ7LpMc`% �����VҴiS͛7O:wqF6]vU~~:w,kΝ;+77W۶m~1�����`Eqqqrssӊ+tEGGkرuE=3õ{ns=22RGՄ %K\�3][�� mIDAT����+ոq4nܸzRVVZLLg'''͙3Gs̹>0&Τ�����0�B������ �����0�B������ �����0�B������ �����0�B�����2LڵkWmV\ÇҥKΝSeeoF&Ik׮2B�����p[eeei֞Z{�����8F#GZ{w%V�����`%W\}?L&>,IPffz ͞=[ǎ$hΜ9ZtSJ6oެQFGcƌћoiqwyG޽RSS?k 1 ҥK5m4+&&F~eEEEi8p~a:w$)55UiiiZpL&fΜi+WjРAѣ^~e?<vmmV^((99<wb% �����6sLUWW+--M:r\]]T;VSLQVTRRPXB'OVYYE{I:zfΜK.)''G;wԚ5kdggW^}U=6l6nܨADm۶$^Z{UUUٳg˚5kƍ2UVV*))Iq ӧٳ3g?K򔑑T5kL~BH����AԨP/Vpp$Yr~)77ְaTIIIѣ$_ںuWo'$uM999!$àSN)33S>ԪU+߿rss$5o\{V\\7oٳgkҥ0`$G… o �����UVV&Mɓ'kڵmDawjӦ СC׼#GМ3g4C˗;d2'IoUZZR66674+B�����ɓW_gϞZlz!8pںuui*))prrRƍox];Nyr37%]=nAH����4jH<***{ȑ#;Kׯf{;;;׊+rs+\***}vs &I.\C9"'''o^rrrRII~鏀3i�����#GjjݺTXXh]|YIIIիڴirٳ-NtauUڸqӧOK(jzGogܹrssm69::6bcc+[[[i޼yjٲ$i޼yչs7޶9! �����V4e*11Qފ,IVǎl2۷Ow^y 8<<<tQ婲R [oe>MC Ѽy$IC ĉ鷈ѣG5aiɒ%Z=.777M0A#F0+V… رco<[[>СCom`` -I6mO<qS}k.hp7fL^�׹[[>زeBBBO?pk۵k~ꗊn$չh"Τ�����0lw�����p8�����q,;������! ������������! ������������! �����������5n�����Xb% ������������۝������ 4������@H�����`�lw�����0�V�������! ����� ������XI�����`�4������v'������`% ������������۝������ 4������@H�����`�lw�����0�V�������+i������ 4������@H�����`�lw�����0�4;w>v 3n c�~ϻsY%qqq>?m �����1_~w������������@H�����`�4������@H�����`�4������@H�����`�4������@H�����`�j[5�����]疄4b�����- i\]]o0������w-Τ�����0�B������ �����0�B������ �����0�B������ �����0�B������ �����0�B������mPVVv'�����pWc% ������������! �����������$-Z������?|'O_����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/screenshots/screenshot.png�������������������������������������������������������0000664�0000000�0000000�00001565277�14401326716�0021314�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��b��������sBITO���tEXtSoftware�gnome-screenshot>�� �IDATxyu'; �Z-Q4#KlgTFHv۩$.'5K&SIƕL*3'vd3qۑ%˒lQIH*H�I˻~_wy�$RrO~^N>[>M7p#�"�eUMY�T`4ڕjKVAbDRLK fW( ugUU*�fS2/"/Xj*FĥF"0;\m;iٹahCrnL bT%udD9\ ~=?}6ߩ+s4R]�H 1�̎=P) @wX2B;ʪHTɫj*]ffu L�YUIUDT *BR <^afb""T|áH m!q99lV= ӊhƓw6ʫz!cH@3Lw9g"vL*"VWoNGCێ'Dp{C1@AP13qhbQq#/p66"<n~(9J\gɸp~ fijS64 8vS~2 !"ceF9-|bvʢ9mb"q*:b̎aLWe Hhb{kFUGQTiq"b@Cj`Dd "1FNs.SjSDHR J+L:瘙O&P.Ub53R&ޤ)x1NqQ n2 iL{ƾ839#)m;*{w)<߆IDeIZʬ:0_?,_oML}]JEP UuI{{2چTI&,A'x20hD.H$^UU45pl$I.0B&! DkQ1cl$,K8hkCw{'(8gzѩ**(;*;019@2ÑTDB`f %f"%B۶myف'# P%BٖW"웦oaff/"1s!L򪤪_ҴkXXU4$FU"*A18'K Aњ RUD9xg7} F[/_$"`*1 ` 涝xq2h<o>m(ļ2o6L~RդiBH#�qR_bLU@EDI@(1\d 81s5RDE._]+HQI5CE44h~Ԏ;ƫJ̞XU"H ^YĤTlKbO} f|{T3IK%kne?PAM�!0m21#QQvD\9&RJ@7p]]Z+)A,Vԫ<ݏԷ(d7Bkg!ȞMBj +FDD\`NhMs1CEduu-VDw�B�ƇfIO.:S]d{/.ϛ.o(�vlV'"Q#�%$7AWڧ3D,TzT_\R5=T(A&Tc i4*m!8'!"D^! 1Zܼe7u]+ 12Ky={U۶ !03HkCy 9DBl#bGS)c ;R*!Vc (*~ c 5fA[` DTE&g61hE&RɯLd𩪝ݳs34N\Rq;Ww L}kExe*OAp`fDTDD{"SƘfWFD@G(ORUUs(:+11JlƘ 䒛@m(: #U%eGCkQm`6T@ΈVk~ހvH!N8 BBNKUU?IYY)u9'"RɩMĨ`SfVLFcfnI,("{\&J`&��[ iD` !QbFp4k`M%13ŊivPV s^֋2ݖ6A83KK9h'[ggsxlll!Ac)�vCbA^*>@ү,H�4D$nnUUDȰ 9bxD\AC!�PуsNUqmȠB4J((RYyx,.Qo`UbϔHW",EE$0.f~8`O?%s]QFQe8r\誐VӪXs)JV=,HTX NUHc PS57*ғh\eUU@6pehneUoT14%[l*ιdٝ>y {IM&ƸȄ*DU{!ʠ y<c&n@UQA7Yiv1(#of2p&y]\=mjiϽdF!"v3@Tu<imm9gK8՝w Eu{3քѳb>tj4W~'P(混M^%imn*:UqS;R\nY>05W=~ξU<[=8ޗ!,Zw_}?k7-80L9c*Sty7ߜSG$VGQ՜z1#>cĺ$L{`#:5f_)ߍJ9m4M4.`*M&fPtMu(P�@gpUוOugj"JJY>W_$֯4fZ( �0$;[{+x`U?:m)*ԩ mp^#"&e`&0ks}Ɔ=I"Um �(T U?hm* %CDv�ʂ(�E�Pgb�Ԕ-gc<}Q;ncQsp@<"2H71G#y@Φi"E${4-H #.&-"=ǯTU2{̓Q?g]C7Sf1ڊՒ+=вz˦3jƚCKy2GhY+,bsX{ cX?yTUEImF1VeRSHsrYu:$ "f-ID"_0-) ɑ"sIѩMtnF!jWApDPސI1T7 ]@Ď}6y f&}E 6'GeB_mXf.mϭFNWz~ys=5-<[UA�E|z2݁ZD`ƴ%f_1_*,=CUqٴ[\"T92l&p̆n]]۲uۙF!0J4q8iCS9@X+ā){ qy8Lbt0hmvdGٱcr-(j$/($2erfye#Xs!y-.OMh jΐz;'"Q2D !a!4:"D[$o� rhU)(F !} H@"ha�kc5f̟բGDR9sƤh4vWQP!rZIUmGNStcN5EE<Y9˓Ea Lj(Eu[Ojk*G)oׯt&SlC2@TI3d˹LFwP>3ҪҾ*h1t&)y_*R]>ʅ>K9"(VVQU"5;]RjsvE|\hZ2]!Dӑ0{?RY3,*ZóUQ]vH) ZEV6st3AaB–j,l..`? a�PQQј|b8wU:vr96|/-F"JBU0�VcL̃&8bݸmyڌ)Yj|Ew율"mHT"avن-f lO+R(4\)S3-YjbU@fV:ׄʘVcz~/�w>ESPOgڕ#YfpW\YH(\y3baՒk2M`RBcby_kđDr@JJ@$Z+R\yl}yZ<峉yn5B"u e`T]2B&*]c9B/%c0';@IgӚ,x*3+́M)l5({YEsjꧾ=u\sVl^Sw6?3RRBS@{ R(CL��3,Z'G\Z,L5:-c)H+}ԙqDmBu�A�ǣD@9#5ZZRY""Eeћ/ء3~xc!: 5�yXtrYPծ.LzJNFTS?% ?-**E݄ۮ*&}~Ͻn-ъ U1ɱ|[4fRb帚d^&)SSUUD{GئjD@<O1Dvd9ض57 iB9nq*zn@)@1(#6"bAQcHˠ~҄/RziLm.՜_)+T+t~MV!,\߶-\,yTWUEzSG(G5(+jYR}݋ΫR7{.1=/|C3T<+xگViK(^"h 7LZv82s =A@ot7l)Su #ў7԰ ,xr|T{f |ZruI9w*|FF00սy@`v+?G-{'JyEcGA3p-Sb؆%N%@57d¸iɄ{"" mإyTqDTTU%Z swfִv8j 8 3LJ$b?8dŠUl_DaUUیrRh4n 1F=)RfE+ pF5g)+zO`uZCMT*tܱ~WkZګ0pC%ԵE/o'l2,<մd$߲_rL*QI_JL4o{W~]׭4^3)t*E-=�13}tDULiGtEi$hfNU UL)eF6A$jG`uڧS:AH%[[ *}ɂ>UT؝V1Fb4!kw3U-fzz g 25=df} sNdfeJ1Kc4BDvDUD Nu$Ru+B!ĕpu!Vmڶ+htic4 \ƨ[2 V1M'%&}Ŷ1si``9ESkNTȂqAةTV~U4b:ǘf6j*XZ}]ayԀ<?|" ( !8b,ԧKӝ(2"YR�sc&Dβ3BfCܛQ.OL?dbvbd2LVd2aʊwTzK!=*;;m31QHPQ. }ĻxZdƛNqt)YRڏiV)fyۼ sH*h|enR4ה{T~)_#Z4[U7P✛Y;Squ/HDs׬lg~O5DDLhEB0+&˻Hlw?j5.2휸:-<Dl3NˠF�.fvSX]8Y<F疮pJ!cX>X ک8D n$3 vzXJ]M.AKzTnj4LnKOmSL>f9 cAJ N)vw;f!jt#P2KCe8c|QDb"F l)JRW\y �D$J'hA6IQ$ZPDec#+e7ůbA>)9\@XvFr6q lNsd^JjPzsYa <`A`JUH 3Rhi&i}!U'L%L`zkIW3YeZI|uFN@lXJYDU>.";l)�̢H3sjvL�AAmN"rO;^%K[tSkPrLr SìgJw*6I\SOĘ\OC@iuJ|$e"yn VF9H `+36-9�a҆6X2㖤ꜝNn/MD@{�`bhHUCbnc Vl!6xXB"s3q! !0yRP;E7 g8 1LsNSεI {:XYR+1ş;oi-UާURᢍF4sbvB^vASRϚ]7FQXYf@ lr;( eu?8\8w9-u}Vq2M #ԥQ*j/d6)Iж~&& l? Y"8 yۤYu FQl2&n*s rVB-xo�Mi:KWZ%r^59E%T&qui6f jTV wѢxj~VyTV&ZI_\+w3<}Դ2 rg"7EFjH1T}-0*vxIc/b)M.TJ} 6-)Sɼ].V-*m-Q*I5PyHKuPUjAq28vBnbQ!H9Ezuio9ٹQ/iqpfMIW+@aٺ3:QC,eh2UQeγ)C!2Yi)Ӥcyg[KHӥ EXIA#eFDdrr4M\R?RoGY`&y?XX ;n\-j1H 6QX8*8_ IdXIEɦmM']UYhMcGYQ18SWEUUZYPl B*҈{+s%h*6TVMg!!c4CxDjyR&QZq_ !&3&ʒur8SY:ձ):WIvT7kbwK.1*{OvLFʹ@"Al/2#*DXQi(VUNsHk&3u’v'"2[irr"DL6k9S+;Qc~v8鞚zQ2Zғ^4G*SUlb�L0vdz9֪Ik+"Nn\֋,wH dtάLYQ-5ff\QiNZeյUfgv!xB`ri錺X&�U0[)jgvaCr RT˿*&r+Emjh6RbWI߈y8˻0o5dX:NY%3 RN$L`ԪԦ~]*∅Ak) mLJ9Wݓ\;@6,OԼ{dJKwĪ3.$m`W a'Yt)Fm!!Z֍x˖AӆFDЦѢT|=*)s4e ^1 bMPk1yz1rL JFtM3S.7G^(Vu�� �IDAT,^¥Kћ9-=΂MlP[qc.xd5棝{-P+y85 'g>(zkP[aJ֋spI>K,4ܥ7 39EY_l%$ϔ2֤RrذmA5XHnJ&AjyV}.}zKzSQ d9HA}/ΞMuY{_>)uc;dj'0�ET$+cb$$4q5b<̠X1ĶmUısgcQLHl m t3cdj4jpNEH:rf(I!qE0f-{ N~hfT> gjX453|I~޽>MgRn]T# *?ic*+a�[09$AM^)oTH% fM9r^huc50 Ŷ'!y+ݙAҞ '(1LO1E<eqM,2mJEQ˒Kȣj�DUcBHB#%jD2Bg'STS&�MБs7Ow ϋL%-җ sy|D:-de \X 1,#LZ X(@ 3l [f;}CJ%ڦ" sLL:�4CU !83 AJU5FV3[ν6x w$D QNkgL)_ @hfvE5g♢dѓ&UhH:U~~RWXd[\2v>u"HHbՔdZ]H2Lԭ<@A`ET!(BBQcm( /9{fe$w@ V͑M='RA "Q1Ug#At~�﴾[>g@�MPH;$h^;E]*U𞦺i46C$0FB_v]:׮a& *lgod<u բ7ڻ$Nʔ8(V?1Zȃ9ͫ-ik0A&Q<z0uMBl:kUʓʜ.C�A3^~>$[Y6_iI 2?ފsTT}Z$65U[ٝ{�?tǧgjuQ\.OΖllQ!3`[��Ԭne0Z� gW)x2P/�(<:#zSf1.9\L>8_Ie%s.+;aN'n6*' /iwVR;#m'}|*:͜s)(TQ=uj D1d2L&zeH &S4V|rǥ(&D~G�M-)ej$ VXj:;j^n_E܇F)?{`0m̰ߨhmdm,JQ٫E~%ӐO};N/Y+nԦZUy3WړI\lndX%"^^ p̬!N&!6%0٧7 $@Ҕ&L& 7 OP�#Y$&)oVi5f*#bef@αwv1D%@8`yI9A 4޻b8J5yZA-› DԼsif$S JWՠUYklaN.Uȷ?-\BL$9UC/ ,z*=+Io%4ɤÖHMJ !NZQ~ů]9* ĥޮBuCs LLy4G'DaJq֤zĕĎ@,Aftu7WJE}QjOfhǿk{'4U<ҸFJYbz$7AGB9P&Z{-%,a KxEAرg)n;R(|ڞ@mlEq�ΜDiVWLp1&*L>&\탚BR$zyC+Mn {g{1.A3i>. nhiT\1)]H SС)%ةxܬ4~TCk!fT}0385Nj�DBc pD`lWĉ+#/Mg 9XbX +b=->U/S;eQ %WB~G-d8JjQ`Uʍ❳`L 9vk}6|ef鰳"]lwÎY/ WfЄtw,h@S?mȁJQ a@FѠ"CL'fLnʎ<1:v!A6_6턉Chh06MEV<L{L,9 T8cr-%PD!R8flQC-Q{ffv I(д/J̞B6,)֒<?[6;v߬`j]BPs1E`,! !+Huܓ ) jǩoRH$j++:ݖյ_DÕa$915D( Qxೇ575l9si><sw(OEPكHc%( shGW#/�]v""gN!7eѤ sf!ssOVv;όzF8E \}B>EI2w K'դ#g!ٿl,%1c9+lw|8iV6ޱi6F4y̆ʜ~*ffeVn}4$lxtmȤ%,a 1 "fwEG= c0 4McDt_ef6%"ftDf2PɇDR ,tRbq;旤jP!;b%15&ƈ͔2n{VޮNpa2rVU.r9ՕՀ0Osmf@gtWIՅhl-ΑΩ{�$\PQEZH;XBtOd3k1 U4~sNOig8)-HҎa\0*gͽܑ\Khn~TQdt_EO3Lv J̵NDla:S8OWtX&yDQLQs$4ϳ#k˜=W|UH:(:Ms̞U50"i8%s!;:52#\|8H&:騑v!9_Ns_AP2g=PLⲐ&SFxL5dCS%;kUCrnbc:/ ԝ/9ȌLbRBq<HTD{= av#]c0[yw΋DԖ.m 98g$D$F)vb7A:iF`Dv�DI1H"1 Ykkky4Ab�g7)�)t0Hy⭿"V0n6k:R@k8ɝi /9dˑk!7F)z+.B=Ss/6' C2Uu]4\qۇÅvb$89%,a KX^QPpM#t0‘cvEqV6vkɶ|4N9I�JOŔۢ T+7i P�B76FPeyǃ`x qtfX@P\'/OCd=Au =je~b7 Pۤ`8 kJrgϑrQ3s.HTe!.w zgc.9Cn5ejmFIC˧e;~$L ,RRa(,u$vw,<Pkw{WOVv`}b\BYNQby`j.K38A#+HČ1`GcBc5JQ E)q*�V8bd,!黎%jC9"g׈H *�b(ªPZcKC]Q_$9`<mERr)�954;*n.N7lHE|V|Dj6 F7M (B|QHiEI휝0LGm}ɬcnb.%SLFݚJ670M&cjةF(5Y'1Ef#άB۶anz)|AT&=nEb ib3QC@*J;S&0"cWS05#.HS2L\yJ9Kԯv'Ӥudq衼hWsՔ7�^<=3fT}jm־^tnNi X.-$<7t*DTPʓ1qn%,a Kx^|(Zn%"mKM]=Ǎn_$Gڪ̙L4IB]#0DSp{c]km'ڙYg蛳_wˋ6P?;ʗ%o3i۽H`Iē/4շs)wdY K JQGٷ~Pq'ʗ%,a KXw㜚@{"ƂWuz(`f 8D왟gfn9-a KX p39m̭ry59},>>%,a KxRe"\:B@>kW%J{atn ` KX%5"bv?r ; SU_/ r)O{[oڲ7�3t%nKX%749he;+r pDІ]Ԇh'@U5j D<q=%,a KXh%Q1gJsn]]+Օ<  XG©eDv\r%V'C'w7/7Жˮt>_-a KYR!yov]h4Ls1%c")W4-\Qߑ-a KX%|ƆEH D9AfNOqLJIڭk?y ^uwdx?Gޱ/�P} w΂+u7?z+α0?ۯݾ Y 8b;e "y܎ƨP$ό!B#1aX0v}%,a KX0sDJ  X!Vo4DM]Իsrl{wFˆj q$Lzhc|251 u\:pW^y;~y-?_?>:扗o9z{~G_S2Akzы}s,a KX+**Ld ;= 8.23T's>T<u%,a KX@ȷjI*Ĩ*1UĄ(cG=u۶` :y׮ ۷o/xe'n{WDWG@rsv_ڷ[l!dn~u[8qmU~eKZqw$Hג+;;;W眓rǓsP;f߾;wh4~}h+4%,a KXW]XT!bvbWO۞}"bát ޽^8uZ^~y v]|áw}ϞRz^kڀSGg>o(}_;Os|w-?ܹc떕F/<~_xnC p׼yáknr{ʫ{|W<rS�%oM>kw}x.`Ǐ|ӆM{o>gtV~MwuWܵ;#ͥ~᏾JǟWno~7CwW<{߯>s߻u?!g+sR.φTLi9*P%A/ˀO`gصs_\rڅ}ǞWha{7xţ^A;/;xv=^~ KXzZX*XUE "Ѵγž۵?s]'O߹Gv3nl\p-eW(+_͇3~^iK'&W?7z\z/رљ1^pM8�d?o5=챗ǼI9HE8s=D�X|x[p:ذ@F6Xu?j^z~ˡ=::=K]yVZ\Gf ^q%͐~<s>k>3^v#?|?tJ@{.lbq4fMhmeozu`<| KX �<p8cDLNi[86wuWA ιb׮]/z7^-[Y[߱ŝ ytknz+y;�=x Wڿkmd:~%,a KXlH´w(J$"erRu Y-{ p@~߰_~=.7mN~u;?c;vݿwʛ.>}_y^|7b__/?+v7wN0#!Do[G��۾]||Ę׆LN_Wwh?*q߰On_K=a;悏<rWgy%A3#•߻�e'NDS-ض 8}2lt__]]{_[nOݣʛHKXjrcD1DQDJ%YeAW妑7_~18ғ/Ԇ ͮW]skGg^|o<1 ׷4@{z<zb3q{.\]utO (vzn1>);'rW^le̷ p{^[Nlo-a KX9aI)!]k D� pp0ϿyϞ<];�'G޴r=?I_|NW7"=_}/n{:_|7劕v/Rd=^<h|ɧN[Wmu�s~\;MYxѥ__+#nc;'>%lb{c_ g{N8?۝}x˕W_x5;/ys (@8uCOU}{wF<t,a Kx@�V @,P: kP(2;RDDE%<I9 /[ݻw]~e�}쉣G߷oSO?}^c7+e&ֶbރԧ[AZt{^ UQn]hߋ6O/}i,/  ]{|3糭t[Mq{^S7/[KXYQ eP/"%ps'(d}GMpߙ޽�<Nt{,z>$_C�g>{Ogy��!,#?\ywẋ~W=H0ոbK%2/Y*�}}ns/\}po7;�� �IDAT 9|ݨ<n޶}#o۾8s)#zu9�]KXp)DŬ}�"Ҫh:LIr.1&R1A;<;^x7Mӱc]tх]&Xskmc<{zdk^sekWR^m[V #~+<~ڶܕٹB#O@~kp8 g}/?vzKnU̟sTپs7ON06} Kf_8 �{}ߏ蹿O=qJ Xzٍ7\oé_�Ж NF{~rn ]�D[.M7g$킑{ynmy>i n.xSc5{׍܉'?/Wt{w>vkc@~э]o-Æc{{>]]B[ly|֯՗'qˏ\P)q?曯x϶c~OY9s)ҹ%,a 7x32T($qNc=]v\zqyΪՆO_?S+׮?ypv3aϕffo }i̳Хn/@{{<vb=y޺G~O/~j#G6{0`s>O�v={$d[oCiW- lNU徣[2~\c_%x[#R,&[xu?O<L<ppͷģ<tӫ2s?vfǭ{2 8SO%,儔PvbAq)@ jvM3I;PU&ލ鹃˲rUg袋.p8<˯@xy<(ɑ'{~Ǎ.lȱ oyޝh^wࣟ|d,7_=89!|jgRObm߁Wtv2Q=l}K^xH5yE93glz[r횃dF#8Q-{f>W!3n_{O| ,l}dܷes} truܻ{j]Ni0X_5`Hv[/X|nDkeI drF87~opkEG�`.@ZTe͵Qk.ѩ?rt~r؞:R\ٲA6ykMVylUKXIʔex(ԗZRg?E qx~ݯ~:t<pfdexꞻ<v~r''^wWW&?.~~_ vE{SrU[/ ׎_~߹eǡw/9~u;'fۖmy{wrۚw:{Nmry֭~?q5[~^p 8Ż9'~rn[FG>gO 3.5$"jr0 i`ff&jy \zk;I{˓@o$[vleQ̕GNt}@[oѴ'}m7-׬Uq+=?ɯdw|/K|knw:y '?ormUw3/=c<ޚ#內*gIV-N7f/=uׇp||޵=wsu+O.9yE9{~Ϻʞ ]9!]+}c+x-[׿x$t|;>m3w齐BB(SQQT,""Uz5$ەc. D9r;;3O1q��!{8)vϱ+r7mN9n8!>,gS=T%3#4_g"Dƀ[F*?=W[U nnYAU55p~ἔb[E&)(W~gd$=3eƟ8w "979AQڥKlJ*�ʽ6LL/:G)?J,B2ӊ%z~~{)kWw成qbgpl(2I /m3IddW@:u\lodgC.WG@{/T3/]-0^>u!^X}i& {6:QFBJtRrVbb YO{rDle@P%D  U( �47@ %K |�juFcI223.^2*%5 ŹIi Q^x`H= 89;PG*2�e߼e3Gf=u ѕY7ܽdB"3�ɤ 2 WV!kG.m@z~5[ZRO~ҵ<F:oOw/ &3{z:xz9#2L�S"4^|\tj*Gyypk t ҃!sy6Q-4pw} <o TT{zd"5[yn*@ жXjP. ()> :G;9� d:^X5'v%(v<GJ{qՇ/[<-c6Os额+C�P~VˡčXxU>uѭ�4o]LkYaEXU!z{8[Ml^r?'��xWpzM�@cҙ ߝPPbV̋)/ZJ W@dFU`!:ubD}Z֩qz~~5s-s2)j)U\^Q<#�TJ1\ɼ~!%2b1 WT5bHO8pV lۭm}'0KqPY%R*!ZF?<%/:5ϱ|6[jKdCGp9llv@fדSlcEhhӼMhơ �MiExؙ.\L5 :7T)!*bwʏh # n.fU'@ ܅2( PF%*!y&+gMVN*AyHKģ&%*=Z (rAX p<j @)]wuVrr_zj֭ZZ? @Q))5j'&DȮͺ4K)2<iND)R%hv^Hh-@4g(>NWPh$D;g%ʰB.ށ<#=Hd(V=dٮ{Y& ;2d 8[_A`KF$YG#H?z4ާGdi>URkɹ\�d]R]r(�T|DvA})3; ⴣG7ujRVY{*֖wv#W %<LUTҨR0{JM:z%֫aў~~',HWDV b@P)=)5QۨF@B؞. @pT@ 5/"2Ԍej3g!(Ftuԙͦ*)!!dY_n-C|; ǯE]{Hg_9dbt>JHG45@יRޟ\ވ1$RC$iJ)KWA~>MrЖ)y] jA3g@ CÔs!cc̀X~-) ̺t ͅ ClMSfR@EcC}:t2gzR{Ȫ4_?{w8W\ZRUߢ 9ggOnLvβuVJ]Hqoѥ$ٻpSantu6Wn@ (HljbOGfωPӿ>yJyN|;oOzWRROU2 qb&Ef$"?1k2E/yBP(z`Q|̶^O38WLY: :f͸o�@QƱO^K39JU:�`kI9&pSq^Zrܞ]k}LWK7NoLFubQvdǾ33 L 8Xڠ9)dJAɭW 9pS BSʵ4pukY@LF%?mc nSGc]w,cNPUڒZz|}u\V(++3+N~j+tól#=ڪJ wL.9LKkP@dֻy@ (MNV$Kc- ňHጡ,˒$##^l08;,L& MFQf$K(|j6Բ:YW=�SSjnGPmmBu9I;8qn@ d(˜FyXӁa@ 0!0$H:$'CI0͊Id 3n2�W8�ZPj9ٙ椫W^(ml'J@ uKzAV 8\RJFP=P J @pk&8b"*8CREBs8R\ \QO�4:D5k@ ƲA�̚P[V�U)m,r"$D@pX9Gƀ8q!c̙w6\I8>@@ aԭ"BS �@nXK�[o@ E箜e.IS3!*8�`P$3@P9p�(R@ wjh럥ߗ$DT! ÷Y@pADH@@H"CA͌ȀH1F"p0@dd5P@ {A@JE.@ P] )r5U�$Iā$8S Ș�1$B.B @ kQ5*21�BhI ws8P*i}Տ4�@DILQWk@ mGs`+uDdjw~`'@ }E�* $2�nbvlR82PBƑ" 8'n6q6`)@VJLVƊM}@ H$Ɛ1Ƙ吉@ 8%C�5*$Inu��z^$^"8( �8!@ h�4C8%0Mz8i �TK$k^TЄ @ X 9D5i %2@ Ah|eCPCUYb@pP@e8LoSLb$;UA֕:i{|@jCkPC� (Z+3ԉ<@ BsdʪK9j+@ $l ^N$ CDuce!K68)ج@ �X@@MAӁzX ED9 @ @?uŏh c#0Uu`P jJ:@ w!8p-44UH$8W8'լ@@ps�RIBƘ2@TC�0NˆP.@ K) �YO3Ɛ1I˲(#Z ; R}Օ?C@h9K�8&>P:a'Tg7' �(ILf�$'7;wJMKq\FR|a^vHЫ~`O): 0D`j&I@(I1:#Wę,q��d 9qUop9@ 2$3NpH 5-! %ĵctFm1Z9D8;;׊ŅiYcۮl9CzVjAKh oW8!爀B+\a1Ir!c� 33JYe]m<C</o/7װ-@ ; $"J5$ Ds��ռRC@d53'HMMݹj~8&k+ 9W;{5x;r=?[+ `ɇ Dj ` FɌ8'YTk_�HHLdL޳CZTPTD;OM��GOPh @ F!$D-͊\!-}‰yl+ PIƩG �H]#<3^ðo>׿vݵSӿy?+)ಯP알"n(ïN^4Ɣ8{x}Z'nT��#D5c $@uJfEI8�B^^~^}j̺hӮ$]-,,Ͽ�gggg''QG{}/N-d 8!B$aTF`B ֞6!( rHKؿkk>kz5dlzg=VCxguMGx*ZIZGL�1B|q@ � bH;D%GT99'ӣ$LFs^N_Whdg5lwŃ I/=w^-oO*L];)))00v#X~y^{QYdKˌX R2LBj;/?65M5#R/Xy }Ϗ,;?/2¼ZpJTб]ڽREL0|�@OnxnfǙ�=>[-zӿɺWS3suN! uվ@ P[�0d@9) *M\\5= Pbc5ՇyhX7`6>w&ax!2hinA 5QKD "P~/'&6v-͵+ ͅnEk9>$jr0ǬXSL��̣ٳFw _>}֯uB�ssFwg=#x? XrQ\J U_zo(Nrԁ-}a7?W  B`V2)@R kDTD@. ȴ"jûĜ�� 1`0EoO*LHJaBTjvZH ]bfmxg"Gv1 kW= !7n uU"5xI#u�Ҥ^2[O�b/)#+A�ھ a&u ͼ[6iȜΘ (6�)zcLb(tHs9WEGP ηd*v!2hiuTzj$3 0 W{y5;D_kj&[ e]aAo}fbM\[f*o'Z= Yx^n֭_斂[4`'R9�N-HՆsR1ܱ3ml"/.6Ilif,1��{oO7_6~ wB [ ia �$+DD@D$KdooGD$IH6#"*WKW @ADqfW!FDX&l4ZFWLGjEfsqAj?\.(QQnsFʽ$�0#U JN>|e=۷  cعJ&n͑}FѺ/NhjY_ Ƹz{L?^R=c`}~ʂ=IaكPj7�� �IDATz{{5E;Cٛ'>K6[7ש qYYA% = jqZ荫X+JҦ#iӬa>כ..j {WOw7W77a[T9̉4$۴ܣy㈀A56w7Pޅsx_Lj&ͺ?ӬBK#g_Tm1i}L޾&Ws>C}C";t3V.jt]7஥�G@ZC $N'dDW:2'|W$"@Phk@D2ZM8ZyCԞ3PvR~e1ʿd2\Z415hܔ'2ޠ*SLN~lzձ{/[/ V;ա)qKSrXd(鋟iЌ3LW�`}dMvh1Bz�ʕg7ܛkTo8Pd2϶i5nreW, J:umk׿p^7af= ;?o٥cfR\VFH�.;k/-E bwſ>^vzwv]:�玝,pW�0?|$th o�1)j" b�G~s*B~c/ 4Ԥn w5܈Լ) g�1Itl I&5_j[b/-_z+;bk;Xt*l $)1@B+xw2�t�QB@d@Հ70'(!b�X)2sʿk]tkpcT� e(u]Ҝgʫx.2 �K!Ky'=7��&Ϟ m>e] 䥉f`}mRXv^!2Ě�gJS�1�w8l�0ŝR�ڷbVN|┵5ܔ7>ݡZ>HA~9e elR:AxϞ~x3�8uQmKq̑8��:N?VԣwΟݙBS.޾>:DkѮ%ϛ@p7YpDĹEc-!x�,f$;pҎQ~ܚ vwtר?ۖ/[]=}=V /8rqןwS;MRn.`"i暄 Qb:eIXm* @g_u�3Rgtor +=Qڂi4tfð~_}k5qg蚏[sD1u ]<]L� }ѫW/_;צTqZ̵މAIX8#�~}icǷ:�EtrG�o7&+UZth}q@߹= ν�@Iq�O:y:SmLc_us~}mfg=|=5hм׃S' Plʼ)=+g~tܙwom%0vƊ$3�{#gl �s|:tڷ^O>z4@1v ?e�әCNjTUE�̉{T_NƝ?}"RCOuM='7@PDd�T3Tjq�8'F`6i=,m3^epzP=kV/X5aWKz/7{{<b?EO~P@pDd~|m\^5[h;L;`qٻ`A~Aaa;9[.БTru#'-=o=CZS:~XgR$; @՚m`t:;;;;{jN` oe))3<Sh2KFe_}Xɕ07I .$�@?@c��#7b}r3^ zk7k:k]Ę�@IFd\t?1'k@C&<ӝre˖f(>}✶gߦC &sWd:soMB)%!w�/{-DӱUk.jkБ_ygdhGf|F[5O{|�ۻ_{X>ĮV% (IG̳W6v��:g³Ooe�[pnQު溮&Ք ho$IBd,ICY@dT]@SSTcmژ//4}?xW~AZ3߸co^c! = R??*iLG4eĬi �3ozNNQ]}ss;x5h茭)&TwZe=5k/3E/n;~}rW|6nŔ}V-]nGuWa@ ۰ђQؐ81R'R\mkBKˎn}ǢWC!wv.ddgF9\?l.Ȗ5a-l[sC8uB=yY,ڼw@}«Wn8b-1ѡӀ0Zd=g̞j8r蓟LYB%TĊ9rژ&Ե3k.�[ 玞P{վK&9Bo784$$4A^uϠ[~nȳ n>iOZ",@ ߯&JAɋf�ԧ_&={S@u`ۿ?ƬKASW'߬13WN)1QD p>P$ꨂ%Yt,0Wcy+>LyFz~n'o?^l�2&zq؜l\hr?2c({ۤZbw|k �ⶬ+6oX8v$?? ʖ:7}aM 4GrVCâZFsPT? ^@o(>r]C>cղ!۶iױkcg4rO_4c=ݢ"Gz,|cX!}y݅hg6WAMxvY�(o=ѫm->>mmեܘE 8$AdW�*ifSGn״Qp.&-=:>1YQs?_J <m7/m0Q>*Q)Ēv  [=7aX C#[kf򊏄Q*3m۶:ks~|E_6^?h(4$0/~~4^@`.6ʦGhѤӐi;k[*lߠ?Fҷj �@K��@ ^mƶ#mE&f�Pp~dˬlO .wٗqz@ұS%6"VjPAnrR /_<Kܣ`$�*/5H}rعMfzFpYg@vhߡ@ݻڵog t母]6�}nklqHEE7|P? :Đ/;g u.۲,]yxr`5 �H}53]qsA>M1f;VL�]wuҤt7m2F}劁xQҡ6⩦Nw;0|41 9&Y1T8(ܽX4/r9lաbJHO_4# Jx/�v`^.�=ЦMq:4O[O|gCЮqc1i��)Ӑ],w6��ڷu;[{Ϛ+%5`6 KG+.<1wO:Ǟ]nb+­T}Ә| D Bf3$@07VxP}Υ4-;;P\\LSnq-|Wwp7g*A)Q|mEmm_Q6~7̏__|dF^rZ}NRa(=Vgҳ͓[>q= #!ӏb3y թ ;{nݷ~;?dpRe׺dޙOD ^x:��}ȏ;<. v4{v ǧxy}B i9A#UIG{4SKO;uʱ 6 U0'F2Ϋ٠S>z@Nw�=~6:@gݗ hїM ^ n|lnjǿyܭ) @FF9qI@PȌʿ+NT+Lkqǁcit.ѧ1q^xT�tpt@;Pd sRp7n"i^�eeiY ٹߔw_֛}Z~P% -[wHz S�t}zHkg1O\2�.GgUPUR[~1gv-Un6zu9Jgddڮ?UU^~vvIg^1o%29�SA\K.򿎥ϯ|ipD !MS@֥/2F�"1d13$I:Y'Tl HL"@jDj\8'`ek?=Y1 ƤC|YjSj"qKjәӞ te=9TxαMnSLBZ.W/*ftwj~Э)�lΪܞX=%xr_^]Z蠹jGh�{7L k=:4wEj}Kfl2%O7?OC=viRܹ}6v�Х,9=db!=iJ,؍<Thra}מ::=5<t&@kwff*(=tθn�Т=]f?w/kJF>{SXyR<L%�];= f�(g/|{"�tt:#*dyϵG36Qk6%>Ghנokwl2&_8vWK=Bw^ɳќ�څuحP)� 5>Vlؾgǖ Z+moи`({+M@ 3T+в(# Cp 8M8N;UQܘsB/^ KG{t J@kW-EJƚHIW{qM95j,?f}n6TuD[6jP׸YcKV3&Lݑ?!j&W71aq_2Snml)" C'�I�|lgLgPK]Z"k8nfMkVi>i'�T.nlplLŐd<7RޣEo򁓦u)�]QA]_קL97G=s"ݨMW@e@,֠9\ eO V?<|t/ RҨHK(G |-d Ŭ?HA#mF{PV֗�� ﯉^+j򬟇62QmD0UtQѽV|„ ��`8GovD5ҹޜcΛk3h̨|f]l<w=>:Tm vOm<��e#>N7'Z'rw@ݢ's1WXˉK'iO'}98+'fi.ϾO~O<hfbO3]?xeZP=nuKk~9lY7g�`~=w+6ƚwlU14n367̉O<Mk7skl<6f}[6"��x~V%5waRذZ>|'|Gӿ7Jo;_z ;P2 5!Y7 Hl65qJ56lcz; zOd e]P rd?��9rpY WjkϞ<s3%[h2o'\zغMu DU 'ye@.,# $;?|U9ַ% :Ye n[ԠWOkk,g%fee^;瞟F0> `0d];pwr[uc+D]CC(~eܼW.>;&1`fM �d."`{xNv�t6o_4e;N&�}kVP^n>s" TJPo$�" :!"ǒOn׏n䙴|s`,:b.:G6^ݥ,VkAX1Gc=2n5m6ܝYEQ jݦj$4Umu|{7~4 q}=>m].Ű齇zyǢk>vƍ[n?s:n6F$7_&ڼm?Sjܲ۾IʲC:xo.j&qrs7[jغu['S+)8whXzAפ_5-QJTeXz;Jg]5E{1On[2y`g"X 狿w{/F=zڌz{=RQa)M*/vUz*p뿞�L]pITo#~fS/~p)al@gq H̅� yޙ! A| Ϗ]ѨZhӪk>;~w+U/;O�HI1kTm[~5)!5yG�cB@6/2_wf<PO�;Wd8}*rֿYCڔH~}(Nnck4qM_hR.^y\�?^  k}3S9^]ڍN@$�2%n`;O-L8d/7zOÒ->n8 )ԥ}Juִ*ҿ~hF/ܤi#fN ƢV@DOhʟbnjrAr9ogv[hJ͞0yll߰\а_Ɲ;wlZ>ǭl�H͚eͬջ?s%Vl> vM# nt풦 {7l<~U[xKrʦ$DǠNæMS/Xgw hݼI>NU!ͣ\9){UmԗڟW8mԗ.^ oi̵aQo3*Lf aJjL.n]-b kW}[DEvgW]pfJrHH3r o⣹uVx$*v}[[pf,/?&,P[vwF_ _<O_?ȋ؏رۚ&2wnލ#5=V P-z :6 @@Bl8\}ma|f}S/* @NΞuh\�� �IDAT,90�t_0ѯ1PQ %\Wc5.],+<>g�\\G�ND�^z7e.Ѡmv)oȏT5Q%�Vнۿ_s@Jh,Jma&7o@:N|3wl*+}d*qiQaÚ͙yDj.��聃̛��p)[0=u)y9%Wvz %{N,٫& "@Vb2n ;!0F�v +C | ~6kV~m]|76W�Xc 7Kӧ|+0?DG Mdo^_r33}*t2sWASų^D 5~jʸͯ~7>Rض}^4e`_>wѨ#ǬZ2[_x !=<Qn2{׳;ΎW4_Шi38v2;Dy8&+/+zӵs?Sٓe`~S'>~W G~ԷL?cMmS>*x}O\z<i)7~l>xzb-}gB~ (\k}RkQ,iH׭Y6]FI#]]j< c y'_?0ĊKO7g'<#ՔT0#at5Ꙫـ j4*>Y:;'`פugg'd*~j:.c.-_ al7edG=s+|#뢱$�挶&pmc_~a<e/,dԚ6RNIjG�/Lg�( '86{ݶ9w)=O]=7mٮFvׇ?ՎCe#ge#O q��M3>ߛG�|1f ʂ~{3ه;RBQ~a<k�)ѥ,p3�ٯcys+ ?%g۱SqIג3r wkؼC`[*R`Np5= �%o`x{.ՏG/ٰ;&!%;x5o豏v+/1vO-&t<֏vnR}u-gA^e3 Iy\whccZbZ[ω@pRʃd6Oc N���O3=J"xCV'p{lMcKH 'ɟ`S2./iS�1y6xV gЬ_:ˏ%5;i�.\ڏ)zrL ͙^K#o~aŌbt vEo f.1cO|bt m?哇0�q̙}>~a/O `̙?|bGpt\?Wg$$LSbl4\Z\;yCm\['e_YTvlkw�k_A#_Ҕ"t lֵW=�~1䣯5|nڱ;z[O\O|lwXgh㑐 vU.m-uMTl(OOYEw?kM)�O~yH ^'.Xb;mE�dtoP -|Lت}+"#B\s�D^['{7-M^20NRp;s�1ӪjCh;82Ls3}nuA]uLB`Cd qDD%CLFKbrNA3^^^^'IҋO=scoEP*opY7VjzVZO=ˑ$)&h˖'QG{NݦȊ a;$TNu*#:_ TM*!L镩 GrsIT iW,SnlN [ ND�H@Z82t$WJE#+ooOL@ -d�@DmYfTX%s _4Nn? YɅ/\8\pdMCw�tqh֯EDŽ@ fȢo��b%�0q$&1ƹb6sԜ>Y*F@ w#1(Lc�9+nfˣ5]يc%2IWA\PW=(5 @ - SMf(\Q"0jnDLU(zJ$MIQ(AMt1Z(ʺ)-BIeўb :OI(b@Gi rd!25PK萕Pz7v#?*mӕv(1c:GYpۊW(J䐕� q`@ 5h9ʦ:9$Iq49W9Ĺ¹jwE2] ]oO*L][ i %f(IĘη!lkoO;xϯF~Z4wqybzR9_9LB#7W gX~ ۔JD ]E9<THӡN10-s:18c!P2D[E1ujR:--BIeўb ZrEq$`M1Md9. ;]ړp53X~ԝppW E'@NĈ�QQ5#85ӡ,ɲN6MR@˒j5Y ޽{o7O!2hQT/-@ ñ[)ITwbfFTIEW{)qٻ9ߴĦ l^{@s;էMO )c/?]i\_s죚ە>fN-pigLE}^vj<4_x(6J!70k>D[3 ιDsι@MpH�(-ajeWG.]@P]VZ9gf 'ͺZbpR�dVVM )c'5k?o|%(X5ߝ~/M2_v~ꇏVx>ÿ9_L{t辍w|cfwSڎOvh؊7aSu1-{yP*n1w겟i.t[#D9)f( �Hw⹲aTKKK�MجĹL&nPM?lgq*=ιf6 'ygI:Zo&cOP-ՍF,>Aȁ#; <sI]^k&8P`op|lwKjj.?5|;kb096[!&|/{o(ٹ2Iլ%w̿6<Y@ �C5# \R! 1%Y;!$I59IbfeK@ w!dRDp9 90BDr'@PVWE Y>kլ27şC-Uz&{WtKo~߷A>|W�XřkצtyrLlq ��GѱbO%WM9.X-=>@3~jWcp]1C{nF @ 9;(.ݻō(!V)BZPX8!xĐ`AF|]$J .2; �#@#�P </"A AF"Qy! R0`0  PNI xJǢQ ԠҠ-ƶ\fO(LnOψ<֖nQ i>y'.6Q+:,ŢK ?~Խy;_ �BOZAɵ]f\U Imjgbt؊ KdNhW\./0L_j8jQԖw'7]\2CK.v?̣� ņ.RE-#0ǹwМsR@SONl9BBR.rA.L_j;#,ۮ}"�y}jv.v6RWܕI 4K{k{Vc~ Q,s`k}{r'[Ƽ^.�un~� sUK vB@R@a�QB�(U Y%  9qCSL8Ɣj `0c RJMc)R]XJqQ Lj8GP-;.(bY�glVǡXX+Ԓw2g:"]C5(U_Ǿ]wTO3,fWǤbc Q .u/-9>讦]"޲m[꪿>pڸ&:r�%wmck]�9�;o}&" #M=nph{�9ul7a^\rsѨi>tNyGL 8<;M{-&kZu!�^�dQ,1"Q\'?2D*ϣF\ܡɣwq >`t'/sY+@WløI,)SF0sƚ 9a7`å8P`@a�JA�PDB � ` `0>G0Bt!G�$P ]ҟPL] + H䄖�1Ғ`K5^Cv``RLҬc:=x @"--qVvT_ , E`�G'OG7Ÿ 6nݖ)Ɯhq3'[Z[I$� ˒z8k]L[𞫑 [ ĉ% �AHlɰ:�ӗ;_۷X!]Ũs G_9D"Pa [S# 0Hr`:}b.|��pD"�::#V̸X_xNecz/qYxa0>!J()�QeR eC!F`&&`0 Bbh Wa@pHiyN:<b H i \Ꞧ.RuGC53-kybv?Oƺ5q5)|,ߵh5x_䛲(]pVF� _wN䄤9 ZؘZ egdUay yFlQrB@:>% {_䒧YۘZؘ:N>Y愤duk[B luS�HpHͪۦ0 U(A(�%(8?S*!0`0  �$ Ξ-- u?p?r"O7lܢVYiN� M ̢J'aFc_S&?y*E_ںb" cq:K5ڮ{{oiJfZTl@\T 517�E7|D:+?J'\$$Ǧ$>U\$ܤ06*UQa4+&6[jf䑻l/6Ko6#`TF"L.T@ q? ewce0 D QRJ%JNbQ=clÒJX(15ÒJblu)>MxU^.�O<ONKU,oKbnH-:ܜ;'>�8<MV)׏th;.KN}MD}X;woЋԄgw&At@?*p]QM7إ՚ V/iN~ _윷9 ">5517,x~C]-4:)r2[$Ds?ZsKc ߂CB )%JD-DJt�qM)$W:%`0/K[,{W8Z{gXJXՁ4 Ջ;R5~A-w]NWH#NOkݰW Ξc ̧.R!TV~m{VEVf]FbAIy= {/3a6(ʉZ]{U:Zԫp߄>У7ORo2oFu[z~Rg_= q?uyߥ3l/^Y)3r~ŧkSJs^םkphT+&#vu4W>}Lk?Og o Jd�#1*@%@"hغ D`0 Sc1BH15lڐRbT|J()?r0yCbohe/~^p3 Y7gP##Z�֞G;^8?dn8MN_��hƱM9xuq`0^Mr99^!@@ib0b>D*JE"1!$;')%WcSi%r>R] `0 ?vlo9Sl+C e0<+LMM3iVAJml`˫BQK?g{� O\&־B��iO^vcF B sR*\9{)#��( RB�1PZ!D"`0 g RJ6Kx3) 5 k=L.ˈaa"%W"(BɿFυH:xO࿗`08FT0B!"z��t�!Jc(eft`0 3PB@AHQ�@{nŢX3 Q3c@R`'(50b8x9r c�!DAiMqK`0 1Q:r \W8R`0>PBR*˕T�U)D#(c^Js `0O`rT$TO&`0>:’@9Q ;*7V" !D J)ƈB`0 c寪Q|R'꽐`0FcV* !�}s91!*Q80y`0p\,UP&P 0 ( PySZW^cL@ � <`0  )P1OR0$�b� %�f0 ƧUh)B�D1B�c!a(NA0`dE/)x1EM`|L _^9rKht_&c0'o5Mo'.1e-JKF81 ?MaD1P� 御Tr\@)q yTpyyN 3=<} yI/E$ժBAjGqt>aި WLd_ٟF[d07z° ;, đ?>I<(#\S` oQ6Q'V!D�(/G DdEE%ZSGSKW\6bu=^Z;�8s>y?!%dԞ^uL55uu̜wryfR[]~k_Xs?iJ.O%v$+ M6:__q_Uu^ʐ?WBW[[gmA W)- ԴM=}.T�� �IDAT~#4wUl$9FbN-'{p33i4u rՉo>;�<PN̿`0J! (P (> '"_/q%₂|&]]HN<WOmک+?z. אA,=}*z*kt?'>]ڣ9Yp##�AjjT |3*?TAa|ģ %q</d={t݃7v3t׿1hR!Ǔf ٰ͈_6g|e=x#$=ujK4QaNPwpzX]Rt# `| @�TWI@0�L+85T"9%(hۜ|@$ͺ $t․nVWWjnϗ_�HJIXAHyHR]]-C 7]a{+6_",@֣W[of($>m=MմdzPE~ȣBʙ<7?(#?4( M4Tš+zx;ZjXm7vr$ ^Os6;T@7uАh\\�7ԦG!C-ݡWiDSSW̥ysO+VW׀f\_{GKMM-3~^MM5-{w<Du—'ԼT_Σ7{*&LxXgb{1JLT;S �ysnVj {}e6R]=]K)^*As.Οv6[͈nMwC4Yֱ6U׷v4m]E_Q|kp&u,tt\[^XUW$h΃}q44v(JuB&c?zBIy}H5,(jzĤ3☓3&m{&�Svnjnmb9@<qsWluu Z +pS|kж\- 5 wj e-p ,80>r~˄E`0>mV  T!PJ��QeD��J(!2Zl`]y%cOi\ΦH>֏_ 6c}AO./lȿ'h$kפ?yE*IK$H]Gόcڍ;)/P޿pAeddӓ_X7&Fd{c@:z:@~]O#&OZv:x�N[njkX*мE6TL]jc/ưFqM~7=QHWBs"]RCwԺ1n%]宜UJ^=HkEY2g'?v3 _;p$hqG@]2~!fYK�AxqqV~;_Ԍh{VW$NM[a0>hmG]޳wf}Ei`d�5 '~AevV󫻦,EG*{XMH77%:~9FW$>4DH4�rm˜UbzNB5wDɡǮS.>|ٗ^:r9r:Bv֒FQܝ? y;tOs,{t>0HG~}-(ե-k8T}S{9O@'/>Z\EJ0 /QMP>K \6!RJ "D\<#v $\ e )ǖmzϹ|GONtE~=Xu1I)I%GFQ=q%$^ccM+)d6-;HνO.ʿ~թRۭ:n<<s񿶯9]1�:a;Ϟ_rUyT ?~x{�C|ŷb޸|&/,9pt wDPÒfGEt fV|y""fxHhUkUjy[oC H~ӓħ;bZxrPM?O]6|eغZ4oѸ4#G{Z4 톌_koK5]ޫǻ?7@|g ۇ;˚!!R$l7DTt̳1?}5&IN,Yu#-KIHNx^lCb4Z}*((4"=}&-[QϣT&z0*1>9:ߜ?m|;ur'[A&}>޶DRám< ~&&b5*T;b`@!`0:(#PPġ2*PB}JUNCx/E<UmhΥCg1 *1]ѭ7kvR�U &:t@;?6sigxv8>[bP;bh3мM=57nρxQe;>}v玲]Il@=Sר}cvo/uq<mR2HݰVdǑJs 2h؁J232 I+�bk֝G*#RYfQ Y ޿.0t~MrBld`|&dUlz42@rS*YpaTAjh7r)Rk=qZc �-Z(_ylD+/ÿ?(i?Bo܁WSoSr0'5Ҷ_@yv%H{6޽]+P4<"fzZ�$%<R{PJ @)B@i*N:od`0{x�a@P+$*�8D D)-**"D./js;uv1FmPB�xSKT޶FUcW6ӷl}{jTqj*%bU~oW[֝SQpRF-;9/;$aѱ8Qe%U1FhQQIp]شv󮿂l襍'H&A�$3# �$+3�DM{$�B˪v6J/D�X,FQ%�`&_oSjfu[}vڅssj>`WT^xͤ�q� +*yqO S!3WM1$ ?gmo+B >)iYK$Pl M@��ab#mN9Dzk{__csƚ65'QBŋO�C=Ǫ]WR/H"pWá*P@I'0!!FoejՃC AK%F`0 YiIqU AkB(@,uڍtdӰSȑv#9�]${Sst:NXbK[mbX$ssv n<lSJ);4_8tv nTTG7ji+Zq�%|ETChFddޡi_ԃ~<��X[_CP("52xs)!/[Of'Xt쥧'4'.䵌 @4�U Y{޶'V�iyJ$ĩ2@u]8]=.`s/.uv*q>{4u(Dz+g$;ܘyZU89� ѱE̟zPZ`x] $i8ݙr2ط 9~5`圾 Hnӛw iUU$dvwC Y=7Tcq>Ec5vA <Mw3ّjqH!EzmF E>5v;$܀c7BTw,"ͩfACm]E_ߘw(Ղ1 TnT?ʛ{Mf˴˹yY9�;خ50dfy.zcMyMocg]qE'jw=L<tkgC|~F ݧi;O}B#A.'fu6N"t?)Ǟ eF_չI@=cYKA5nھv>Gz|k}zl5▖ E2$fzX[2_vu?��qoɫ#/VsҬLK[4"|1ºoT׭Eq1ҫj 9ꃎE^; Ƞw:5/ԭۊ^nT0Xb]G}ĝrTo>1B <,?ՙgg:<K={#&\kF)h ̖ن-~gW5 p8SKkSg%ZJbt3E e�He-#mRBMᅯ!)$شIKB̯=, 69e jqPm ;vsধ?`G2*hmT2g`0a[ސJ@.P&&�l9pPk�5eKMU2mWT] 8Tm.t]IʫǏb ݻNvJl5l0 F>sfq*o/C{ 5 l:O\sq{<V*8vND𕀻IbKf|,1�}]Mzlb$g#=k&ںaZamgDG<|]u ^n`mʆ~?\XN(#9N}|u{UTywn?:8/=D½ZǞaa t#||Χrm=P+NMʔk[mԮS}c i5ϏXj#CSpl-Bq&vڅV L;#oԞhywK|& Hw+F+ffkژiʄlCR7'g"yþslcv*DkZ3m;NƠdљsFב<z{匸<+ڵԖAC5TNϝ[5]]#ڸ#Hrv"�iDnRJ �Jx,`0oݼ)Fhc1��!0 @A" BX�fLޣgO#KuʔIjPJuwI0 ћ:&,}puf0*yX?%sqƢAc0"2' ,/HiyB`0>HB]eAPPwP(`0 g Uy"(.J=.D�c�0 C1Q"Jh> `0>#U2 �ұaq`0>*Lr (<PTip@�+LLS/Lع`0 EkȟCB@gJ)O)PJP\* C`|R fHe}@K%*%J !@@�0�Ua"%Ǭ `0> ("|J�(=*PJK?1M`0QPB_i`0ŁR�PG/Rnd0 R �z2a9F@K`RBq`0 -?*_zL�2  U T Tc^aO9Q9 `0h`�St Q'>1 BG1)<k(zQ)>*aZs `0>gA˅FH!%�^e,$t_2g"%ɏn=|U4_}K�{ |:v[Og0>.TJ3 @C"0 |APZ!#PNB4$gʱw Ɂ&vrclT)]Q/_ |3S_;XXsϯ]Xw<߇3ďogժ<a{iG*f,hhΧq?R8:xz܋F+&(`"�")�9Fr!TD `0>c*M1<{ݬf~-\EVˋu*6nKL4eZl}VZK-F˩孬sC8;YZkf7+HLsv&4A-)ꃕEd Nȓs;ۚXz.UԪWiCT-$š1ZZ:50nXEQi#sq6q=sˍ}㴜ke[N<ێX:C/SE+@c hjobaªJ)~urֶV^]'ndF0aJ)4*8,PMd0 `C['pUaDMb,n͆w|n>y̻|fߎh5G<~.PB,k 1 F71yNXŅ G/tZ9c E˷{O-;dȰ-JDCkhܫ %K _9fS?4J=< Tkbg 94PD߱}:~z}"˒yAخ掚~TC ~X❾O~_dd틇Y~vU�)@y?z� �E)M �B/x"2`0_*)HE5fc1Gc^+~^%ӯ[=+�I \Ml,m\ T� v̢]]K�l _Z_jf�@2vֶ~1?* 4Z=G�Jlg`ar"F_t 8ŧ�E-֫疫|jVzV.{}mf$F{/5Uy™SwdP]%TU.(GôE�wjr�Z_k}uu/sFWzf6 妢UlPQ?זX@sҵq kS{念<c]sa٘n6n zr�6ckice̪+q=Kkmkm-ppn5|gѭ_uobaaelڨCdVֶ~onW4@X3}%'{hYԱqiJuis4w776^~Iol, OvJ־^ �@3vѸ}#xYXunױZ8:78񢐟O\)Uh$ϋY7c^\]Jnt%GODːCî/k%QW_GAO߿ں |2S{~_[kCwjc/[ԯganel֠ oj%ȣq157h9r?5Z%a['l߼}c3cWl[9w'[k׆}; o]nڟGT) :gn.VNM:M^GYՍ6cimҼ׻n77w֗y/,Ts፭/F?ɹO(nxwfVvvڨ�� �IDAT--<$rA/-ڮԲg?*h~đÚֳ3v?wUS`~ͽ\̭͜[νZP!{r?aizBx|�zz8ئV]u'o@(T)&BI9(R !1ay�1Njbɿ]�`0Oʀ"C\~&`Oƺ&0}>,[9lzs4@ VaՋ_3ƫ[{y;^#MYsH٢_!=&0{KFK3<�-3GGqp>M,1I86ɠabc/vM{$TY%# IZtYC0frS ֜,WϖHe?\*D K%j^ȁwh׶Nչ#N-znBRt.0le5[EʹޚI :rJ34xMMg ~nVAb-7nwSmhދ!/ͧnYL[HwӌOu$pv6?W-j 4~c&-A�ĭYA gYQ'$=zmmWYhg{k]{-E:ײ_~{++qN낞Юۍܲg#HĚٓM#7Њ)c:ʎLkY&Ǽ}XTjMG|7ňԗnҙXᝠEKd‰i> dy$ko!;Y]uֳy9y�[k�`N=De=|-X<x–2kiDV]|5kG)ϰg-dvZg"E1"k[Ιuc2 ŲЋ%XcO[sVW2^z^ޢX Yt,62K/r C 44I6ZM|"} k#5@R c ol /y}`iiIKJbokXd>=uݴk_V4yhK)h%O|q}# wmBveCx(~ym~/{Ys4]ŐT}+l=.=A=\{ho+3"?:Z{o'/OZ�)@OTJr#XT!3:`0 9 *6r6E)Uޘa!v[/m_$=EֽDm7TqڱpNS]⣕8̀ߞ?w+|�Qḱurv;v[}+FqqA;%iEzmVTqY5njɅEe {%sp>DͧmWC.uRDFAn5^U ׻}oN0Pz[˛q 'vz,q&TP]}w]uʊ.J\Z]t(�%Bӹ}rm:p9b3pv7\J r9M=-I%$-9 {jMgƿ۬9jIiD\?ޑh,ǴV,��$6:i.hU|S8(̻}Z�-J]w t8ڋs{#7|u< c7�Hҹ{еUVZ4F]vݒ6mg4y4�@4֧WQHlh[ץ_ܴc;g4�@Z-'׷|56+%iiyRrp ykO^To�o+IOH. �ԤdA%c&[ץACFn\C_TҮ7~la �'�Eh]_@OMvҽo{%z<;--'A'^k<\dCg6gp;1�`I'"& iYv|1ldz;A(X4Ƿ[i1dold൘oޒb5`qn,ydl糮 8@3jߪ2ς;Ok9^!'C,m9Q~@Vbv}Ҏ:e*�y[ilڦոu;o~Ykmm[ڴo&j=c=f:�р褃�Z76No9lQZI��eNi@\Y_SF V.4/%%IMUSJuhdb*O^L1Vq9@@A鋀"`0ƔBLɫO0 `cm( T59iTkHDwP?Q_jfHHV\LUNeP$i:dQˁ]9G߹NuZf%IM-]r-_zkfaw^\pHt#[iӬk~"HݧeC I{7N�yw33}FSJ1;l*&Hlڤy YhsPdG_WޯRTBB;T~#,:89EB=F8SߎbdԲS],ݯ~˾_K�I$ոb?'esߦ}g= _;OKrY-(آEK'Ճt쥐 2R3(Ȟ?)bicjacj9bBJ8X̴t{s6|sUUruT.~Ճ (Ž;Yh;R6_ ]IfbBN H64JeQg{XjMew/S!~HXTJa �`}ݲ< ".%�X,UQq$Qzr8^�ZP.BB v^%im}m|TV*vuy�GwdedX3ذA{I(<쑜Ң[KژJ N<J@x*{�5*8պN &kѡ(AxB mۻHʃثM3=5iΦ4/12Ъ..!>i!^ PRJ!�UBNR"6?sa SO `0cUڗeJޤ'l9HRrηq*u]=\m=5jdc�hC-ӮS9wO/)/nױF3ͳ/^g\MڷA#�  ٢T4|sgM.�@[ 2-D�$Ħ$\s2w*gӥOwݖ߭ݣRJ�ns7s^;ab�@?nwﻶ/ӼlC)PZT�FW$Ag1p㑩e G#Hݦ</!\q5^f M;1w|&c}~p/Wkf)㔔< Xjj/X|6,8xf}dBoN^yz5� ?dNA28u9}veKJ�+&6tv)'8|ږ|묫+ghow7|d$gmW?xb�@�$�PBߚ[@ i%P D�HݭNzX(ͩ7^ JIU_W;Bm._0 ;JM -}JFNshY;r06֦wR( RrV%�Rs(5#@J""A9<!$FĚ/掟hӱMGMz;j'' `0>!JvP@_@BI]HZvIY0oiFP_E -=tU1? [{y"ȿUw hkL͞}M/tլmcuDχvdJ]VBy61`3wwchNqys̛9c:XW2JgOPE.5bb �_Ļrq]Ǐ\+kaHvV_ȐIM�w蝰]C,9r68{vǧZ=Esr/U$/WN.\ңHuptTX鋫L@ܬjJVg(μ}YQ0 7;xkezt2GB:nvĨB c)FňNd1'.XqS7.)*!A�3G$'HUZYfĹe%GjncF6@Ã??wם;O7m  ͬ(hƭ3MYGDbttޮԉiPMEҞ )MXv+@Z�lna}BV#$-)V7<D8OGhIjY.7gÇ=H#�T.+RX#^ l\1-꠫JxPRSќ7[Z9FPaE#�IO9+XӸx9Mőו FY) oPRUJ YY0DBBpPsbrsy:hz1ML l{;xmPGWWjpYHm Y MY7[U?ϡYV0볧4y|M=LY2$^fzZ&>}4'zםs<dD;'3C }+6cV_O8$ IW45WZh];Yik1_%e1>/hvĩkZrbϔ7;%cgkrW>BaNP1R,ʗЅ`>3KhN^\9SCBҞSYWupvqwj'_pkdznuO=L %VnȰMj�I.o5ȵs[�DMOkmQoR}]Ae[$nN˂r@<rfCxvwmcppƾCU2\�w.Ldzؼkfj�@Mt֤]>sO&lwت^qn_ �}y] ՠkg\lw6ῌ۹ {Z~xഁV} |q䄵/]9Iꤋ@^nX~J3˫U"H<y[A'w[{i'ڸt`b&{$l6iÙk!L,i xw= 8|ٔآWClD$ШFvu̾^lͻtf/[wlڨ/c�ߛ];DDSNErcf1TVx9nw};;}}YiT;P?_γNEUsͯ�y#W޻nCb}Y& 5H19dߞ%#WЍ՛4i=hӳT[ 2/1hɄ*Hsoq)ΧDu]+oռV/)JL?ݑQPz@QJykLOPo^͘1#6 t>e8rSo;\ 3>vĞ9=yХӗ �hUC?}lC 廴*ăDtt}{ \xgΣ\%߄G;}_uתrJ@q9-'hڊ=6;3e_9}Gc}AR�R٣#}thnJlxIo;7w٣ǒ]Wc gHwsvV a皡nfΝ +sd5H`Ìyoc/y\{ӄjvh2>՗4 _ƸTɫc7?zŎm zi5�@W :o ٦YzM@b5kr=JЏO=śwR>wN'UɟSr҈<;T٬F}.Y؞(: sVYuʾ3}PVMf=)9~S[FޝڴTs4C_e 0A4ZN%DJjVQAV'=]�g!c<]$]z5÷ԺQo_KF~((>'`Њ4S~"j-BL4�Y5ny-G6X6aq/1FgZJMޥ"bUOܳ{ty˔y77aQq.wNcy:9f-˘ dYV% PWǝ>wT��Tm^ȝ~x;M_=Wlh| z7_V?VdxAص=ڻX4C݆.;0X{}<hѲ뜫W^lMٻO:nq}C{k.OY(mݼ㠥݉䇵4~NޏWlޤa,jm.]2bͻ?h-RfUsJȘ%s=unW`3FVէl^ݷf~֨܋59@%I[nZʓ{޳xKgo[2!qt75iȭ�Uڵs[[^*-;u^E>2KToK׮aanPѲ g37)JU-}:^ O OKz=|ůD>[�RxG]bmֲB1Zp[BC]iK+l.NlNb�3=f$Նv1䬾-:~r2'O2xXHX *L8T}cͨ]FTyM'�Wԑظ݈qX?eW5sJ5Z ޥ ~^cG�'m^ب=Wnyp.%�@��a}Bc!�Q J#(1y}A΀ys]pugLZZ=IͺTyȂ_-^vӽŽho➮-9 Vzj~ζf6yY.dhJ+|gY.6z~>7g)gwX>t-COFWGԡ K/umm2cUrpv�tFZ+fXi8Q[?puł&fҤfNV.<donPd�ͥ}f_3x:[�i>\tIW΅԰N#|{ �*Lp>js.y4Codu1 >bM!%fxk;'lRn~K5ut)TӘ#-{]zQeSt)kZϠ;Wgo͒0ً6u:z`vu+*e3rtJkoص6}"Et7!sr"!"cK 5+ZRI) I!D33̏W}&] ue[6w65KyAyݝW%&-E+G+@a @~E B)@EH1! ~^ѐ'y PBҝemqEJRO=bQV>�@:xw?2iJbUC*~�@wOƗ4&D\yӠƩSwjSꧯ6Pbk*\På>J� |Mʩn\G1^ŢQ{mWADXl7(lE+񓟻L@{NWGt?߆;;^[rRi}hfT baՂW= �@:X<]oG6~nftYXM fwq^bw;�4hr^z~uc�� �IDATd$t~"3W6*Pͮt 4HF5T5ܞ^:U&ܿ_xa�"țe5O̵yH]D5\nWImo]„+;fI2)64fWpPb%yk=rF) r4Jc=}z89>sڳ{q\$xJ1&E t: :HD5[ IO|*կΆL?+ήl kѧu}R&ä|ß�Q|0:!"e4wԦ9vtvBo#�ۀȷ0#|@��یhQUY?2 'UW]8{' -g*2u|*+j.,n]81eǢUd�(fE5^(ٗ"Z͏�p7i�lVȺ梻_e/oũ;2jNkrkg|~p݂Ps/r 7)KO{dP˴gIbFvls} <opc s*~+CyPRPtݶE;ZnLɫXJFk] a�L)P  QEHF8@sJ@VEۏSnm2u0L/B0N |1>/8qrO+$\Y'i BymM#<~ȣf\ZU]׸и[4n~RoQ;M5Г1<С/;W*&} ?p?=a~yhԮy k5yf7۶OJMZNZ/Xd"n[pET::S;g\BTNjR%JBD"(Frr7 &{34|G.Y5W^|O)N/eu_{`LE@E!DSsvqD޾���HTd$"stu�. 9:PB)E_=k qv@W2|xd_�DKgfKӚ4qRt_]٢e~?ުG=_Eo.uAгOיu+ fF0:^4&:�`�<Ч�k9^w?EE$*_ N,[e[ȐY!JH$,g"SJ%B0BxWˀ 0 Py 4>IS@n&RB_`er OGՐ:´d)Σ|rm>{ܥO{ĹOM+~{c56uv>n&_^Vv/]6pDh/\/=3dѻͬ{h#�-zoqO'q?hL^oI\<9 gЎ=a~y|~ҕKSVZM<}܄f2׻wnTP1 0B Z "� (f)aaG᚟ʑrR�2@R"ȣ(r'.N$|Kj5~fzbTX3lH|Bj?=߂9x|dQ= ߣ_)=^n,ޕ�t�@n8qOɋ^RKUy }F 1Ӿ}qiȤsսԱjB7w'I\'+w[q] =O}ӫonggTHgJc-H%^Y4~n7�� '?Sn RL}-��^m+=^3i͇;xGݹ�E@n56,?:vskj2jYIwb h׭&<U#6|*>C1Hu_˩Hۏ^?d}7kW9�`yojj@Ht:JBR`a = A�$߀D)!Hn`Ϲ (M|v ٕ0x® >*PI1&QUtBQ[mN[my )� *uĨVcoӏL^$w5..mlzstc-g&DžXV{=?l^P7*|ݓ0hП]Tz^fb% �rHs%WнyԽ&|C]Pܽx->1bjS 1i֖i-- i_\y˥AbYsƞU#g o0Jr+z3^gY `.!{Æ-\yhPs莹}_ˡO`6tkk !D" !�1;W(:A tZm 0 WrE1< "r�`C#$Eԧ%�J ( 3S"d IzM??]bNfI׸2~ 0 c\ڳ{IcD$%1y߹cVE$"H DQd 0ސq@崽~J9!=^N%8umTe0 0ƎWjR1<1%D oڴ$qna;�rzD0"I6$OB@�#B)aoy˄wu3'aa~D4NHx^P*x Gi:0 c0(2zOfi #L?wg_EJnVR0 0̯OE38bFx O "X0 cl!{!�$1 Xl%ega1.DZZpT(D jԼ ~HjB䑩 0 0r�BY@"Nip$0 0L&hu:Nj5ZQ<)ML\\yBaH0F^¬0 ;gI >q/Jw53 0 EGGAR 3SS"Z(N9x^\}Qʲ1 00}o `E0 0 I@ ILH XZZ(U*s㔦�6(P}a1.ibaH$yB"Q�acPJD(ŀ��r:�!1 0  !V&!�XqDI-IY30 c(PB%�@ÐR y}wJMMM$(0 0 3J-8%!$ s1`(%�PJ %@0 0F!9D1"xJ#x &azaLLLx`D)"D /$<a 0QAv?as)2 B?�B0c0 0 J)R I pQQ�IA* &`a X@n% K# (HEB DAaa&% 0 ɕ* W�.ea1V1 � 1܈�= 7(�+1(1 0 dN`<_(10 B 0rK`Q@ϩD)7 BcJ;1 0 a%B(NXQ<�7aƨQJAR+x @(8�(PJ+A�cC/$'A!r0 0 si4N�  9n0”H6"0 REAR(8PIyԃ3˜�7|SJ(`gc2 0 aD8#+Pސ!)0m2 0@8RLLTǁ>j=-�rvC1%N 0 pZ#R8DQ$$ H<B�Ava1*jZPJ s0h�H?@Oh@B�50 0 a( *J(Qr*Ǒ}�bwWa< }0qg-ùWsO�#,jaaNT"B>}$:<!qazIY40 cT!(bJ1D$Rʍ�!ӘBROB(haar2LA$J($HT!D$N) 50 0s!D(!<oba_3PR�Bɲ =0 0LhJ( `Z# "ˆ7Ԯ9g0  "IPɽ 0TA1P'@F@7RRI'IKN֋! l aaJPPP# @$D@"FDt@1DD�TV1k&`ar]J)R�o#@)MqY%: E@(d3�T^ jH}^s�k& .GJY*-ɖ~ *ל>yZڄ@RBRJ ,)JҳgϦT($iZaNK)(x@ %F: 0 cD�D% 8D"q ^D̀҄Dʁibb"@(ȏ1F@%>ko氎xY7R1Əm9LkmؿVi3ʷP7R(Td@)ި$Q #sSSL"$c,g~*}[aa~<B  $ysx̌y^Pk:C{!�A�#1VX,"Gq1~la_kJsQt:Qb!�Bɛ O@abmm0y^DF� EtO0ƔPB$ Q 0ځbE!)ΉY7R1Əm9LkmؿVi8 `ci-I!EuqI9 ,P 0 cTZ-+�j5B$IBIb!J4WsbVvq1~la%cްgߨ tW^1f J(Pr�!ca<W;sKz5(3SEkutmB}G ;;x:tK>:_ޒuZ>7 urXPCc[T,Rk1�hZWr/pǺL- ��ȇ&tkT*0_ W��Ց-+ei}#J^^BcoZKB ߽rg`間R/a;JHHHHHSՄBZj!yNR!8A|(!D2_Q Ŋy euA}Z#קW:եbr"|We48?af#εG$vՕ^{jVD":N T@"@�Q@PL)!B)r'9UWzc[/F+]Mzz=Xf$]G-8T!>߫F5jK"]ٿA&$}ғertkk.6X bXC7?}t~m!C6. �@zO*CQL@^<:IǦX9=|0LVɇ+4aOʐfhԞAWރgI+09D $! IJ$IZVQ�J{ t&ODj\?[[17_ɾxⲼ?ᇄifQdmFӬlD ,׺K28 o?#[[ZX:zkwH)K~׏twvu kSKBs]v}*M-t?$l]\f֦VN%uaBfg?FyΟNo 0xoxS ! $NտP(xy Zn]OE=cd�@n٨lWw_J�6GSНT֣DKz5P4[. Ywr6$@lٴR\~w{&kWWoݶY,As??�ż^u`t %s;N;6kcX}zȞP5(_үvq_=ە Ϟ(� ;vIm+3Wb�=xdR~NśʗEt^cS/>bk^/7Y i@zu`Fm7kPz !P�s+xJ8S*<#{JPn>8.cfՍtY4\~ޓǴw<RYTwEY]@wcPu?DtZ1ٽ#}lҶ 󇏮^?O *LRQS:ӯ}"ǮMtZ&;z珥V|7ژpؑL79{I^Q0qblL86&v򔩙`@NZA+P �ǜJ BRʫPVBUHSN>x�7oM L_Xԣȓ9GѺH}=4ZΫB\P!=FRM<`]QEUƏm]!8w8\ZGƉW'UmQ�'5ﷷ/Xg@x (R*wpƼ;dx0)ml GZ$K�|jU]-~kW_+ЦIaJˆx1T%BB�-�+x��+׃2ssswwswP 7ww77gk%$lk^|Mh_ë putt-ju]gW+Uݬvw_)֔M^EҙT|V%r8zo�¹-lL:ђK8Z��p~Q*]܋4�7{Z=N3.C6.˴GMt=F`u}ܭm]<,{,|sk>+}C}qY\}+ ;f&=AgK{/&H@ޝشkS/N99,kq9C?�� iRέPЀ�@c..j[.OAG� ][z[n.6.E>.�Ш} xںm:1Cz0uU$ @oP}|5._hڠDv.+t[}#! /Bii䚻PPQG] }"DԠlKh9Co@d׈E\lwԝZz{jzpe_gg[g^]ZjW/_%׫V̼ZF0!DA$I5$B$AGCe+ ^;tB? ؿpo .{Hn?,"w.m!.+Ey42('YG6u��H؊5"G٫ƕT<ݪs9kܜMV}z~x0-Q)K ^2,b2y>P/ \y Db,h`{vz]gw|iZ#n1nx͟}mêfesǸGG|mEc?gb"b XE0`ܙ"Q,'8L |O Pè$[ov{aZ.cm7;/6] k^_Zߥ2n؆۝/ޮ˄BW4ʏBֵ}Qk݆[]W$bL0N9w'FX +Lz[=ޘW}vn~mƸU$[m_{kD )4Re rS%]Z_w<&bǟUB"I/iІn�� �IDATGuZu@.|ksy,eBTzfVܬm&]VՅ}~&ގ˞7{o9BÛXm]V1tB~g<m赼߿㐑SVq_ER1]D?zs,}1oldlxjE9Y未�:~bT!q im6o*\ѿ{Af/ݺʰ"4Nz.,svX~B_n"Կ' ;� 6ʴhoV:MϿuC{funW'oxak솮<8*: QڇtU9Sk.]:w&� �#, '~�<lǏ Ic>+PYkîٸhÇ]d7r>]ܑQ$<BTK_P.ON .QuHB!9[T{έ`- g,wMu�N^w_k�@?]֦nTs\NCN.*ڭE;=egh{+!:t{|kWT@l4Aӽ#/xAXS0?Lar$EI6-e(%2*aGD A/IADBAehokX#Ϧ;|mP6=7ݾQgYǻubՖ=rUS1$+?%rVvU!q#%ם0Lƈڕ�g�D@%PJ( Owm<U|yF+gݩ1ej#vܭּ;2SA)}?tZԳLYIByW; ]h.1j?}"\;u׺.�G�46  o݋h@fBt1;| HDQ2\m YiKsmf7D+kDtvmI(EnNfN{whj]mR �`[)Q" ք x�p>\6[?~ ˈ 79N;"#дg/\y/gA̲IaP SAj_=5 #9y:`;�1p)لDMܺ5Gߩ=W޸Q>[(<J nȳi҃䳤 JJ 4MK!)I" bhld{Ct.�)+Fw)Џ1́30C@ޅEd`;gGIonN ~]1`6ػ]X' dx͋'.<C){# +G" �c6l ;k^"��TсJeBǿA*Pؗԧ3~L p|9:Z lb`|Ξǒ0 tՏ�$k1H.ЌZ �,-@рNwnUw7'7=$|EnHmTR*�>_r"h\xXW.,mxxz{+*/og۷R�lko-$&$29ٳ>q6RQ)",Q_!\֑-xx{&Lk3Sefg/d薻 }FK/X ~``"YeC ncɂ:;6L@ oTwz JdPc%Qao%\J)A߆QŶ6Hfk|=7<W/Y;sLi8o/};zAo<7-IL~<Eʯ.FM^v\[wuHXc:s/ڳbvaaA6 e2N} H{C5pgAAvqUMUwؑ"JwwY/?%D{zi򉳏:t[3 ~:2k ,i�J>~KR./BQA}kfUeR8Dž:/;!zbaG{OENLӶ>Ӭ< 0hCY9>?Xm>.00 T1! 4yKrS5=obu Ji?wG PE(ANʢ}@cf&�o~O,+�],ūxZڰTݒՂ]ܜgw.@' Q|QJ]PJ[8y{R7�vqsN,RFx !{'za@:( ŐF"1ErLڵ<VSA6ݷorsn8SgU7l`abѵ%L�U,;; R>][L@-C\oco+C)s /VNRd½yRKL�@x5rqsD>MJSoؔ'"):�wNyжpqz2F�ҫg/sagN|!=my7Dɒ1I0 ( EQ4E<&biyO^{[x>xRێ\ޕ{8&Tl1K?>wE<R z.4oυ.<Z/%Ms'( 4]!Ƭy*σ@� 7q0͝ c*QH�0p|wVҥpNy6fUgE ^%\2 '_^pѱ7KkYBă|$- e8f̰M jD%o^# 0J(P$wu}OJ0_$-JA h4=�ا~\0YA*0C"§}k>6u_y~&Z5i5uxڹU1Z^JV8�y(W6M9v~Z5\4zT3(Ul u>Yk [UpbO{ѯb<7/=oְEKk3omUq:6Ns%*o~M: Ƶ-~|6GCf;~\FST]\q)6 _\mtOc`ήrNn !"*hL-yi#N$XjWobI*Sz&u5MLvM}ȩRo؆[k,0S~e۵pl:}!U^ly}³I /(Ebxu`ff*s󽶆2ju迧]P+0/c,%M>BBHJHE[l3}_l[ϊ#}xեް)^1o>icI_R +xZ:1oE3ce�9mSبF}1Qhn �fn5;\ء|\O;%{E_3BAYgɃ:_, uZӂT%gQL/qDP򩜤h+4O!_x}:Z0b3"y< '՘bhߟmSy ھ{E=w{ݦ 'ѩX._GFU7^׉JVxԜsy2GcGڼ -lWޜb].CڥF |CVMPqBymW�+7$7w:3:wnz3s7}xMO}ʠs'v^r3",=6:E6\~Vqalqs•/=ң�6Ξ8fN*}?s5h؉=yK\ ;3V٧k"F_<W-6vB/ulaK�R6W) ZӶN?|oR" n%-0q5'K k,0<hl?jB:rZrk0=8,4p q^5~$V>-\tnyDQoovC[;r*J"!Yٳ6)!>0 '%W2CF% !6  SJhv4lhf`h�8qdIO|'00>|I8K?0үaZ{w 4F:bΞ=ٽHRM}^';PR$>"0 >_ |Ϸ.F8K?0үaZ)$J! }!=�F�0`H)a0t!�QIa8�Q !F@3G;F%q1~la_kJs` X}B`1/w:�C%Tk) a&.1!@{%r;>CA'-u:98AeuAR0R1Əm9LkmؿVi991'Bc1B9Cdcz 6a$c$C)9^d Q%?5\'P�;pz.E Ú oY]4gǶ&[6_puu 1B$J"� B"J%WA%QvRaQJ9^L@~ ':"r@?7KR|Ν)SRVKY*-ɖ~ *mΝ9]�cNM�!D)B<D ax$ C`x BabJGc(voaa(NanC'uda̡$zH;Ha9B,0 0 �Aa„HTJ�8.!8%X0 a<`�`$*yBlF) S`1 0 dJ@@`�8>'Fs(LD(?1 0 D)a0)p y�aa(%q1|υpc0~4)*I(Bha~&[I|㒴WfS#X0Lv@r3&Kn:*!D$Rʚa5Z ;(4 ڵoM*IT@WD%��qX+K.OV+ RnSvDhң-Z_#OŽmK;9^Rզ\7/PͫO>ľ,xszWWsI�)A]<ڪSw;=/r!7w_ʝgԗ!~w[W[Gǻ^u9i̵M })wFJy<}rm;DD`cJ䮱UA}[-#н)ZhkK&ѳ"Џ-3zK0 �~!eaa&gǏ:uБ̔lm-EQ0W h⇈6G7/kͬyrγHX>pU, ~g{WW7\D#SS,7MKJ߇H$pՅ߽vfg&N]Rc@ܰZ:4mn'׶u4>.^Ug� e>\47?X|^A=׈}6[?6c?<Nli gx12HBٵ3.98!GLm/fP_i%W|:_xɇ$ �Ys󀬽J4>1F-RmGw3 sd"@> `aan'=}xmO<HR0q<�P|݃ ts]]?x5�@lٴR\~w+NR׷h!G>iuuj5DlZAPyE-iUb�Ww\ku*ֆΨY2ٻH6ӎ%52H7prtu(= ܨfݠ>{�tLj_gMoOh)-j(T#_Z}^Ző/ϵ;+9|;ͳJvm5-t|3s0WT*J)FW5͒ÛO;V۫da=/nR�)EBI{qNż^u`t({~;6SNpiejI"j ELs/Y%w.sF0O~>(E� %Ngo>P� qhG|{?w/+PbwL1ɣm!YI !/]{ނj?bq #;>X笱p;7iSQ*y;{+3^!9�L)$B7hAEKaNi!=FRM<`k"X;ؼCz],55έ\6m[UU&𛧯?}o_F);{p<�8s ~kXKآ5۶mӮaQWqǵ Uέ//,֦hE.ocLD D�@;'on} BbT߼�Ǩڻf_T75kd {~~<��`G"�:hjZӻ8ڍۺC�ݦnX 9#.3yB}1$gW,}\y`*+UM{Ug0 }d!zڤWXKA FgM�4ae#Ҟ]<xuY kM �@>Kw$/Čfl™QN_!ݙVѯd1o3Tho#pj;(7?v "`"*&&bgݭba`"&Rҽ,cABc;ﳳ>aA`~=7SI,w(_o-C~!YWލw?pι L\]dĉSaoUUYX,L^/).O}5$Iȯ6g@).m<ڝ{4f>t7HaS kX%mVoܶe݄#qهфM<}�`9L=w-GOoǦ#H:}M[|zV>蛨wtҹ GY7\JQaaB(?}gAoPw720ۼ[F��d#;.w4r3T4q�i緥Cp2nnqƅz*�I O$,:H ˊ%?e)g^;ϜD}Q5f<\SF퉡>Sm� :/RM".EB T/HS(s(ʰ/7TbYyEt\AӁ~ @g]V[ дFZ'=3|:H#Nhk9Z]-5<U#w<+eJ"K8m@c}*|? >)DeܞJ嚨]$|EG#jw֍hk5{`ŭ+:ѯ6u,vO_T9!? ]ETm:WzzmS/tʞ:%쟛itƽ D<ۂ5w!+Zӎd@wS7u8gEexmb <.# *Ze2Zbc�W)H?~am&%~iIimbЦӺrLgef'n7w`[ӎ~E;R@�\")H|~Ar�IgaNmILHɨ%u uI%\:mB#ӒWy|aoMO{!XX�AAFNĝs3UzS؎BADR>b)G(p޵G_zj|AS~b1ؾF$OݢU _]e \�� �IDAT/|+OP^M y9fס,BZX�#\^x3<g^� O Ȫ]G^2\(-'Тm^s/XiNG1/,Tac\a<[ HήE'+<?vt%�Y9Yݯ2vz3lj[%99OK�yJr 0hok54yʽ-]{x[SdϚoM ;9wвeU׷_k UKG._� HRTj }_١HS~vh׀2_<W} vdnmв2$z&x|gGm-}gq�HT*J oMn@�[ }qcIL8֞Kh#t ͓(gs8|y6G/N�(ZA}]נ|+9eҥ-W[O�B_<쎡f,Rm7w~V]6� skJ嶏[u,ۋ8 \WܤQ)or0qz:%+olz:i qqaCOUoWPcao̮.,m];��%V!7>]NK[_pь'ߪaʇymڵ[ ?>A 9{ ;*TUO5t44UqdcP8sf,ƾ|J6R%m_yWĄEm&snWSmN^8rGwܪY.SVM0}xl|HQlYzb% >gjEڛغg#�΃:Ɲt7ɌK]22]2Ǧʸ7 rZÀ�*vg7=1dmSJ d7lبܙ]2Νy.Ǚ57F=ם<ªt[@tD<q? SӇ ;bwsofiWfǨ֬{V8?jcCG_��C]T��(w_/ YmwV.:jfЏgg6y=G>g)[YGz,z( ;8XjY?44QDiFq;JQ'gvol"P3k?2�Yf1p|m~WvofhY\v7K)"~M\{3}M n‚<[��T֢̆vS7y45q8"]׋�@p{+/xw z,B�wtS#}5>#ox |-ʣFŜJUizOqM&|W| i鷖p˟�<0f'|T<4Q:FoO&QC"Wƻx<𳞶I%EHn)l2t`+:!eѷl�0/k [ �`.䒀4-,4?w'D�@'m n5u(^o\GeLcM\un~ogZ-dkYuy2Z 8X_r�tFK�A-4+Z[vk2eMD\6Wa+y:kjfg]H��gv1Ts8B]3p %ejjj _D¢B��4EiU'EҤН<1fwԀ,ݺw׎W)ȂW9rړ H6 <tQcnj}6 H P!I>ί6v,1Fm}RV2Ä,3G}ƟNǚGmWv~X!OpސQǨA~�J]!-Ņ[m\p@ut_S愾۰Y~?}ҋ#'; s7k.ysܘrt!/C^(/+LuBhX9^Oyo҃o-ݺX tCAIuX@` cLi]v'0ivZavJe� {�g˼ޖұ[FXr9&M\ 06)D2=7M�$x崫wӪes۩ ��c∰$ zdY! _ɠqnUںģzm7ptiɿy!� T̚;;71#`{oZ3IƚԷG_=4TQbg$ߨIf*UAہyMh!�3K__j{q|XV+`guGv?ћvyikLЮ4M @%)KЩ5q/ a?6u� S>~}/}],Xvgմ3 2Ќ׬v9�J<Sعq9.L�@,.J7&%|Ol^\B2[wP%Ŧ&uoEĆ~! �*v!ˆ2r N|QmISvqޮăA�"\]]TL^Aʋ8)n|+J'pqΥ|޵4!ylpf>KLKxQ=>~h39h�YAc/i̺veYO.~D{qۧ-/6*u+)?/ё1v,Wf u\w-m";BXf= *�Lm~Z4?} Fy.(3/OmWމ 9=˩PMm;Of-ūG:;#[#7"v Mxrf`ZhtF濿M-{! |sywʯ~u9ɇ_羧7ߛ(D!8 @08#H�ܵm+OϪ="ǢSR"zZ}ҫذsM]yG %/B.F&ą `۔K?.,AєiTJQ �LX,� I4Ml6[ <o͇Z"?a`4Y! \]IFCw8cGwrH(pV3'OvuIouBϒ ޭ 1܏ȫi7o˴n'vlq;]±݄Ϳg=qIlJN+NjReF߭N?ĩݐe\V_[��:uphyo:H<=qu}sBc/g:Xz5|7(zӰ4)�*]Ըa<rtfڹrv? 1KeR*w.P+2(�'N:|4kpIQ1- =3P\XׂiXwrFn{1&p^Bb~.,�b"rǼW.u\|ȃZrY,>rnbQU!1~ct(satFOߺaLuTH V+ ѩ~*"}W<m[]RS^o5 �@^~lS{5qusZæ</-sqZg®Z:lO\SbZP$$%Ԇ&v_xSj/-NS5\61m6dݲ~%~'T/HTİ @Dzm ypZYh@٥3-sb_"}:?6"9GOd-^ݯ,^P M )IS+#C>c$ /TŞlڨmǞNz3@:AH@Ԯ}h/q~1$8~v,Xʊ~PH51+@\D^;|ܧ׃HPWklMy FuE<7yI8�ZY,%y0�!77i`a(ʫ"$T)R#ߤH9Vb<~}ʍc^:׃~啛Eu<q,u#j7rurnJ8W{-L [l]g}mS~:Ss؛[aw 7]DyOwna;|q&M&dr%K?%ϵFf}Z2б�i6llޠ䑭ek7S 0٧IsM8PjܹkLL>(-xDQ;+GDmw1zzں4cs z{4) {ꙛ6rkH?LO4ӠXA@?$IM`JKK`\|>E*#֞.KovYZ2&}_ԑ/{Y3p׆v[IVڐsݗ^ru:Y |XX%2u˧ߴVɗNyP}eu5 : v+ڭj9 N<\PkE얫_xt_cu2ūf!r\aKsE(`>Fb<V*~ €2 (#*Goǝ6?u之�b!).!$ 2!"|q-12֤*_8GX')KJ8"\Z M鮅y<q2 T3mI *-ګekG q`攇 -p%��ue��Sz>ANnsSG]nW30j7ll;�h6>{y\JY\`V׮OVqT TRsC;S?GX(Op.[kE; pU߻o~i?nKd[qq_mc<%Dj˱sCZ]]eu!ݷ\Lm$7/а(EiiwtxÕi䐛KC*HU F&Flщ~CxhH'<v-ʵ@^gzX1k ׸bVJZiu7)sn BEU,MTBbQYJ)�!SJ4wE @ 5Q(WD>yU},.شaZTiebaRi[w{E=.uY]L /5Υ>h;64w6S#n[Պ?>}jgKPq@n�3G9覴!xmo]xx+]M fߌu,Uғ(홓'^Z2cq96CQMI35ח>L'zoOv;o+%3-aM暆#F֡UCKJ0� JK> ǵ6W料JA!40$Q0�(SiE.S =�A~5οha.8[76ϋt{xE7}Kt6<`kSIRBKh"D)ļ/nbÒ=h5 !CⶎzlT5" .I8]ccRr\r롃Y5hNOhF-IǾa*?7a�jNoAى^~ۨr빍yYu."o~8emHB[ 3*iy\HkZi!wbCM_݉}R{q }y?I\L:P D*!*e=}{ORwxW1�m xwkĈi$AV&0舵-UR&>[d[}x)l>NxPs{;US Oj~)Mo=xmu/'�-<8>̀}s0bJ -=ŬE"5,۫U$Jc?TsmMjUрB^i3hmX|S$ƽGF]Gx{AgdwU tt=H+nsYf@Wz&κyЖbD6xmL*_;W4lbYQR#�a 켬j9ց RRtFK eMSca>N}_I&}!�w_pvڭG7] 2Ϛ:sk?ko�>UPIi=jИ:SR)Z,|$erLF@ `!Ez6Dt!| TyGה[VZZZZ&XCF4}Xhb +NU 0:{00$7‘,쭫yGw8d{MtaAr{ɥ34o:~*8mFx^ۅ(eki ݑM#|t$#2 d=K?δoT%}TRg0#6]ynt 8xAKn`·96fUnH#n]xgu}uOh�L?=bqtFtv l r3(0Jrg7 Qo/ig [w$NkY7z̿t&}/}&抛.HKHsҢ+#uW/HK m{;CvQA! RmA: ,èR.,(lS iI/c yGx~sIİ9_fuN5uIIYʭ|ӑ<{:~Z;�i=kYgtF8patӸbЬi{>-e=G/41=3-UhI vk)SrdBVDg }+jMzqɅοINdai߱} 2p[atuc|0kf4 8vœv{VOh&; fewiAC5촉 ;ޛ׮nFѩEOMz@>͙�nڮe-[K>}u;7BSǐV3G4 J2頱v^}3:}9/*�`9zҿY* ^sx ʎϯJztaTRrտ<B}"hV<$X,$I`4`coQ!�B(J$uO~=pcU\e�_~( hAUH$ G0Vӏ꛾Ðs}+��R\#kuXq5 {OMBlݶIG8mE{Klm2u7]A@4^K<h%SD 6f.*;(5ݶQۑL{vca �`IGA 0~YpD,LD]4C%!6K.ҫk.44߬йmw A_CleVzٗ4׹qw㴣+" ê}y3IfK׹x��qo#)fXZSrNx:r-{}$. bA[[dj`tȞG^>6IJCoamN�3wLloaqhIIVkٚڹ]6lЬY9b(n8a<ؕ6v24v94g8{3TޟZj�op}ȡH�nji&i2=Ś5^u umhl۩/fвuEp~ .OjcaРZ>jHsaA&+{oY[4l,t~K˶3l\5PMnhƄ#3R�� �IDATɹ(|x'.��υ?ؾ۔C@nՕ?=x5754m<QKة6A2,?ɳtUtOyhjW~e(ΩL:ϼ6Hޜ]ڴa!sz_ZW,: Fm=xw& ,{(hh*Dkt6=VogBpT.+c]c5Pk#!ڍWx y,Ӟ˿w�e_BztUEQ8jUީkQG'XY{QG�rHV,(ʿ$bP)r6�a�!1S#H ߏs.I$Q @+@Q@$*O}Xpce2r)#>P� TUUڸ|I|#uEI O=hݶ?-EM*vr"h^[`*od/|;8]՜h1uԭ(.(A3U Z/?|-5d4:w%*͐Nڱߠ{3k=ߦ]{JSIe�icLӀ1i1PPPP.]j^ ~~3&O9� 0ʫ BL\ $ i~:T\W4>}BF1``6x3/<6}z =M ]gxOd>aHVDlbns{_\P��ES2\!WP 4KM]C> HLc$�$b ?LaD"`@ �#��0X鸎1&He6i � He 73ѹGߒނUպϝrXtW C?-a:A6Xli( CSY�BASbhD+C *I@=$@E+#Q  ]IJ%D"e rLV\Td20000lT "��n7 h2I*=(B@0000000ס((Oz�!Jil1i"H�Je4Mer X,b~RP}qc�#0VzeEx`.(6) Aq \]M%y !lErc~uBfTZԽ(LC41�w$c```````? 2&ʣA@\.W&2LPKKK,φa SN<|{eE�{8ZLT|bN;7wв9_d)v{v```F>j}pE:ďg(s(10000000I$I*c9Ac&X,e29EQ㲲Lc 5~<g0Qױ|_`9�_F sy1{̻n}GU'_YѬM;OtKRϴi'Kqa~oWC#+G\ a[TBi?6uB8a?Hbe=vab000)7"Uԩ& ekDQ 0&Y D(Vh 1L X,\P(86Ůg)mvtn$}um׼&R٧ۄ?$Q*f)W59ޞ9&,^1zQ 7׶\>f�\xܦ[_d|oƄ]"tޫ'wT8\BagtwhYUebyƕә9:QwX&�@$bS$ʏl H$G'MQ,K((bLV*+DXR;.Ct=*nOpLz.}~/c6?5o'aq%j /uS=A Xwt- ҷ Xt׺SF#6u9^qL}txIpSsz<{Cŋ{[#_|zέɒ }&:9z pNg&nk��*wZeawm<#C�қ3},w& VJ*qg'];1s]dNV/ؐ� ]hg)B/njQq *J_ g|'Xɷ~x&8&O`G7�9ݲG S| ?)"!10`J�MJ�`5ӧ?1a#&7уZZ9baK]Kڿ$--E$P@"lXE\"X쒒 -gū]3k; eKd.1{5=NtL["YNEU /2[ 7-qwBe}\,_f%OT9V^Z��`[7nDݎ7jg%F��k;sȔ2we9��HNCB\b|o<ޔ�{kM׎68={BGtzQ}U@2)v^ڊ 7>T_ 0+HS}Qa˒SZ{m~%>=7-KjR��Wywυ\y.}ܙSmr�N_gH �sG{(P_000Qe@F?�� `1Macc6. >]3j˓GZmOKQS*a`< r|4 a �(,(˓JXUs}X.c ņM&ٿk# :@\yxE_) 9|ߖ P7A@ӂ - E$ @R\LKK@ *9 )b +(&��>u8oN-<-{t+@v^K϶ڤ"b~Ir,9_B tSFS##A*<{2H oAWw03t!��p-pXjq50?(iKik6t1Yʽ��"Xl. ��B[W #ı```OQ#}z`\},\q:```````1 ИD4h<<1M$bk` I1I*bzJ$�V]1E1 $5-lkqH)Z\IML-̻'TNF7ǺW$%(PE#PH/qqq E"B(@Iq^`Iq B$ DRqDJ:/r[sҎ314341|<]Wmq\*]߾ڃX}+ŘJOˀ &TVJĨO>bX8��TFZzYfz&zF>3#l _?e4Z7A DzTs3 04]V(2\.Q J*-icT.c׻˂T qqQb*H }#}۪ˮ_]޶xE\zf!k=w*��q YP _ݽd"Ehw' X/<K[ҪGF-%m1UCP 56➊>pNtD~r%|--AnV. �lKWΞnӹV}e>Hl?doe]�U�%%ftUI ->EU<Xv3OOJ9u4 -mA9!džiTNgL^¹"MMwRU,10 ji@@ DAӄ\.Hd2BA $ ZMԫu4oܵ[bq!Y=zB&պt#lt}7tmˡޟ5OMz04§W^⟅e=>lSov N߾۔w~rW}g;;y!/q~͞<<*Hƍ ""?�Hkɕk%ef'FN,��C^9ծω>&[Ny^PJhX q!3lUT kG]t]\hbzzbdDbrMXѼ;dlLXPLzVVې P71\@G[5\ UK h\M41"ifX\CH$D "D!-Hez}NQ41<:mk,I,)QkgN 30nꄺu"  t[#Uޣ C'Y.o}wtؿ][륎�-YO]<YrY�T\WcxszW&vܺo]5SBϵ}[4�g+<6>a]%iQk:ldۘkŠ8hP[K3$ޚM ulٮ-_貶B�w{nkt?RM.=>[+[ǎ ��ŽkժØ>Ptݛz~@ D t+LzPt R)_@,D`\.f4M4AȪqk1A%R d2YlZrYgǺn2;O3>_+qCY:ƭ"QA(PfBD!ItE P SF �A^}jCvpOUUts9baK]K6Ә NciLc tR<BNr dy<袢bJ.p8I)MTnnnfVAd2|UFio$SSӢ|~U#```:2(*([0MEӘVzUc\84D? I|/B$EEA) IR$KGMM],V|¶p!2VC gvO7Ed``)(*P'=@LK*/Ta|zJ�,)J*(BN(v3.=IDFMCFa]) &DJ+EqzzU$5pR}hhwTr?dKD 7䭣,~nj,+,,Jr0&A$I"DZ")bs8W6jlMR|-VƆۨ<�Ƹ@w�*c@yFY" _�P|;`9|,h-/v_,�U-ڬ @u)H,(WVNEYُ)׺XXE}1*X%& 덡s;?r2G O:!ʻ>97ɘKߡ!JKe2E)@$/iL(7aJR)PP4�,_d````@�p5|@zwhF"IKu*Wvm)9zl|[RIľA^*//m# \ug�:u7Ҫ@?i54脶&*|Ә%9RS5n=rdzZ$޶j=d*??֗wBT4[, ?2:naшbEu#ZhyFXqN?I ><}I˳<t}S%4_ jsotʞ:%쟛s6{k=x[k6,.CV<n4$d8W]!nqϺ/4Hlۻ HbHIA(G)#A|Gc,ihdUߤ¹�TT�!T.P͝7c"d٨e�@>rE7_s Nk׀Jm:n著 W<Jˉ=?h5VtڟQ 2st+WX%Vz,k"OVxL)8N7s=J�@sڳV_efgGն`KbWsrQjC}F`Р0WqB{[ƻ4:-Ɋ35v5re˪oA>/\v ��qϑJR4%侯?PEza)m5>Rkr/p쫾;~#-+S0KZm,ߩ{`ʍ9wf|4q"y?b$RT*-5 0n1IL|č%:cO4 kϥ 筫iƘh SVPʰM"X,AEQ*b)cG$IAKyeV i=il'; 5ȊǠ=q-,;/X:}ȕl @Kڮ81ZҞ\o;Hi]q||N;zqfX;OY5Se:nKSdLh@{[>y@`yPGcظӐ&q նQf\WTYjjjjjj+l0 Mdl (?.Y{{kT1' f*60wfsgqa {fu'7sj=ݽy"-x\LO.gt= O6;Fu8f۴2Q:�%ow�@gozQUv_v*ĭo˟b㩚'AeAyٙ 5xliQY ;Vp֑*6+Ju6VrZO( MkZBλRi�iəO9 �p֥Yl46_ۮݛZV=RH_=!W}: @AFEu5<��TMM DH~b��=eF"�p=u- P�@H_{-^1{c1_0x˳"e1gfwsUw}n{ӧU1rCZkEt%l'r�E/Lh穙x +/Puo`(JAߧ_suبݡJqѫ]x<ukֳVOۤ"M7j6:[Ͳ[WG�\5tu-�@{tv0UrI@L8sD;Pl��uߚH:of#yjNCNòޝ`.OLAUx|-3OFKb2#P�Pvw()�<hsU{nM7x[P̵7:c"Ogs _ͬ I�,.6j|Gk}Ʊ.) k @H&eddA,b3jQRCp<iA+^ J5N 4:,^^ 9Ra4zlRTJvHK.`&7Q``� -o"��%+]mmV-3w:=<<׸ �;6,K@ܮK  ZͬK<6 GמV�BŬs>5ctn/6諧f*?R쌤T95iT=(}s;0iW-�tvFvKWm=q/˪##w=^߮gz9zV:6mM5ƾ)w9eor)�:û< a?6OA*-}6M_?r-ZYΪigfeY/7ws0�<xڱss>o}]vyYW=�Y\$J oL4$o`�� �IDATJh彸eƷ&y�KC_]MM !fW7ފ C�TC =eivے8F]׃$�D%$۩gq��ƷTb�\]Ob'FO;{nĴGkߓ{w>s 4ƬaaWuA0)}rkHbQc{ePu޾ &׾S/Tn֣zݠB��ً;qdE3߇pz`Ěr;v踐ӳ POdapݼaO;(?bA *^b-{Ob=Q+lQAT*kqGQ{A?w{;;ϖy)#Ckt|JɧO-[/ 酲w&MmzeonA=ɉt㤴o ׵fzNBVvb(*lS90om,1>p-g{4"NTN/�tjTT^S6լ?`g%=&dd<RwUgc^$;ir�,JyvejrM#&vx˅ I$)$I$�c3BP(b\.%HV=TH'�Pe J]�^a<5l\p&K |(+o‹,jc]튠ɽ, (Ro*TUjJd�glߪɇ<j(34]BAu!J0VͰynˠwk_zogLRaf׳?5 XQ@HsԲ.UB3pI��Ҷ5#sXkرGB`{yaVvě{ ]`''|QqLIFOd[6Y_尫A!-?j|un2`Snk$9r CGSp3}bYtD$ף5#r b1{CFZzցC,h`9x0#H]ĥ>4ߪKVcK0rCۉkg;[vҸBmd ϑ^Z]'n"tF*/ij mb@.;CZ<Z߬}.E)#3#3Б]ڶ,B ^>{)ytXXRٿokofٵ^J(wߓؚZؙ 5̗3t,^sZ=pgW~R�WJ;{z`lx8ΛիhC'Jznp3k3;9Kcmn#3{۴]~�g n7M>:{ R.5r  I>|,, Cҵ0 XwAؽ}U=ҳodgcvVW o6ScҎ0.VMC]֧e'[ɼu͵tߎwґWf&Ww&`3|c[+3 '1[7 %O bz2\N f0\�@�IJ2DCri6`aaaaa`P  a0 1V(_v ƪP(��` D Dħiɇw:Xn\�@B!*)笸TMA=qE쯃))*9-u*`bbbbbm)KD 5\pvV4~A~rf^ZQGQi !;L6>MMJ28x^8^mhAC 9"(qs>P{$Osߏ86ٍ*evm9(b[;`n]wֱA rԷWV1ґ+Em^+u53=V0 .",9oqKlj4oʁZ uK~";=]{=$}h\U\|2Ԑxfwj(SMm-�PK`rr=&'}I:j;;q[=p{O<l8dŵFL^F$cG7-ivّ%+|WZ3(\Tn5RyaaYs&Lڌ:8&&&&ѶZ+"ܦ#XM 0&Vx(+;KQC>tafܜjx6Soˋ ѥoV@>6}'Ņ^{O_8 AMsxJ�ӱaOʑ_w?B7uFof@ yٹM zhE5VvV֤<3p񕈂k~?zwjWͮ[1� $GrZ!@4MiHC�4$1E"QqQHTV^jYXXXXGG% BBH!ġ(HPU|f͸ܾ38;j^ȶ0.=Y<XJ"gcjŤ>^>W��Љb.Kwg9S!n]o6v0v4vtswwwws12qwMpbcn,I'޴ϒy3oӠq񅫢ߵ6kr=9՘v?zo:t\GOg^^vn}CDQr/dLyǵI9a %9B._G]3'דMZ[y\OzTsq75<Ÿ(91[LjD~ڄ@&fC'Η �aWW2r[ C$Ȥ2cB'IHe֜WV~Vz&&8Ujm |hL jvX_۱k:-�2<{)ǂVIU"Ɔ\i7KU"KkYNB8%ḿUm*Rw Zz":5f{^oT)iɯyMvC5vZkh- ]wfW\fi>jBSSANRrIMw ͅj4x(Ĉ;N3W4p+pgP|>0FG:pqFFWz/9;gjĂص#MMpZkU')IK~nj(@'ǵ߹kO8iٻ3YέUU_�WЙY``9FR A$rD.#4CcbB07֡HTMJLlrޯBPj%C}T> \0گ=s׬\D"H �>`H㘍F%X,`x/ S'~=K^s v¬p 7MI}}4v$wJ!2z͜=ʩy 5q;pxu ]Qԡ}Eܖ97o|Nަe�^wЯDa%9IQG/[�eQk[x�i^ٻjRAyw23_}lשi5�,*"\&'!_LGA^AZ Z{<,??7htws7Os<:ءg_awPM5EgLqVjV[[=7dT''^dnf @ 1t-~:@N|vDayVɈاI%C~i/%4!'lꔽuҜѯsrKG_c_;�2zinjBxq�pIBԃ2AGV\,Az66'JyTnf/<\&GxFb\FnGJ:+bGF~'bNQA'yp򀑽 jh@֋ m5bY�NӁnfBfnΛb2�#\^0~WY9YYuFmI Ϩ/H(9|ߥk{O'蹹 3rԍ))OW. o뭅[GND|{5Lz6qY[pm._̷oڧ\蔌gi<|TR.c*_+xU?}m+ XpFkH_ �+ɕ<㫩%EԢW,s쨫Gw/܉Oy ; a�XHCQ\55%"`BfB.EbB1~f&FVLt 2nMs51 ޗAho#3S]C3#+v},?kn,fUk 1x^u$غC*<򊐶&fvG��HS.ںIhK\�cFna*5g4C3 0gEG%>_'pi\=םO}�vߏ4 H_ۼ[Tk0x3͵jК/6x2ck/o ,Fs#^GǮ&Qȁlr8E$ {jaG1ovdW':S/i�Xn䣔bE,3]D+`_f :gυO\j\mS6[Gujkj#9zSۺQˬK^#ae= v1~nsߙy��eOrR@L˭M?cM8s4ʡ%{}^FV t$himjxVWY<;8.bƙ>��~c\-�ɉ<Cy֍oDRM'q[駒hs[ڥ 2i0DbH /qy8eIhi;:Q;� S͆t$I<crt+BM սK64Z&c3|cO]-Z/Ѡ1>L-Maeziv { nM5H[ �AN?#Ʒ.-5%�@ҽĽ�@Hw66n8TnL~Bԝlp}[pJ #M8-=WA}fq\|}ˤ.6n^ԞxGWuo1`SA]Vam¡b/k>P`rZeuN;N-nP_̀{no-ԲmlGջ~�Yz-t��j4d #G!6ȩVTwO|욏<]C�0EQ4C3 (Gá( 2)rtkUtii\!e0 FHnMt@ۭ}3YV\t[ e?-oOнҵ}3s:ճi5gtm'!ϊܿz֋IwLuz䉻ِPZ=@xE׼7n昶uZ֚+/(xwʠmo!<6gEbahgeڼQ.YXXX[xwGAIYk@ Q�!@# t+*3#� x\P Z8ZZ-}*P[>ܿ{UvZTIŤnq,6hA,..tΕC~̟0l׏8^)fF¹ԭ(+#YiWjo}S}>,,ɧ?زci;?+?? wceM7۶c04d3 `\(!}`N:5u.J MS)H"f \5.Aj\5a6ۨzw ^B1624m%^Eqazv31sj7xaxJEIbAYͼX{kRu0�oZ5sΫHg~ jcgail|θ)=y1IBy9[QqZސj$9u`xaN]ةH (R^Zl7lڷ(=,x͖A-;L֞:)kYF|kT*�@ H$$U  I9u쩧*-ȼ#ZgqNm!"+<ɬzW"nu휙ְ:$0M3B!Eqh崂(Rr9I"@$aR)Id!'KTwhɁ*6)w<w[7#6K (,8YjM;KM.%j߽R ;aR+߁6,_4s]¥kh;tQ^H?P/Mexe𢅀,}!gݤu"�� g7[хW5zo=3Eu .=< 4PTNƠ :2OuZb*(*σqdƿQwhܧ%XC;P -Zⰰy۬o-a=vRLRk�BR3 BcDa @.eF*Ȥ [nW?4#yJ8tֈ7�r~K7/";.o+G][Юg~Wv(8W,&ݦo3ўyyg}IGӺEMMyp\F C_TX ᙬjL<ːxQ ĠQq/�Q#.feNEYe6cl^MKP̴CTK`  ҇� `01`A#`aaaaaaa��\!9 \"K%Ir\``fRiB&)/Eeia¿܋{wj?t7yf0iȅt\H,ʗgΠ1e߲NEULV[䱿wr'Ҽbe!?x59+[VzwØ^z/9$=- ?D$7mJ=;Y� 4@\&V%/#E%S/兖'}:]CB6}?1 ,JO@d!0`� B3Ja,,,,,,,, -e2ICӘa�JKK(Q1 r8DRj>>9:zdZ Sbʰ݋ qM9ƆI-f�006 Al9`պ6J BNP3 V{͚t9[\v~-]n.W=_)[?O{jjBJW [ؔl⢝wvF NK;CXЃ\@f#~r;|ɹ-09lH ޫ )T˂X�Uf5,,,,,,,,4�l/҇JOG$I0r90F f}FtAx)Mt3FfCiA>=MW'sKw/||fN$~=]v; @R>8f)ZG#@EIsg1uک;L' U=[6(}A֞j ׸\:޶/myQ7g^=z=: ~IClT*~,,,_@H�*P c UN%EaeiQ9 JKKbWMTZ�=;2㫅�� �IDATry{+n?~ƒEew\zt:Zt+!\yZ;huCfHrʈ:8vqT>3"z[8ѣ�oQpVM=OY f_n͊n o�KyᤎksB}lNraVi��� K<XXXX>9A0`6U UBD�z�:,XXXXXXXX?SqxrZi3XI 0rF(/IR@ Ip2^~kYXXXXXT $$"BRC26@�X:D1 F !P"b�ۧ_Ci| OX'փߒܽ}ӻm;3ʄ 0cQ-`L W<BO={B"$ZAA "`B!GRQ&>eaaaaa Aj �+(`@$OX4ҳ|OȤR CH(a(!!L @ I0z|_` "!VeDeBeZD\ q-_|֚so-B|R|O$%D"e0T9ibL$%)hj rBfh\N3?Hvx/�ʭKXoi}J>9,훟1 H �H$I 0M3r$"0ИR! YXXXXX QP8@�0ʨ HpƠ"@C @� ZAQ!R(F3OZl`+>H!eÊ,,,,,,,,,L<RIBeB)�!!&`aaaaTKIc20BJkj XXXXXXXXR\55@BA҈�c�``,,,,,,+#+S`̍*S!�kSR?L.QWWW(ha#N �¹"AߜRP= cPH �BT*�= u:`aaaa@2wX_r�@PG8"֠N(  I�@BP,,,,,, PEcBPRኀ�2сJEaX=G{UD:T~j OA41 *gN0llTTQ9}}UX|1蒔 �XT_,f&"Ox%9(Eۜ$S| e՗DȋrMy)Q{_%ba;o܊n/��@3�0 0A)%~YXXXXX!" Ha*^vcT'DJ=roq,_Eng1�Pv-b MFniyڀ.ˣ�xXыZ9N(PAΧzz*Rб+}Ɯ(vDJkX.~Y%tϟn>秂ߝ~̑O[zju5_R( !ZՍv?pV#}."Xe'9��&0pJ+0Er8/R7 ˗A9M0a0hky �%"5|:̓WݪX_7jBv =# O P+0mw_-<09-p5srVׇC,So?*y`l+Muy*9AWeju\0YѮ]w*+:g{k[5__,?] j͊Wic2oڿ)y&NKIge=B:Wn!-|֔�кht/'%.~w9Af4,d2w1_g||#mɹ2a3ig]mHVqno�ѓ|]\uTv-н=]ܸ)2ae$h� $ I"GQBa/aaaaaa<FDapՇHpܿA}ZGyO [|S �8m0j\USN`�\ȼfs#췙Ç5[r7 ҍ3k? خyEIjj%~4H9%OkTƒ- ^pK�'C~xb[Lnދ=_n%XqҹZM\{`Xvw {L-zV/$ϸ~OWŔ_æs_}9ߢ{էٵg#wݎ}va_.Y<aK踳żo>\dD7AU~K'IQ2<"L><#*)A|m⼰I!aoO >@&e%KN �4,v?1%BlO`Õ;9s_o*4VXc�NQI4M40BzTz?!BcXH?ΩL0(bkl:Y4|>3SDm8<ܤʞ\"^MPOmqAne))5L׍F-cgzⲱwOQ ";imEEge}ݭ\xr�QTN:X tMNʏrs^:?å$M<}id@'mll5d>T`̉+*in FKh~TqA&w0Hÿusj;zr)E 75ݽ9oyt.}`.=:{W;K (~��ٛ;R��(cӵϳ_/ i^mF2?;O{JS}uyuM{)gv8oOM{FR(^hAq;md�OLSCۦK�睝\WCn{M,5/+O)"/=\o-9v˪_ j^znn��׶-.kpcS}ʥ2��ݙfC"ԃOHp;8 59A�|`xKGmuߤi%42=XQxlzg'}uEYW_eմxΊ {hqZ//�\tv<m۟\GͰRnfh>JGChbh4fmOǶÊ1Hۤ^ĕ7طTòSWG�\RXu4x<��ȠiN6F:|. HNz��`7ې:6ibk(rx-%4tlM K@;O=P`Ք[r��_ +�GNpzzoձ&�$6p9\{)K憴P׶0t: �8t3mu55k)_|p bxI1$A$DL&CQ:m|  zgbT6vA~>cStݕ�57/WW @]oAPNSK'=ɾk  ә� ܹ _*��QKvnuܙw4��˾,E: ¶=|r/X8oa~iU/=m`SdR<5c�M֭=LP]1aeYpBZgʑw&֚կ[}�I̖oKPN&5b>%ǔ<u[ GnȔi9'=/f�WҔh�&BCjA_ ͫlҁ{'X>/)UחpLgNTvI5ɥ 0�E<ЩQox_"|1�@UZ^^^ry!=τ[Ko$Zdyu-4`QZtpŤ7ij\ғ]^�u]ҁ<INXd]dk߿+ j.lӗ})R�8vK__=3&u>XX-��ioQyyyѡ ..8;W6?HM<7<8pѫG6e]f{v^]pݩ ~#N;"E~F7\p't7qUS6^Tzw7 SgF{/L:]!ӤΓ"K��d&_ |T0\'[>iȊGru'GX(MʵkWӛgz.o[x a)ūG&?'N<lo/,4{}>,&'~FUon얿)Ϛ_7dkwGQa睪цyoDo|pesou||=K wr �Sf^YSd>{(y0!#񐲽˯? IK`Qh3/SOh1ij_.,zA&��ry<L&��e~DD@i?զX?P OOWSƆ_c3 X\Z[ |(+OՏ,jc]튠ɽ, (Ro*TUjJd�glߪɇ<j(34]Ba a`0[Ag гۼEsFd}Y.)#m5FEP<m=~ЩG mddg�?lZϸH[BCCSb//ʮ{o>ji+wuB\LZ'aF ƅO2%' >yioRg}îjrʺɀՋzO!Yhj^5 Myyd_<P-LI|L\ U7j[v[9^P u=Sc^|Nf.FX?-t#m'lnڡ{K q&Kp?G{ku.Y xlҵ %JD 9h|?k}֓fߺxN ̌ ̼BGvՓk67 /xaaIg>g.^Z{u+u(}O.�B`bknhhag&("(2_>(Wӵv"\xqIjU?OKKFh^}�kӻs7KO_)ػ#顃gad8oBW%(˺ͬl/M~9KnvIz)\8]Z{$ 4GP`fT3d<JѻMO&,'I4J+`c|UHϾE C[^%LI;7t󻺘[5 uYC{#$#nb&27۷;"޽KGr_՛o޻\cc#;߱pn=J^<PJDm,|l0<+ H0XP(hp8$Iq8�P9/M/* *FJSUJ$yzcG'q�iL9ge"h 9":rRNG11111%e"HJj.8|+ Pq9^3/DD#䃨TT]�8rAOSӲL6=װE?wh E/ #I7wS.I?zȉcK֤_4̪!LލB:6(2A3zd ATuL])2mk:<^f$/2 j\p!gie$y[k]rfSyޖ~S</Šbhͭ'H_& lg9ik=Ҭⓡ<7[G{WCBhjkX\�ZB(/�Vs0i>kOQx_߉#/؄Cs|`g!+e7f2$;ix<Oˎ,Ywʕ @*�09os5,,k΄IQ<TKUwE6t^frdv*eeg2j(4ڇ^;yTB.:یS4x<OfmyQ;mBG&okR32w<d4h?~u^ x:c6I9rNXG7A3M !O;;7P}YtVFXY^j<k["¥{7W" "<c굿r o\~l߽^7GzoZ$�BKupfjuSH$AIQC$p(b1S-3Ҳ/$'WBO__K(nޣ7^%d>J:R}+hM`=+ @Vh>�Ph僑 .9׃N]]n[x3-kE,.&t v{%~u3'\xx& P<ijF=WoB,V<Ѕ?w)agjVő:=X]"{ $ Y'Ni%`i#񅫢; ?{(R TcډngzZ *.ٹ IIxk*v *ޗu~x]qDaēSGO,.O꫟079Y 8� **u@5ӏ}r=N6im%qz穰لqƹ4뷰Dɉfڐ{zA9 C<2B˛fϗ�a o4dq$ 2³F`h(/u_!j_l=HR0Ƥ *Uk|hLAbޫ}mcsNɖj�@wGY7W<*xU"Ɔ\iִVQdJW417Oz@u~iZOE�AKOM= dPNIM7i2"-521ҠnHfPعƧEwؐu~ʹvRSANRr n񑰕JO>6qޕ7 W6" ^0j[ wؒ"ūCy&1T" : C(>SW@T&Hhڀց32 ?(9;gjQ%z[oG_Oz�m �$ioMMeh=IE\!˚<y/Ҳ"g$�PXgE.tfzv&AIIH <r<c& dD?~jW,i{ƫr<8ה[XԖ:8|tO18Ce8g@�@$ E7_<s;p BP4^qڕZ|D~C!ʼn[ݧ9[5< >pS׍K w,#1{ȮjC5!W|cKuwP:Qaܖ9ϰo|NڲJKs/-]3ZwHK<zG\BE[٨c7ث=OMR�j\NҊ'=;.9܇[P �X.Up*[ er8< rhSS-{8TyfsF^n<�0h_>x>OϵQY oX[ᡚkI1ϋz5g)JşZ8ސ ^xrV͌sfg X{;@Ӓob/ay֕IV.W!&2�� �IDAT?՟g+漑6sI6GuZ5k͓ۙ1yʥ|/[c_;�2zKsL6:b ҳ$\< d<ţn&^j8os5QV652j2x֕V:$@}NwBϣh:k;4-.V4o蜹LoA>2{_y^ih@֋KՈ!mf �@Ӂ:zaV݄aP o6^UPUWj6)?^ZcQrKv2(t^c7Auک7Ͼrԃ'. o2[ 1#=ɗWekFoq7 uR'��hvMt-Z}<xA1 \TVqne?`oʹӗ{o,bA�Ϥ_]wԕ1}fBZʎ~8nϼ0DHb (uuueDүah`$IMMNVfB!H>TZRڸI 'kܴEII}c{tqV oQq̨X僀Bz 8;=4|>/4叽;xӝutc� ޿i>y24a:xǑhgkC |؆Orl>>}�Ũ]w$ 9{MCH|aX-b�S(22捸 ':ձuL'zxyN3 �w#c/eYu%ZooMlv:RMOnF],,yY 0HƸ}gN3�=]ˑ[gKu�3- p65&(]UCB}^FV&t$himjxVWY<;8.bƙ>��~c\-�ɉ<Cy֍oDRM'q[駒hs[ڥ 2i0Db#_pˠB;v,uHoU:j�d{`Ё$�0W67zLnbE)󛽺wF{&^dBv/vCW2d:K<yzvxo;SFp3#"Ez{hi^sžBkӹuMgwph35eE־@ydS�K lGi�Pk:;5t<q2�h!ݡMͬ.Npl;&u'26[\x<HӨ;NK-UjPY&_2['?2ő&m[ T}׹վ"aa|ئmAez7p辻˚>:ܿzٹt]ӎS[ G3`9[j G[9Qn@#crqt ;gE) �� .&|5Q r;$8'#x|P3@E.\.1 $9$HPE%Ebվ}{`XQS;gz~!_D>o©G:+{Kk|My-x0#mu!HB B @R~E !cڀp:`0:�T\@P--->?TyXjsVm}k)jS%gXǑ٘9W~1ð_?"zW FSXdǦ_OO<|&`ˮh'ܗ? wcJ-ozm`F0 1f Ƙ@(22= bN:|@*K$2 !ryjjJCC;*.{ĥCAB0PQln}v}vw{n�Q{wcoµ]vwfΙٝ93s&i##cS;wXZX c'O _57;�Ux*EB=7_}ǿ/*6 oEWVIv{4Tب\ɻ6_\)r9E"0 C4 �;�d'''ꉒbbc4!5k;yJŷ:x-h  Yn"̽!BI@d_ŀ<? m?" dJ{ sQ70neL [C9 >G0 Ƙ0:"P(㼼<$αHMMMKOիZZ||Vq_-~Qx|%_7XT~v@l 'c( kI$)BB C3.#._Tlϟ'{7)[EѮQWoG}? B"rsA='O"!((iQ ,f޽{ohh0t)+yi^q9<+~oxxx3AA]g„sR vDQ@[0L$PI `̼}677gyȨ:BanT__x q}ХY6�xzCWffE=={i}lQ~#:(}qؼ Mi`"�rE\mK6+Ňu{ k|~>j@0N[!`D!�D# ,[o<<<<<<<<9RV* �a1b!�*66ؘeWI$�im6&Z$ɩ=''�;G{L NI;<:t7!}lM f >ely=~] מ}-)ts^YҎ_Gq5M[lxK!?<$.9z+.G 8侓Ox?9>gÉ訴 9M!rOCٟR��Q4MQM}r;1\kCQ"SS/^޸q3;;';+W 02e)Qwd^Xw| 8v ظy;K̃~zߏZ.wܑMS;Q?P6i΁-o|$-,pC;77[)ϯxB]Kg =(:҂K| MG_>QB!ҧm1*@"?0<펄}wpƲ\/6G? @" d!b &!@0-N̻w>N(�@nR)uLVV~P$vR,s,*em(h0=JQ]֟ OOuCmVJBd9? ;# )Y5Yx @aϢf9[_@o_(_ܞ{mݗs/ c� M*ջҺÇ U0�h)O!BDQ0(p 1gY@O'ZPYS hByvvR)B(//OX 6'%.Ф;-[_F~MQWk~ <fTgMHf^c־Lįm5^ٶ*;gNg)cИmzѹ?gKdMhIڞx6>%ؕm4p޼>z8(:.)S*4sqM9Iֳ&,>tY6lnb�@o:yo3a3vۜ�Hϩ.�o^,Wq!B�hǴs|"fĨ%GXmK7mP/[[o�ʼM ;te~ ǯw}@m}62: h7un‚IuML�<t\owmVmr*`F'o}�iJuߜR~cnIgL_sdd%2Rf~ >|!%6r,u̴ �I0ഝ7MʢM snLZ� n9(]{hLMCHGd0Ku! 9>>ds_O6_ziMksHP~|wfx>eP;FOy Zfw[�ЦsY}TT5Urÿ{ߣ [rL=qƻVaS7qzqElbYWo>H&DrgF:m.�@>>a#uxw&'#g_2B�m U/T -M5/}_8�fhe+�!W/}oK?[5LxJ>|- %([*0\Îc!@4Br~3Vo ԫJO=kn^{e =P.W=ZԦɌZ ;hrom<kY?yqedތirQ^JȺ}_P9pE;~lWԘ,'{ 'Ԩ˱RݧsoˌaRNPQn5Z Ǵc.ߚj 7OݳSCio~U,ܑZa+B Axq?T2cKM[*)Bva{_OOc6uk4ߐk[%_Y9}hYwg4 &=ǘpkOԱ֔ڵn޼UdժUwVB/dlUh&HtZ82 |3$[VIqQ\ZryνuR^T2kи@:fN UZf=5cR7U*Ū~v ->UȞ#`^;yzl7Uø6")Nxh1p,!5ҍo5$ܨaWL6zY@X C_H8a^~hh*_WhL֯|rFݹ~bL5(N}Ͻ3UL{&0`;oʋ(xQE7w%eusb—Oiݭ]J:p k^%s7M [S��?v͐e $_[W mզɉ7zqf7#iZK̭_M }rfJ.rq77o4+W }LӷɌjge(T ˊO|W_ZpZ#@!�( GQ@d~ B,˂#3P"u _Х\YL}~R5)R|[k_KڢrwR!v=# ) ,�ahEl.CfVԫIO ܦ<xQuߓG�f% =|B5F\~V.]'FqWVrw/\TJ%Zɭ 0PUwYK)P{V1�7(5-s*27@@\9vM!@PCOOflӢtkvq !>xQMS&V)B@K6X۶ejCCPj,cQmJ`$1!5U+_"3 ѫgXݺiՠ_@6v �m_qH,є�njgZGֺ1o~VV/c/o/d�εچ�Ԭ9駓*QJ?%$A5T7B_I]!CϠzZuBgРl�qѨVu.=ɍtT3v˂]-*O4jDbS? ,oE_P'&3Jn*ZW/ȉRW'UWO/Yy)2$t_ f/"Ŧ"GZ@K_l߼QG& ;nlE\wlT`KS$3  n\e?6Eڦ|hpMڵsu^jy\&(۠U( 72:ɟQF;ml#K�lDdKN ܖf>M`kGAܛi2?kCC0PHC ,&�Y ?! (Wbm"gg B$7/̤bGJ9b_xެu Bn٣szӾXV窊B$/#W ב]0Qۏu{~1>=)> %_Hp BCU]"PνGħV y׳f {O% qdWhw'*91I*N~ ], )!%9=̲͋ěyY㒱T*A~:$?*?߹K(V/v(Νf5 AFJ#E0~8CC17Vlh(?n(4e,Wml ؤOB%MNNj|Ff>}*.B]#J:nq.uI~(PtE(=N$ʵvUaXj_Jե(N]ᗼ -'bL933s3sv[qXu7k_!EPn.:OSXϮ({^4k9HJum@t\8Ȫfzu?0 xNb@.Z4=u~!7"WK>++Dzqv/nn]?!IVf^ $�)d灇ŴE(ۇ1,Q6I w aY�( �5054ϓJNyHmr3+zIP_ldqObM;IAh+蜀pZ s,E k o &ll@huNOxytC޴jc�H5Xj]ӌ�26a�c�""]v5V?iTV5(tPyVߡ7 e`Ȕi0#7o{PrF͙2\qR {F! 叙0 `нQ9:Q珶@a QH]Ew\<ԉV\:5nĜV:n{%լʟ_KWO0Ae+7o81f +ӺaeJGPTZ`Ȭ,M-MMܻIʾmsSŘ�ܓ"/@iaŖ͝'7ƹlz|HWr Yc}<<r<geFWWPh�0!B�@ ,D��L%�� `Eшf(J6Tx.ySPH õVpmFHN,)~|k[wF3"Uq~UG۷E:`B~kz[\a«n9b` z!WzSX3|VdcF^HU9)-[](KT:eʺ3\T~xqWz7#0.>z)W/BzJjΎ,*dgF]&;y2e"#ЭNٻ`N:-]lb7h5ZC'Q3\A IAfJDsr*ScNS\4\UGpiׯE#2tȋБc)xhIXRk]^Q/ĊϤ`w`~I)W3.$)2B_&]Mt x\۹G/\<+;-nex綨\v8GC_@Ovԍy&K!,ƘL8}쪍CL �``CbIՊ'3rJā 8DRpe�Ġ�E�� �IDATB�p;@AlcLQNPZEż}' ǫʼ@gNuO;6ֈ׏]}+c3StБGa!7O/I=*ǝY20]Y?AuZu{NY�cҴe˥#$_[PAUw^<_4)ʴ 4:hso%=DCQ*7¨Х;jߤv3(o%xKbd:W=:ң{9'Um+?@KWjWKRav zږWVMݘ\iJ:4 &}vxMZ/ḠvuaiqjC@o]cKelʺ,F�@~69Na"Ugov`cl7w?~"-!_s cO,nUP�Fў4Zwt!C̩-zk($HtyDr.&T19FyZR~5@RQpIQ] %I/_ kl jnvk[tfWd2/<٭}rpmW}._B.>K+Tчb<s iyݪ g\ӳtpر:'5P˚VZݕٺ躰I~q~ݢ!Ǩ=iM<Aq ,л9"�$sZ V  V? B�B�! �0B!�DD"QffP ١MK>xr=)Ưrӆo;3vȠ Λ<t=], ~d#7XհE'zfK+MjcCq;(gGV .tElb3n}ԛ>}>k>I},nRNŇWϚ`zuL)Б{ %WU PFٴ^}5uϢl}&7n`@=M_7400`u@ЉMlZڬ2fʱIƶ_g_ɜSzfT-(Ɉ:`Ô</tqFН6>gg /jRA0l# nn7/mNѱ+G'P^ALkbT0 $~xo ^zpJ3+2JC(+Y-+!Wh6vN$6::|3c3G+tP~jG~-P⸽L8NJdVdZo.jnF?M)]-W/Mf|n׉dϾ €&,7yv|2�f[7zTMvMDC8zϔmO?! m>T5Y=6Н0kط?V 5-ԓT_|DZo tIZjܣ7zu\*ԝ{dJUEE4 DT"ABSK4 r@I9p.�A�$_yx8;9 ´<IPGib a<?N?R'X ~QSorreM#Q*<qqB3n/]Vu \Jw(Zl]mn 7]RUT& vTviF`}JFͮd3*ooP<ԥ( QQMQ P"�BE!mw@dr ! @j۩iX{)!oxx!Od\U2)%*ؿEs҅j51ܰe>&B!R0<Bߨ,Ɣ5bR& D1nnnBii:::4Cg` R|G <%m"GCEcDŽ43p|~Bo͞Y:joJr[)KZ㯫6k'nJi0o$*/yLLWj8  p]1~ JL33C$3M .m$9>)m?X["M2T+P{J埀4#>(eXДuqS^%nHR'֖?LK֣&Ar?ǧZ5ɔڑD�+?6ԧQ& !J"@l` 7`Y6''[h x?JQMዏprf:-F5zaRHS FAe/jSJYW3wzCb˜= @ʯ` [@�R %EH?|= �g§O}KJ$7bEwo:�wfT*UmCQSx?UT@үZ-5:ɔmz}mY}gI. }1Fyov|ܹ:ͻWa<<%vH"^(io'{RE>Q(RNTjj*�0R477W@ DM<Z插{yeMp}BYK_~C;}ȡס—-׼Y\֝6<~*+.J0wlTRA5&-Q@M*[q G3ȲP%,ե4ܼ5L*s1ÍuM[/Yq2nh`ᱍ},t lʵq.p'BOϬt '翁}ӐK[Xُ7h+}ͽL٭MuEFU,&^m\GZ榺Hݰ gFUU<Í\LtuM\mzTW,;;suWS]]ˌ\3J_GdTk?d_u(Qʦm__m((k0Y[V~ K>yƢe0i~22tiglS7qm,>`y'{Y:S{Y,#O Goo 6իO)o%Ncz4Mq. # (@V6|>xxA_b<"?H8 b,MEB0f1&*!fqIy92%rJשr}scN?EҜ6|"�$qadž'*zҀWtrvڕ^kz>VJ{�=־UnNo3:B"P#yim]˫fL�Fl;fw?&>XWSzydfUU1dO׷o!XHjTY2Ok=fQ~<Քk*aH3{ޱ'wr!�*WNN1nʧ,d^y SBAnZt)SK <֕+H:3ܦe_J.$q瀶+ɷb3٭ߦ8^dfgggggO>#,vĖ_e~,筌k qm$)===)Ƅ�(a� ///;;G*-8M[bu9G ?Wx=|52U~gP�^"<k(<�5}]={lje [2{Eg:ctp4�q2Z9\-!'իO8ƳV=g RN7y%kgDL鳧 wuָcgu6a2<xmSU-ءvPǏѯK=qtJ'PgdlllllLZ8=ml(`_.Wʱ$(%:⯽gWSd\a@g#]Ј!! {wߒ:[7\ #s/kP,�YT_D_djZ{ɍcVqY{7L)CRkPe]6Ϛ}.' t�ŮߋR��V-=mw4m: Y\SD6.5/D$2rcebB2՞!n"ĩ'9Fّč ޒf=XףPܽ6nNq !el k,�H⡡^vzBOQӇw03l8\"'4f~uK+V$΋Z1P #ݬLI��*bSݦPh`;v4BH\�z{eZz}�kc]PߺB of:'byWy87|j|hYGS]Kj'5Vz]�$>\MuE5z?*/:Mɬ#]-#Up4t7]W gcĥ֬L.no<.L̯}�eE_\YHZr 1D"ã̝->#��8fI7XS[Ҽ@dT3Ԩ97DOGdRsΗEZw"]sϺC?isSC%��Yv= � jnojBoӽmȹ8%@ĭicVu15v5@ �$Ȱ^ƺBw2&'ҥ(1fRF RHWof7~o?W5_0%yf)q+(es(g/% a W&1@S֛}|iG6vHPa=zI_|8-;{z xf��h:O?�d]>Xy |qÇpN}˺e<zÂ8Q'M}z60R [mImLQ~i.~ۖB�@:WZFY+[/^O#M b$HίZN|?s1\2wx$~U]}}T;Ug:964 It굵X {q\G}SY�yO"(3;O7k[M~ ra={z~N;;Esdggto"ĹO�4UL..𔺥(��k촓}lK1|SN $M]q/bVXn\SA #�ڭm%2iN-+n[{S5SNDd�I98ͺ^"9="Yfvvvʶ:ӡA?Gқ O<y7__1oe|U>2k_cșz2zGX$gb_]G]B /z:&%} Uvg#U{<I*A!&"��nLjVoi#OF.|otY$�gG5 gFGZ8WMƻ~} sGn{9?ooBW8)!}ƴ6|bҺ>hځ^Wao\X ii. tz{2;ۯi н Wx$}^2tZ4 }zjB돥�W+WWWݻxwi~i|CfW|::jl7(ORƄD)!RF&&&/=0&&&&~˃o?Q93{C!bR()Ppt[ BB�(Hд락;3jPiHVz70@iL<lڔ/W`Š&IZHaѭ<iZzH/ }^^zFL|Hpat99=H @9u3Ƃ5\:ַO{m(eviKq[hbh`UOq1U:ȗWV*-3MR}N|~Zdh=xIǫ7./.ԯ;wN]3=z'jw]!ɑq9nY>aHss12SԦ|rim+ej`<vt*nfY20|IDF:ڵx":QU@,I~ygl`f)S6}l0af3 +Nc9M@f6,j;jҽci}u`MG-mg]aRr~wpu6<h@R7 mNy5u>Hl$>:2r@#"o8Hw:>ܶQ-/ʹ̦ ,l,lvQLbR־lǮu {,IΝ/Z7ѿ~hY#>[Eao#� Y]BH@lA9sZ;8M_]кê pQfu/ sTzfEwm~Xi~Va=}9si޴ƓtdRYŬ(n37,mLj&΁b|spHY6tP +U s}}њjcjbo\*=\j'@fne\Mui/~g}M{9V)և8ԩ$G.^Nt1KO|Ck_g:&JYXnW^cS-WqQ\m=kbqN5 t@4�eFgddfgk+G`n ku ~[tkcLi}9θ菎 o^( Q%sr( ې|Itڽk� =YIFz&j=ING_~6b6}ELZuݻwool(ȔTHfz@H>!cQw޽{&b}LTBl +bBUK~婡̒{k[d_ڲ'A0 m*Uw y9)/3^I]B\)LxLW;vin"W]ߧڊ-"J4ꓘ 62= lLr3>#@'Gof?ۻw\9$ tӱß*7C*P8Ҡ9 t?0KG狛?q&+$,H yw_H$+xĦ|ϘBAVV6��!;;ps+4ٿ+үMK7@MZn>9+EFamu{jSھ\Yg q9V50D"ayɟi24ggfs AnV�NQ!Ovݻw޽o/ Q>öoi8ũZ>16wuxGW]>^P&:gO9p$9m C΅!z"H<$g6C|[S7q̆^u<wQkk6dQE>C&.uKSUX<<)n&Z0>Cئ,f.籬wQQ9XgťP^ 8>)1LL1_9|m]T&żepo,�Hl(B;긗v^SB> !LNN@1HyRcTqss}2:*2299_`bbbddR=~]x|:=杘O`?rf~vk{hsF�H=#RL%(D83CX�@>:SHʅ-jq}]/_ ?`G}B{P?>?w^"@*Ǵ]V7fրvr# yH[g}qan^L#'bl/͌T*M}J5y( |x7-wol�:c7\]IO>v:b,զ;?lᙯIRΏn>Mǃ''TS;[ޭQ;'?7_L7ߡn, LHl{ �Hާ$蛈6uh0K6ںFeY3�� �IDATU7ϾW- ǣ.ض6qj@dc<1_ľgV5eh:�(jWy{_-:*{MC^n|ؼӆDoT@7z$Bhk;krk)l]wu"[S(.5j63&mU�cWo̖z_۹uA/b2/e:̣yUT*wDT%$I[ەʺGe ]|�<ZLbvh5;:6M�@&-˿Af6-v9]@fEF@Yԯ?Ցnfti$"&I<eJbDANz[9P%*Y,"ɋ<<aɉX�m$ݻ s7Kuk~i:aڦf 4HY/J7tmlc"̺}v[/҈}"M)MӴ�ߟ&C �.l\{(-I�tlBHnNnVn!D@3ew"$)[RgPog([@!- @�" &�ߩrNB؇Gn0yyTyܜ(Z#`|w.hM3jm#&꣤l<Yw�أO~pmY:ߊvʶy)'-sbҪ75&qu7~;Nvꣻ⚪am͞xo׫E?\Kh'/ۆ[ջzg۷\+=M,굏++ Z3u87-1msͽ;`/ ^d\5å2NnN�:_ +y yPs,ɕ IYAV$66n\pcMNe6Eӗ^U/)uUֶ\ oXM>y٩,?Yg_O_W&ĩ_hdG:aj@.rr)# _[4@ٶnvA˭�nuwз$R3}/rƠC\ rbsl*.~5ܺg9#[6-I{v=ܞkN5CoLQV�@Ҟ^{"ws7{̜ {mRi/4jRE} 3ǃk9QLNW3nr}#:E@fO5ؽTI-u3κa6d7] ==xN\HJvG6� ;֞5xӤ&8MI%_kQ]O軬Ԍi=3ege}xÉX|Sr>Bu__8ִ|:}gRՌãAJY-S761��Q;Hƃ'\˛$2] ek\Gpj#xa3-Y:Ҭ :m[Y,jinKM*`P"ؘ+.mB)FeL;( aL|~&Bk)QyyyR)-J))"J逇?C�w+ONP%��PD>w\$]xzyq؆D-gkt%59�@ayKscIѵVl]`jG_:ǻ.rj@TeCxT; }�ʾ皭Uan&&fݴ6I_<~f q76KS& o]ܲ1�dܹqU*!I"^katC"X-O 軴场~+zֿLmVsή{`eBo8f4njfuǍ\X)xP;��.sp/J1஫[{˚o!M2W2W2=cv+3Zl:6;H7@vozƎuP!��Я5//ҡKe!�t134uosneh€ٽvvi0lKV2f;:o:| rCGyikaGwڶ-?jR�dlp'kb4�3;V3[ 8kVGJ2jn%uݜ˷~&[]}xmݫzVSg 2>ܷ}A|J9%$}q`t3?k##/XL,=|Ԩ wC"f^; �ݽoO.=� 0Y::^�8mڎl*L}Qɳy*&Nzzr<Щ1u< *9}`]e$=N>u7g`>𨇲6+hpm_Y'}TTM +U:OZ+aө% r09 r,mT_�r\Kםsj��0e:w͢5dިM iVٮ< Կ?dSz4ߞ5]l)� BP`4MK$²c<p{%l܀wPx(h@S�aV( p �Tk �e @ݚ!=AeDp##j6%hs[~R5QJ_/툑_+6 JM%l©m4vyiԄuzY(g\S4JfyF` m~Jpߋ_7k ]tZ`ML0cƄL0(:"""$$`ؿcFU=rbYeYJ,5pHP!�eF1X; Eg=Pyj;25 ĶԄm3vD>]KMiK- 1qjR0b<Fk" (PlDQ+[47 xxxxxJ c?A"=�DinD�)HP ^3N xx�(W҆C)}V `EO17DXo?[4R;XѦQ$�� EQ/%v<<<<<% ?^!mD!1 D0m5Dd&K:u)@@el("°+>sLB& (DqN nPw��Ŧ7D' p?=u-neIA'A!":y^0e d !2{8<n`ń<~H E&K11�S@iu�A 旛h�%vQ�Sb炇 q|31e!�aYV"ȶ9@�4n(Ř 1JY1p0AܿDI@Brʁ (` CEcPa1DY9(KjDJ|? WevYsET#DBK%VP/EMUD 1 GK(r9_ էT<<E1KD+0!�14MQ4B!dyT*3HJِ͝cP<G(_N؂ )2(3A^d2)}E)Bp~A*ũR{g.L >3埩9de _ӄ((_p�L!MH^n !)bE6b;C1\6o͇(D^`L0�(BȽ{qqȝTe)EQ5 e(U�0ZDU\U{iH D" BT*E,KQP(NJ$Y#*e{>*y�P=R (- qof| "M5 Et1)GEX̕Anf @^~NX6"G@ נDRE4s"e]eBU+GybES3%pjDӲա6;ŵuhJi 4(BaL"6ތ(RUi#JL!JrURe�POly^'QMpFAi (3qqeIf5QJRcLQ \^'mFc)( `B?8PvB4' �w? `a,(n+QYbh,_H֯�@,V vBWd0שP" $(<�|3�!FP2MQXeQѲ.L*(;WȻpyf8us}w\E9g}7R( RX�A Wl,(*IQ@=!@ -Μgf}/!"C{\{2MWe "2R`5iR VHn2 !XǺjHuٓP@J %D)4QZ B!f`aH8"��X(6$"E̬X㖛IH1@ˎ^$$RH1MTfL!"DD J"Yk18L$7N䐘3υx 8#XOu Ӟ,"&IWai�Q dBDra$Bc8Yo5Ȯ#4)MiJSҔ\af5Ey(&G1p! 8xȱ<psfؠTyl(z0U7D{?Z6qv?o?$`5dʴ: O͌Xy5" f8 &lL818pdMjZ�3Xd ~oO֩Ig69_W PyO`=t@3 Ҋ, sWf"qJ;l1<ٲr9|zq@d �>LJ #uNcwq�$|<`B99 xsp* 0!2d_n xY[$\fZ&D  E*x, 2!Zl6iP)uW oڔH9ZD@B5MP,wN*~%_S`1ư!"IH)eٚԘ4*Ƥ�u14xC@޳YUw54J'KCL (v+šf.xCƮ9l%(dP߰JҔ4)MiJS;d=F�FqE"2Ix(VO<Hû`}=(f~z-=l8[;^eL3m`^q7Cw ey8gC*$k{ Te]gR$FpqBzR3|pAb(FZ<a4od<W9gٟ S}.P첹}kۢk R9B A&Cp|tZȤHkeaHDD 6"%$+HEabByQD8@7$"7.u_N%j2^J�tqZ :K Q,kĈDލK0<Mz*'ɮu8S >7Y,�z鲐|6yW rCGzH<!N ÖۄiHϝ/hr;Gޙj;_3)5j#Mlcbi*%`iT+QQ{gu`/gԺ;70R#UN+OAJkx 4ȆR`}. 5\4Nh $5-(ܵWPW I�Tt?8ul$EBD *0ZRB"f<Mr\@ZЄvכ6j]"0޲qeF__Zg3"dd@\�>P\އ߇.<]BlfV}YcC=.v@eN2a&Vi[a"-XPiyrrQ>"zv zVm23ܝhPI5 :Jj\cF:JD BƘZ<RX"%"5nwЀ0rq |:QBOcq {Rm@ٵH2nf0n~ޟ&Mo3yg֪)|iΜWϪ֛6<e}436Ȁg{TWBbH!E@F!\ĵ tC4*\DcXZKKyG×7@ A7 sHx0frv~XѹׅG8?d1 r!Z+tw7^:Qփ5֗K b2(;?{8v>(_H*e1nA"}hwڡs #ɘufd��aNE= 0sT|CYcReY'gvJz@{̵0!)]OH,-  "4UZ@cRQq$i$ek5)"bqsImHlRu6$*5dOb$Rlk10z"EBVZ姊P*�f6qd$lLDVYklSc%:B$I4efx�1DH̹@ w6.nwqy_a+c|s{P"Rs`,[dtL2KD @�ZR*s`4I4)MiJSD4{  f0sl\rs82PJ�B^Zr9F pFHI&Z$8_Joc2_L]v8ubjC. . fo;.Q 5pԷ }1 �y}'_?aUٞ|:9<<HyC91UreXkDd i�1֥-'}|(3ͥgFyl߱OHڋnvG+8!6tme]Dp@IzpO]}}58$' շ$U`٤ :VKb{Ȑ(*jR")�5R@�$/M= P7[b~ IRY\Zb�콜�}I�4HkFZkk3GQ�I�@G¡i$#��٬I`W&<3J1X$U=aj. *޸_X-=qKq?C2}W�Ҕ4)MiJSϋN_x>[ЪD:g6K �[2ɫ;3��KP3�"=8U T,s^y!;3҆[83 F毮go`x)>d4DžNg1FG/ྌ#\o`f<W{{bD&,pd8�"&˚ I7�uU$4!Rc*7*1s`Qo"Rzr]aߞÓb-`_ Gp9 K(!8+)s0bmpi[~ԵrDjjimihgj|BT,F:ёED؀sclly sKJ뎀fɄA5@D�R ð:>O!XȎ$iM8Pd%I$(�ɤPGqGAjd>#j"�Q,B8'U8­{$n�� �IDATS= ,[Q~~<g~K[PB7rރ{#s<;>C|]2]sw?4)MiJSҔfYGIƘE:QJ6x ? U( QpEF!�(e9q@%^֠KV/|fx՛$= 䃨밋G䁝9dNg瞝9PL Ņ<eH݈ ;@ EtRr  @'upP(sAJD(.E�KL"B$wz"`R֪ F$eBbkI�01�Xclb "ƥa4c:?68(kYk*]XuI0{"E"td}Yy"Xss"18REpp݁>g#"E,=Onڰ �(4%E%)CB\/$(5QD.�Dr+l`R9 [q`ZkEIjpiN\(i*]* $y*ga1#24Npt 1FEZE�458qTU"jZIRVo�Ƙ(dQ!�c]wXf' 3B�q8j?y0TR!g@_'\B}+9=7#xH'.?G.:hMiJSҔ4hK 9^P)!a;+XEqδHN#F u o�pYuوw]f JjޡJc0]m X>˗n-3+ݵ5A`.S@WWXCU3|jV�"j1"`®ox04VF7np$AK:r%'앳ܴZk$Xk%5DqdZcBjSB"JiDHlUZƘ4I,#ap8}s D.Aye@@QY?c!LP;ȹsna2ʾtT/Irl s˷l%z_)e*RJ&J1Wgp )BD9Cl"DEXБFw%IoooKkK!˕JkK+XN� piHB$sZqqԙְe(Z qT�:򛚵JkETUұRZ"T*֤bJ[65.2ak[VURKʕVQZ-t&ؖB&l,8EF��"m%E�1ImM �jpA 9'Fodr7'SrTr @ >X i@r�s m(/Ҕ4)MipS u'ÚuHg$'6D`-bcсs2S&{# rZ9�XFY6x UJ<l·:CzP| ;@M�9˼uxr5~b A:2";./=E/3ksf˗'@2YG@la@R`уB.z<; QjIӸ{!t3sDaqB1VՒ$)#(8@K%tu F Q1,kJ#e0 # #_E1� .zò/%DB-'@4)R$Ab3'P � DOH#aVsJGQJ#(U $?|*fNJ+F/׬^CDT-ISc"y 1 ~GJ} EDh%(MJ52Zk]UP)MJҺ�5X$ĎCĖVklBPTXGqf6IZțC|�;2Pz@�?-On5*" fW{F8T l@O*�O9qp:Yz!p#3uFzr6[ڔ4)MiJS!Qoh`S r~֦bGek1DrELDj<bj-rR3k-!RZ)c x34TY1Q {c{RB@:+:� RsqiWGj4 yzZfXЋu):p`/]GnjLǺj23Z~"t5,s' @D9aA N'a,sҡmYKJ)q0[[V"0MxVΓӱ2ƤiX#�ՂK͐r!)Z}awKǑН@@-Z qo�=0Z(C>S= d~xo!Bj $q$i"S;ƊHgHIʈ"R̖*p H XDh-[6∄@o䱢$ ARժV+Q-!5k &Z�HDL@eyKtPeB ldĬ%(j&*Hk~溄hL̲u1e20iTVnmkakZ#rmB@Ԓ$Ma/P36�tPd'n)Dde!{CQ#mfQN{rQ<R G~�!>ycIU0l$P; (هuASҔ4)Mi)C��9{8JB]:�`?� ɲe˂̘Xc 2Zibs1�y"0"1e+VP9X<^HWݱi$3].iP eJjk*@[gMN  xP7y܏ 0.˷вO̎!vg  7zVZβa:Az#dr>a d,\"؄`@  5J9$ZfMkk\`62L~5Z" +|gxtP,2Idt'=F:Ƃ }J��+qy֚A `zҕI )lt3 3:tL'Eʡj?Ib_M%We&a)ܣ}\?h p\!�7-#PdIIR(("EJ l8OJ-ZF<@6T}@DLӔR�I֚(ժM7ΣZDZK@ˬd^)[`=Hk !Mj jEZZjjRcfZgusDGĨ@"Ϝ??y{Бϲ} ?pix E2"Zɶ?;\:A >tl]p̆[ߚ!wcCZ/7v7gv?>wnj=xFoi#<jdǠ5x-7Gu?OwH,]FhǮ}i|ٻ&"a^MiWG{c{z׊}K6XQ.3oE"NfF%ۀ0�,ʎRcg~ A29'_Q�}@[_D"r;)%rʤ"1l a�|q͘,in^�(iCN-vJk7|IuoBB1_)@Y 9|,JGo{0HGGe-w/@.nP)RZ;�!7i(ZK $ �J%D45Ƥ)3HKw$rEN)Ĉ@, 4 �fo?M(dtwsFC I!":J+RLJ!)Pr3QkIZ-M!)ToQa�0#(VZ'kM&IRluK)M>pXcyZR @!)E:-RPHBG" \0:3+>هYȤzIEJ$w(Z[[ŢB}N;Z%#4PiXGi%w12P-Uն֮!]Ʀk{jUDQHaאB~w&"N%e&rX/~r+žDIuߣYE=Sn\h@Ur{Jҕq#�\pߘ\j[ݔw_w5y@9gMRgs&�>wyG|f'}7&o#HVzP1˦ڬ'W/\|9TNu [m;joCW^y®oիOJ5)_#m#ʣ6kW,^d9+ Utη��ɱɇ sF{3Sb ѽ.J((RZ1&M%N8hA gL)嫜.ewPOs@ nB,o]'|&SV9r `=$iow+0(iW\dtz.$vIՑtEQVu#TQE'MSb*A\J"TRdNu8>wGQEqL^Пx$+Ms"`"R]HH)OT�k%,>2 (B��TDžB!.HM1F %)(2@d�c,s$iQGMHJ(ZGjh CAGZkcM-Қ4MҤVK$0"-A*(5IOH3TqP,8V[cIWX4K(MӑV=G*&ZDžX)J8Bҵ Bk$Mmb)Ҥ"Z"�)Wx^G c#p.JN¾('zO,;ɹOgdXIVL~6Fn'A"轌�~s&Szѱ̒2'G׃&_5Js~!b|M]=7.ܳ2/0ȿTɢN-1:[.e~s[Znn\Zy&~΁VK> q;|WOx);ǿ>fvտ߿y)7e3'oNuRw=y.yKn9c>b<UP?+.>YFɣvC�47۝4TW8kgn=b %z;[7"o6۾;s1mŖS_5.ڍǏd?}ǦW`lu㑃ޢ)A OE�?iNI(!\ɩR(�@D dOIP])!&y0N0Xg[U3Z8ՙ#rg]/LM:Qygt#8)NQΞ7ߗʱ@БS3:+vi2X%|HGX"�֖6@o,5#G7.n Z^0]I$֊CM"9b/&SZJC%̮r.P#-'qܷȑ`Eeȋ!, + Bkk8X(QNX@W)$".y(cccRkS�*B1V,[c  -<% '(ȝ �CX(b!4;ZM2@HG_,!hdB(E--B.JaS0\j 0O/G )"[t'0ְ`eTB"]MŸj+)w :Z] :֨PB\uBrΧBhpfV!*a2D~:RQ̎S^Q fT]� q48 $0@€ qCOYEx.gÆ\&ilkay3oy'G{g?=o)׬`�^䟾:cOF@`أ{=KW̿yu .?zazOλ#K_;+*5>8uo󋹯o>G|:l};~W]zmg/>hF{{dm?.zg^ݟ>35~|`׵3f‹O)>k?Qnu=|][pخ'}_D8%bu֜1~kZ5mBAM&1.o|i+|cֱ'\ľ}ޞdW.7Ǧ4/}-ѯo}1c `z8{|Y6:Syf`+t`B�Pߵ�H�Ƙ$Ijj �DQ\1p!R/S\P�[:xWp E" lȩA_ j%ܵĺF[Pd@cѼc<'ѹm<I2 IԹdQ/n>k46\j5"qDDJPPZKF:DӮ1ư@R9+H."B!.RX*J----R) J+DlkII$Irj&Imjd@@rT)FRb^@Ou^` %[/jrOOOooo$BR* q(Baˉ�:P@8[ZJq!f[hYZ5S1Nj4b-IΦ, !r[P ;RQG:RY�Sji"�8 IRk )* ␒$5�( ő$]�7IՑH(e,[2Хd޳\=tTa$1Ƙ$I-XB+E(X27�CQ)΂ $TG&8}@J|񼳧rYȻ^yg7"%:,]eX*;#}we}b7gyyc|{~M8#~{r0K^.;vtF/<L}}xӮQ;1cZ/Sk~wO|LĻ~{ŋNUO_~c`jK:[O0uY_oe ̓ qn{Ox}:+?-N _QKgWWWWW=twf!0Q>vr\៼{̢;ny'qg]OܞˮC ~]8|}zݭ8t!O=.<vߕqz� ^/?yssKN_�r^)�@zϜY?l|]tw3'}bĝПݘxvdH1*vOg*}i=~!Ȏ݇;Poq|g!nQIwϗX3}n-vtm/{ �Ƶyʸ!-qT>/ێ, |3o}C.l5:j/%[^v;&rVJؑCb-��}o?zѬ35-FN?��P+D,ͼ kݶ2l}Na�cwltW)[Gow:a-ǴZGm}*V_>Rc~馁vn^y{o3aH).tL:no cw �pcn]r[{I^-?fĸO\8` -s/*r_rI}7q~i=T]3jhЭ?=v:Ϣr{x<dǺ)YMi`7ig<�ؗk_d׵:rඓFbokԛKSR(R�� �IDATvOzIgx;a˞-N#�z¸\_�HrmwO5gamн^ua;NRx_�~/2elW)[GN?;`|ro*~Z;N(�rZ)@DI٥RZXV)>2ZGJi# S^FR$$'l--QI5QEI]"}f9mAj'Cž uDYR*o@w5!Bqwz0��cBDA>tH+@ X2gɄA+NF0Ϲ_ Rvg"{M#lֆ)ƺP"Qy,}G2K--BѮ&BTDZEqLDB (-i08zE)! B*IBZ! )uuT(;;KXG6MMȀ XT+e&86B0�D$+-Zc5"*Bh 33)D: e%1ijRk?km&ZR RZ[kYHpIN2ӔRBZ-M hjF R*5FZ, q&I1M*&QL*,0EIJG7"O\kl8A*8јT1eEq,B+Za%=FRIK":�P,jkHq^`Rd(e@HR{޻׾NtŚ>CQF"H#&MW$ [o*7!X< `1 �g_U=# �mjFmPIpHq>F˖G "wjlxn<1Ukag|{t!iL5O?| 8d3҇7K}mg}Vz`O4>ݳO<SB�M^xS�{_w6n_7o-Ilԓ/hC;5 ѬC>ogN?3V v%߽=l�ulݎ;n5||y=egz*iukM% mӎNݗ:��~Z[Go{}wכoycN=]u7&cxh_R;m'X�p+/>�%>jĴwxɚٳnl=ఝ;?{/ n{_[?>xw#u@}sm-\y~׾ug5��ԔS.pfg߶`g3ܷѹ%/?]_8sOW{{$0~GU=,xvΏٷ.gCxECt�^~眧7}q~sO/Z+gV��K*kO>pge1W\t=_ڇ/Z#>k|/O g9r"u:3WN9|/^}|^y)3N}tsn{_kth}~ֻ�.~;'ϝym\Ktǿ<�灩gG.oM֦>cn?K>r?[wdC[=e WƧWx ^Miʆ:V]6}'.Ŀ7eAaguгN ~|`7}}mqݻ[`�gw٪:0 v_ o%?X]+7=ڹ'}K^|c^?c朸.ۈ/vGdA~yh^{nz BZbNǙm_4轅(5SZPPŒxC8}L=]=Ju�~(师\c![KR@TwtfB1s%m3`8:6H� k .򓥮BȲ/?kv<_C 08I|B�0֤i*!5Fk])ZHő$!)}!iɢGS&ibnT+k֬T*j5IԤZ\q 7QP(JRǭ-Æ 9b䨑#>l!mm-B!\"qZ'iZT4lkժ1*GX, /-~QEVk ? ,G) (Q ZԄ/` i b8'- ~8%I"@>v%VHi$nO[&-{.Kq4P#Bk88(kezkS `f $1(c^+Td:BBIEbiEGk}^{߸ RZf4Hk^"ME9{ؒ=)({3`BhtZ ;xWoH%PI\ϫr>rl q 1V'P쨋G Qe%-`%YOg-pOmmk)mлv]T?q֙;ο脽6A'd<?Ԫֺ]Wlou[[j=5~-H=uӂZGG_Z*@7~2i׎83 VO~'m#Gkf}iu\spJ۟6r5g.˯YMW.9aWYwkZZ:79edw/`^_z֓چMNSOڧؽkW=ɐG+K60mvʠ孺q]ń~3oEO>,ifcP飇ʿP{tjX }am=enQ\K_k>`#>c&?vq1ĴNxNֲy?=8s6~;]YOyf p@{!{}mg[6@Ү+ۿ#yƞ/z-�� ;rm<nIפ76GWӏ?@Ywyygm:<w{KZk[ �6zҸ#6<m`akG['!7ߎo z3[uy)~=�c7>P|{V#o6'祛o~Tʵ~I>`-$JfO;g?|˻)?}{n=҈}ϸ]V=c>:p)M`YC6؍.CV�n]|=+EW\<g̉睱ϔq;:pϩc͞slqgﴩke?~?G޹jG>rQaƏdgn'KuԤ c7|O5 *׌�$#`ρUBQ/@#|^C*�'埵Z)AY�$ YC,}fzWN1殩SE&🸋cG#!} 4(<}3x-s%?~{}3#[ E2qN"B,5CRM>~YDXÄj\.#aV]zuZ0 V̘|kX,LUBҨ4&RhJB� IJ9VڤZ+rR&jRʕrooO?&@PHsؐ!]Qjie bTBZijԤ4&JR{zZV%$MZq\ 5fI$DbG ijT"]\r,Eٝ(B8Dxl?acBRfY\``P<Xev�VZ18O^0bKbr,V$^CR3v�`FX B Qj$MZWb�[T-m*jZ쌢3�Sɪ )ۧD;,E1 __~ 0� �0 ǃ�(D0C6&v\(Qz8~[/t Mc >},{K'O+��Q_o`V郶uQ(t6jxCsΝ;ˎ߸4b^qڌt6ѹsΝ{w> }=}J!#/.Z:olnQW7VQ{yݓj/tVMqRcׅ[w._h߸vw$s p]*1Fc:+{p>SkO3SO]򼕕Y6j j^Y'.{u |_.uWw鞧\DC_JsƮ19s>9Gsҥk\ubX,n+2��wC\l27a�j(AJo؃7o+]mDc'u\>suwf/q7/,hbX,v}ڪ=u#I徲&m ]-GjO\ܹs}5_DӾpf;w}+ؾ䉎&祋]HaU$]s=ۻ۾ퟛR,?wgzJk'lGڴ/:r /~׻<xo?}c?=;y-ذؔSfޠA#7۴W;b$=}m'l:w$t1K/ 'IK);xc>pVvÛϤ_7ٽ�P:/MhR+EA+P0ӶH)"u*w ��){ Yd5a00D;r9?<N4MS|!?\ 2CWKx6!!gto<zd/֓APS ͕Fp>X;DxFgxvuTPX I/.2$|!dR*3cɑ̤iVCD_.'8AR.h MkƲE kVVkUB,ĥ]`ro֚$I(˕JRV*Jo_oOoo__ZMӴZ&ȳi)0CQuwwwuuJƎ=zذa]]]Æ ommkkimiii)J-bT45&I˕I(ZK-QXD#G"㒀8a&Hb\S)##-k/ (6 J@dae/@Dԟ('D�RupFAIZ\GԻ]2U9%`T* \,jI\.3vE}ǂ_~~CtwYG,~�=PK�d9K 谰Bua❲@&Ĥ.ID ȸ|a/p;FH)ϖ[�nQF`ÄWȫN]Ҕ-'7U {j3jꖛ nI4_ǧz"D{i ΃kӧO>m-O?L �`^x|^u{F޿zuOvy jfӦO>}ڔqOO 7wA۴-ǫ+|彇2^Caq[nޟ<כvo~i/^r׷>``]W'2|$13u/eA9 o| wtBkw;-&o?l#tmvsoc`7\~C6<_߂^+7bO-OMAO?E^yCG�4|^876cEKJAZ~1m#F;ꪕjSlE 0=n4/|~acTYps5 (J?g_K=b�>x{?'ZJkΪHF(L=7xV7+IӦO>} q3*Ag_¹w>6bʂE.7EhK8z\}HCqӬouMG޻hĨrqwsuycƴ-`:gp7n3w;RG}S_sWyQc>gٲzlJSzcE ZfaF /מv%7gsw[G3n4/$+Z3 6}uW]uS;yk[/{asPq۟sv ]%//vQlJ4B!jK^ג9k~Ш %w x+,Ԥs�mRO�3M9.uckScĺsVV*r9D:A�Um|^5TAp=�>xwu}/ݓ K>VN\{_]8!"C.D| @>3!Ye*}z0ֺ\k6e 5k*EQ---CaZ%5BX,VH8R1԰l6MjiZ6 иGi"pZB#�Z[KJVU+JOo_Ooo_o__o_W.kR<21�@X,ZJ]]]흝]ݝ]C Rlm)qA &5l@rD. RH ](\r6DQ+Ii�'}>c(` +Qi@E1#"ec#rGE($ֹHejf z 2)\�hbo}"�ٯq!(LlsTbJ&%пR5<x߽?.ONLkR튈ZXooGX$pw B,[7^F؟Fe!f%Aג_R;*<�`9_+eدmID1p%y|/+JRM,~{W/ytj#y+~s=+w7:yu4c\_pϋ -ؙUC[W}Ox.p #^\w6cw+8e3>x[5T;e @@H 20&yHaI ( Q Q0 !2<@H $po9gwwZUU]Ϲ7#`ݻOַu%g_nyuxY~mq%Kŏ:}vuHoSum >vGyK?znur'gs'?M;�<v7%p-:/p>vc|g裧�|+̷p+&g}&G{k U?8On啥[_ʏ=nyw.{zߥ/|xntۣWܥ�tc+_wf~}V�=ﳟŻfAuڹ[a|ѿ_uwo 'n9>?\z__7n~S{E9K.w/P~cOzlJm:�ko's;�� �IDATG9n~-~轟 ~ r_>K/W1g׿|*M/$}̳/bUwqCWVw <詏Կ}+x ޾Ч<Fe_p�= >oħ'W=M?K/9`z<~3K.El)[x VGgs}yG߈ʄC<Wλxƫ_~x/nk6=vky.e>ev~g|/|csvIR^{�{S?9{NzM?/>m>Ǟ/!'<9c5O㧾O}O~>ۦ^q1)\9B BI b2Ȥd7]`΅5) Ҳ^//,,//O'@q1s>NJ7[FcTri W+ȾrD} )i}7�rdOTL%[;s}ɋ37nLEV欝�ٱ7~ҪAHbj]I^ Pz52۰(f ogl֭_B1vE$J>4奥-+[mݺc;ݶuʶ[w}XZZFB&GdڜI?0gC$BށgQTi: }MQUg3"r}{uu׮]V`}}}u]{19pф :Dλio߾Ͼp۷�mC%4J�ZVXK/4MpM֙1hf(郎Ϗb#cB/M{}4 3 0z @}ͥ�^g1DIpPA6ANnHA!j m}OLkB yC|[|cn2];o P�Pvr{bKNR1Pk^n`utLRfgJZ )KZ�nN:序})Zon~9;?b#KV~v+Oz=bO�?'no¹0473-;6'}ڡW+f0Osv]ym=<nnԷ >ׯ=Wt|kGV2Kϟy旾7rSns='?Y/2�w3?Nݼ䲯U屏y=i-r7>?w|zer=sv^</\zO�u:C�[Ї]gq 9dn{sǾ̊AG}Uw=C1ow㫏[W[mǡ+=uϿ�X)O?Joq�\r֟=n߶}8~<v~}Î~7y~1t;>y CW>ꠃ:}pcnod�og?t'>H�?qw9xnw~իw M<|˛/rrƩGfqOu{);x/~nwʧ?5舛](|/|o}Mڗ>x+Ox ;{<̣^uu#grħym̻_y-N��;<a͏?r</�xIw:N{]nsS~xY:kw0שxۍO~KÄ~x{G2\mnt^ݡ7;W߶w8l6;9aw8_~}}/R׻m{詯}%9[ܳ+XƇ�|n{Mgί��=I]O6ttN?xƽnu]~=o}AzM,��4{2T Si:>dS 7/bK Y. Uq),{/" IjXʓ1\UwӅro n[BZAnOԹtncan{z u0l4엗U"yvOQU!`Po,Kytikyvs5)`55b b$A"j;��2@>GĠƯ!"BV|$"d߶-3V�(15hꦈOD<$@ ڇ!Bʗz^J% !&=iN2ܦ>CM$FT']@QV0v*�Cvw=S O&=c Ҷm4b"A&U9D$zTgbYpkA@ *h()�CB"1w]'1rEA%25J@@B$¦iQע%ι*ׅ KA7žB=-7.x(JtWWWm۱};vܵk!ݻ !6Ω \Re}ieG{֯ <GD^tB,үSʵ�DK(U G/Ц^nÈV_ $4FUA6~IO{#'Z}lw/a3 Vw>ǜwy|Wyܩϓ~/iWgp{vyoow-S~9h~`wou*6spz۝۳#/-MAbs~!ϳ ŀM SHYs&gT6�KP/!+꒙6d7, K1)lc{pYOeKr�h۶@yꅤZ\[knl+uqos0A}ڽ\OLbFvrj0?E-7{KJ1*RXA0UǜQ~0ydU@db�6;sVSt|b>t�JY>Ƙn3!g^@amkk6x+CO̊C}hfL(RTQ9n`bjXT^~6}/uA4"H?iԒ{Lk ,cb}A/^UU (6�A!236l6YLM_UVn\�bhne,-M8k۾EűZ}߷]k%*MG�S@h(*IUqeجc%DrL "TC߫jc:'%BVLAc&L$ (sT<Uihx2+Fe4ؑ�P 3fL�2V`T5ޱ[_MS�`Hصҭr_[]xjuJ;D`:9Chz*3_rɧ`bKjޜZD tm $ƄG?  I@7& 9A/렠G4eh#k_jN9q<P'O4G=w-'wT~.:9`O_s ?Z` [j9JT�"g@ C(SP%TH!jwWvjb޻ &B+Se]oE ӅLW4r"ٺeb$9 FQ8U�>( rCKtݍ[?}@UACe1 dE@�6@Ui,-"ꊶHfȆP]r hx%' " ,E{W2[쁚H$+8$G읛\D!; ̤Qc.]ױ禙1WJwo׻۶mkG?c|иSF6&{Y7#OG8o xum1GqH0uOeX4Ĩ1=o۶]4: Ļ@֜qųY]Ɠ#c;v�KlCjl&*eض4gK,#!:1Fv<49寨!{۶%$&4�8Br!1Bh79ܦ;1FӍD@觍J2i|Q*PBe/&`E} ϺkC^A風ky�@ Yt} V!$Ћ:fP16MӶ-6KU:Fjv)zz*  f!, N>4Z9`" D"6sE,E� —^kO!jJ@X#DqLsA2"(( jzXejћEDQAMFZd.!/la ۻ-]IH<?~Ƶma|㊽yw俪9 [~y[~pw#nF=W>'vvL,@Q[q" iZR@"Ia042 r!Ѫh")*s8B{o5�LZYE(*/BZ}ݢ 9g.Y ^;5m@ǴM۾PS*`+705lֹW: Ǖ\ ;!Fٳ�Jqf0J^iu*f=q@}  Qs U%N!�m1"}o!#3j(�8Nda,"#��l``VsΛ轤P0:D �swK2@Q۶-s��b UQ2sN %P#SPާDD"�"*$ !@k[d: W""NվkkAsk7d9Gl% h"DVJbX() L< sh& ;g a:6 }/]v>Ebj1Ҋe DH<�0Sƹ^TSLQ QEUBBLd#0BmhC 9 o Kf$'+! {k sЅIIjU6B3Μ��0ڞX]` SBzP$ʊ+P @!=2TE@�a= [-la?KtkN�p게$ 6aRXmYcԂJI:Ux23=B"1ICDlH~tGU˄C{3;ۛSв6lDpLj|MS\Iʼn(;|^!7�998EJ9O@܃g�HC�H%u�%+C yff #bm۵,w~ ! (L}Lc[n T5m<M8Izxy͹a!J+IgJP*TL/9?xթiG�m۶}Db% о:q^j bEC)fiD1Fb�D}iC2lz"֥=6r~Ð\ЛbI` $h2֟1FSŸ4h'DȄ� M* ;&UڮWULji<@x4s^�0{gqq$TM쟤8}+wQ`LuS={.�E4`[w_pJ"-%s [V 8C *HkA7y/m0͝U$�wK/XN!nY _2 6SL�P٤ [-la?WV|KAC >(py*9,Ɩ[<_Ts.,!h�.��9U]][ !Xv.l[-Cx֎yYz*\q_](Z:w8s: WEǹl\ UJ;�Wc}<4/G5, '0HKt "Ȁlc"js"1A�Y{D1&Y"X$躮i9 XQ+;fiy^pż!gTMJqپ„09t<C&ǎ[[=1#�hBCɱCT9V!Qm"UDH߇ITE9n|4FD2 :MKDb[%X…1)|xT"lSNA�}:M(ѱL&C=An2it}/mK'"`}o|a "y43M�H rRe2!syX !)@A^(W0eP hs'!=ݤwE_-C(dXuN4X!:<% #kLs/ҴgꍘΡ]hȤ὘;ѲSӈבO &$D Qr,la [++qX) "Zi**hTORT�1/IPRB-d Ts�DPq؃}psm�Bdݶ9؂�Zfl8^ .E{BqG$ [+/�l(P\`")Fw9}o˕SCS!RSThXo\RФ !4Ơ﻾;!d�`E(-9iƒ>eYW`VϪGc ($ ,U!Qq`Q C(�QDq2 !Bv4l6b5X5(�h7(QXbhێwfA#d 0{&oY\x� Hl�TD7 *֑V5(9:%F4޹U5hJ~z"t LHV p6!y�}F_^Y)5\D5ΩgyiI!V`HU]߳$ "@S�Dy?cjEP%D FU`!YR,�ρRGjR5QCcA5E5 Qm@T2 [p .B 6ǣ*eB.eG-la [6oI{<Y7]1YdPAdW>Lfcd]yДL+ŗ$>Dcӫmq{�hBk7P06oX}旰U2$lYˎ7inDsMTTɸUX JǦ--W. 1$lhRa�BE Me@E4-f<D^ }g$h.hӀ2锋,J!y�� g1z(űJ�s8VRR�=lcHXEU~�@Dsb$ "҅U12BlDL<G`fv i "6t:1e޽4sKi†f볥 e QxpYt4vڢ *i%5AM(2FH奥t'¦8f뫫-Z*RN;_rć'7۪9@.j ;<cPA!&CG)|#j ! )$@$fP5U1�s!"`( 錠 1 cK`m5D@2Qs6zj 2 QD0*G(�TDy7>\-la 44+,aˊ� ya^M"� /DQ,xWuQ] ,SuZkN1ڇeJFH`}u} Qi+hjP{ڃ#7^k@ !W_ӪZHT9W{V)ѴBVr5`J�� �IDAT#`a[-)rÆ+gYA3jfH1VCͤ sKDH� 1҆M|DTv'`b&JTczsfuf8s "VPKL??(@9[AL)۾iC|"^|@C1y* HJj)@t\Ի @.[:&$R�QYZ4$b=)a% {#�Q+mU^63sદڈ2%Hd� 1t#s.Y䂙m}sLj+D$� $slJ~AeT<MhH^z2 $"S,$hJ6pc"bR-c%;mBg@EbƔ0߂ke&PàeU]P-T+4B!a JI"J$~J/;ɐ$]-]` [-RpP�c�d%C ]3iLkPEu4jfW>E,W d6lgfN& h8dE%��]DԇBoU `T_&A,13w}O9ƈV/EeF!`f n4ڏOޤ#aاpWɷr;9$ 3%ujR74.mgf (Ǫ*}sM{bqxIDB&MQng~@(u=3Hg '&("$J !Ԕv1*b"ZZK&D�|`E f҇ޱsn&yܵS@n2N/rt:ދ)b" }ga,$?CC&X<%[ET/{LI۵c4uW\_Y^n&ȅT,b%sav69R31$"J4̈T$�33GպE`]R|��Ut"cC09OܶmJ!'Z;dP�ZZ62r`8VM9 `)/ oג5hK\k3�,$P6QP%ˡ@�6T-�OX !uVX` [-03!R.9f�0I *11 @( �1= fmSʮ# BuBE@ǎ;>];i&8kg-kk-!"q^>*B4/λk$Qr _fNl9Ͽ,f.TwFV4btt_a҉[О[prDHőB݀1/zuDՓ(QTQD!*%*jb�G�!%3 -vQSdDr=g$74JDѨBVp_ Lmh=[;EE@5rc AQɛwtGm[ F!%9W�0ZU>B "f")h{][eH#1/ڙ֗A,Dj eaO4?9ˌ0MEb8RG :0^ <o Mgtd)*KKӉk)^ u] �1>$uL{rsXb��3}`"d6޲66c؊"B&b*JhJ%gBo @iD�ӧԊSϾ,/ECJ#;FtP(#D#3[yLkI[eI1:#&4LH d"cdb5<AH0h%A*35aח0{٪m1r3i?]?i"Y6Z0<8P, B�Lդ-%5i2ɱοVt(}ٌ!&1��lmh;sn2*H28"fu^VrhٴZ5g~.@"(E\$볥48^}{ 6u|Av $kaLcέP"$ 3&o$eR01h5 "*PV/SܡO9@mȕ,u~He0?D9ҵcV4s9Oi[�@�"xeZ ?%tЇ7vmkg=Dm%9BD�Q*HaX ( 0#>iVl*Q8|}"S6(&􈖑! Y.[؞9gqiy#9ׄYE"n,/M˄T,@ʓw5 "dR@sM^9쏙) ,0f2+CN~^QT~ZMr_`A! I4TrU4)mX, pL%U@W2=~*Yn'o=ln9 ڽۿ|C${q�Ÿ㜣5jIeD̈́O3ʲvhuXZ1)tb-[LBdv](1Bm}<5l&~UnGuU e1 6Bq}(zW$wlg2d3_KDC#bL̇z܉*R koDs t+BRRH2fHΜi:$!KU0j?��bH%E0N2Av튮zr![X=턀/iDb:`lye@`RQE`@e#x#Fp%[$镢3bh,#b-#UC.eԧ7kf)GKy(`]*mWȨ_׶L9Jo�A|;JeGUgO"1@DLyOQX1L'K�lA1bʣ%vm}H41BqSvͰr -z_~`a& %!cՂpJc ^@گ!n^@1 Ӫ A$*$% EvOﮅ-la [O3>׾h-�e Z[taŃ%8�XХe:d2Y_[۝QLE"{gr�XUM=>_>~`BSG#@76o,G jlsXq"$% `|hhv*k?nju{ TTJL/8d^(fp^cB *H*&j@TS:y#gDǕÌE/w8x cA\Gf/@,ذ@$XSq0wJ{Q%$ ` , A-ieZc:D,+Kf-˥GTilOBmEs49E9j=HW>!(Ht{g!`*!"2.%U4/ `f!`䘙,B? ]#Dd,T?H `ʯe[Dwq#SN2�}Wd;g BH㟘O9b`ep|e#b<Vkd2<,L4*� bԌ-la [O+GذrTEFa #-aja8$9՚Sv1FXEV9N'""1ҔQElwoĕuI]7MCDhU"ƪ­{pk){0H�xm~v1_ۥ-6[|B~Z1YPkT+vMŘ˶^e (Vn ED$6vKR/ :t'V7~s[|#fFrˮXx)DQxWMtiD:|>Ӑƛ;#iDU B ;' p(RBQ#4aZ5c}UѤX3;̌8 ^2p(!D9A Q%{9$HJ�l "S@55($F= /){>�̑$Ʉ?c<v2neYP�K )ODF{ZeY {ʗi~K#ڟ\P3QTyGPi(CeL$WY  [-la?:r1�"6͈EmKPpQIU-$FIDd]͸`aZG]ODMɹ%1'`QuK눥Z2zeU.6= g3OY")?ϰ\C^| Pm6#WФԸb7�|w! = \I lwHLkGj)9X5ۼܞ[n0R*|p\=S9#g[?z#Ɯ DD1U*)R�FOHR��ռ(}� ")U`BB !a ܌3C6�Ш@Qz4I? !-?F%"&vy,OD}[?دi!j9E/"�H@DDB\_[q5DY)?KeY_#b,.PFD&Ʉ"ZHHiBO:2My %HE%ty55͔%:Hz.kVD]f<CA(]L�/ҽ,la [<ZoX 8�ޛD}U,�P)!0G,e(R0rV@5qh"SK_!h=;'j*51sH #ڳfڦkZJ 7m %uOnsݷ528l\`oԎ8N9'u :9$J�dnjP?"+a "J #yxZcr=΁r-07f��RUU!~ĎrZ`'"i9[؜iU 9E<"ysYz �H3*r`ƌҟT'@$ K[WX*!`%AU@")a^UOVͱzksc"쬸�cF&l1R6& Q `&�Z" �z+᭴$(BP%QѰ14"@z"è.c&, 8|)9M; >K086Va:yH~$f<}ƨ&9˒W:;MܡZJQW;d"%, O!-la [`-4t' rb9h"?KǼD 5gJji[vpzy_&4xsm2H4�,[\cF^~suk'>vƫiťo,{椏,~^{F݀Oq ǔZcΑ)we gEĄ2$}.Bϴ|BDPiRTOH)FFQٕuq`o*<a'ҟF#ʉ恺Q ler Z#͡5% AS=>DDF JJHe$Y\_QI*"uI1AH}" 8zfy4!; cXU6N<5f8Y^Z.}C�9ػ뺶c0"1#)dwz>$h2z4oCюL?_53NTF}HvmJ uWV^K~PfTF`fY36gb"]3,}I Z52{2k %Qsa [-DG++1g aR+)EtsR̼TԈVU, mA'IB';UUc@&ˤ-Ϸ3!_뎨km1F+ܐ$~x[̮��{qk_!;3ٙTTsY2^QB� B4p[Ә #*tv~Ha.)ASL'7mtJ QQb;B�>4'Bקk١J*JDαF&aw @i{Q{m[1:`#*}1Ƥzh}V!qː$p=ܯ4m�MO.}1;#Fsl3O�QD,Y�#DݔH0zJ}٣7Fhd@UDT@H2H6(Q@ 19{6DncdfQU8M],vN'^]ӌj%Qd" !1X( Hʙ+R`37U!;cQy.\u~qdWGH ԋ-XF`4JӬp&!@yȭì;st4Y'\/}0Q�h'D$$[-la 1EB5#wQBrmׁ) 9g(#g W[ҲO&l}օͥt/--޽9g$D !0)dWW :73_M|pMϳJIB"ʷ߱{3jf#5b-'<<͌D,dPI}�%U`BHc>-Y4L/#*.5 @tLzE$Aomu+Ƞ`Hr9mQW~T۪}%`iUn\~Xu5yc6m*P,bf݈V^j2�(DL 'FW2yȔ"%wPDUƋTb"ĮIADYΐf 1�":<z ��1dET1Jh(Q,#>1VfD " )jc4TʓMg47TD%#x,\嬇+"1dգO,Xq2AȄ.!9ˆ“V \ΐ RM9- 6φ]]ku+7Gx.أo<Z+p6mzͮ"'da?7X=~i;/-?Ȯ x/^B+.r/]Z~>;Ke{:~774 �)h!1A "J mZ9T+`f&2Ǝ8bƲ bl}%sn2Z!2QtiR1'k#Im!!\A}V9\ru_q?vs6%l<%Iq}$h+jggnBPg]))*qP*BK[TAjh4sQ:+fk�9PaLV=�6͔mzah?c"*h)lܔxգc(F`7ٳԇذI_*ޘa=8[eM~WU[ z!L3!1 :nęaoy&K&fL`e #F%pOfQ.ƶl][]߽{uu}>[[fmgf]vmۅޒ!5Om۶m߱cٱc۷gm۷o߾m֭˓Ʉ5 =@� QlyF*-Ӧ01O�(ڔ54Þfjezj嫂P LI@6Å0JFBuucr RE {MZOKx?eחw?oxk~?<W&Egݎ|ևv`ϵBۆkM-~:5鏟oag;?Ǽzґw߽nOaїQ [φgGפu~>f>|fKKI{gq|aT^{Vy9뼛L'9v7f2is*u];kDtii9/}wmu] q4[lYٲeiy7Ll-\lo5=kodWmWF]I 6 J;z* ꑕx|\ �� �IDAT? >BaH\* bR+焑_ّ0)ݵuKR#̌UGcUDI31VIoXc7,;yqd٭ bdZՁQe՟kz�=9BVt07 aZ)ʃTtwFcn�$9HjA04:Cd;ǎl\j܋8vEDYڮ]mVVw޵G?;wڵkוgڮf*:g<]Z.Mljw;ؕK ٬39WC!t]׶(:0~m1cc 68Zʇ-mQQETnrbB=–6() ( 8JխmK]] ~5O.M|?Lw}Vn| ++~-:t}}dˁW%rɿhyy[K>|xxߞt|r kw=ݶ>_^-[yG4~ݞg۹wynv}z޹sA񒏾oկow}9xͻn o~Gxe2簻>~Z)zw:>Ctov'><>ҁoV]{c˿|s+>|Gooz|Od}ö~\ܔK'-n+/S:}ipgh>.nψ>iO{mV:=Y<2}PK Ffn|3L;OΓ3Dтe}}@t;SUw_zܖ-[sClgU/!)Ho^O^>_^΅˖k>P$/o=h/s!*'BJ+g0 ;~@}c�BdFfŻV٬[B}hNC ]I:v#vYh4L'c %oCE�*1"&%˺+Zp�j�e4^(_ZLP "!BDC2G1+{iSi"@Fai>hV{h81Wnqc?i4S!Fir #FERB`B�mg}ֶklWZ]ݽ{mmZ;[][[[_}U5N\ˏ4I?i$ 6+E3<Պ_v,{_#5|kPr/@q:GzGDUcL8䂬%Q(S6a+̿/?孟ڗŇ|{Fs_�?v_/>o'A/E}u{s\g<M)k;N~.^~ &-}e'O@+O_O>ӣڷ>ʓzgcǞv̮8/y~[k~;=m{xg^vs>wן}_y=t!w>ŧ}}/=o:7O~CN:ny؄w˟u}>69]^p=Q']k%ĮbneIog}˿n;~w8@}ewYU?9-R{C%vE[4ch^c-F (jA"Rޖ}Sfǜ;ww$oyf晹}j:O;D%'k_ǃ_@PH.Uڷۥo��pΥ߬PbYV4u=PFB�p\1***‘,OgT2Jeҙt*-,4e)'ZSs�bjR5˺)~':zU!lGBdQ{cS:h ~ y%�|N9w]@RL2dD,\ו.u>uc!?:K~( !X,�!i3 4M?" `Xj`FԸ)YՍyvf-v&q #5{5 .*dOjAy>>b9![7(1 Ӵ̰e=/yy\2!IJP($I<8p@` �Q䴦sE67cLD8s]vq<΄d2Li۶qຮyF`+fYV($SpwzVB!Rț>eC-4xK)8Y zG a|__1 %D+W'碈l팰Qw=F :K'WXY_r=wמ|^�kszɽKG<6yd5 .H_y~A?>i/} Ys͓ǣ|/�KVzDrxYqîk ЌWTTTTT6o;RMa=R�l^™ya|aiٿ<Qs՗O6>>zt />tU{qn{TB]:w?C9g/8j4w`Dsݛϸ>;Ѽx �l|bc��g~Y?m7㦎#mNroFwnpYsk^| .a3\Y{3\38vىOS6^j߷<dź >9h-c/C>?{E+|� vzŸ5]y]9yhGo;>~LX(s36Vl{}~'<ƀ@mӣ[�{YÚ?ݫ1겷�#Sf�G.?G"m׽�[9U1X=O֟>:?|&YWpaCEBe}B;h|Be.~']!QBS�Dۗ6~`HA<0*οjPlңubL{/o8~~D>g=H(ƒWÕNά;Ylݒ'<kS_*ϳ<[�Yt#Iz,QQr#oX� 8u=2zǬk)w•dNV[t:WA4<s\Y��Z��+v=efx^f9�97O]-2C&]vS%?W60�;^+"1˞Y^th91)Lr+e �#"r]unҪy4aYLOL%mTF�ZV8EX,^2 Db׮buԪ>rQ\᩺yz`~CŢ/ yLD>lփMmڍ¦7DG8g<4`zCɊ763R-G#qD*b(PY PUɛ^6atׁ$Ry+|94 -I %j҅Y ݒ_Iإ˿t=\f*zBf{[2B^:HmԹzrSy_~x QiG�Ǚ@(5aXĤ p&,܈r^<\ױm8 u H\1yŜc{I s\:q.Ɓ <&=.sv<۠K~$1q\\ٚ2814 B;b 嗁Yg߃#A$@0R0(DP"�*/n<Y ɺnn\Y�ȄAE�t9W2(b_r/f~n Uu`KT?D-Ukj֜' !|ɚQ29v[շ`#nc>2:07y XnWan ��:xЊy��qnE~ ֯n;zL5vP%$}n{Ɩ/IY?9n2ڧQ_M�@c#ƨ ~ǹv˴aol[Ƴ2sۆ.Fjy�['}ם'D}c }Sg=4OO~3ms~;j/x?Of!U_p�v �߼ۦ#w:x.QˬĎz@[lKv/=ʷ%=h^nof{}d· � :e W~4Էn]͓z��:t;zN!}z휛w\@$-Z8U7,z쐕ݳx{[j]K]UqʟSp86gux'!zc&oK;{Y j=~MʦM_}c��`x7t:܉!~vg[o|[C<:wŷ9Z,I势-Y䍛'(\uGi/iY7*֐Žݗ> Mk?~ܑ7.|w}ͷp_w+`qʅ �|GKrƌeX}3X ?lmXp,N֦1#?4?ly9t|ճ/~<β&M/D%u`um_i~j?OWޔ=ъ+_;ߘ~/_(Іo|rcZM>hsRMحOV3�oG5-tΟc j^xyygVlڴũO7xՒO\ri�\Ţ[v?}ZÏ1Y޲&T.;{tʁdB|:QV2TAXRWL&J"c5J"b,#iʪy^:</SJ]M&MMMtJF=�sԷ]-]NEQx[G o.Ȗv%(f׿UBٟsy뺎xE%;֖㽫ZVx˖-MMMp�cx,ʭ�u]u9RiVu2ɝ" qΠiBP(T8h |HҟRCts9sx|в,AUu~#(ƘlbH1ݟ "md2̰ٚi 3D!J .T 6 Q$bf8A 4Mc2"E%ԲLN)sA7k7=9Kr>LԤ.mr+ D�$$# dy"Ɇ`YݐjO<[AH\yVd!~_?BrS\uc Rm<_}'bڑMW=ICw%caFd߮hmK1`,u�oك׼4_OȁwՃCafY5%!c]�=W잳0Ws: '>GW]2VbF~ԝLfX'/zx>)$n˷K7t=ܸv,q7}#'-K 2|Tx[IۀxK:QWn|SM 7guI-m̓첋RwWq};S5C'0nX=NS/?A*A/&-Y͍v<: N9ܙo,=N<zHV-^㽆O6R+kyGT;�FV5 NbvZ{oW_ 敿~>⻯4ψG3{6SNB Z/?ԣ᝙$G4‰cŗk[IE $"o=7z-7L{Kql��@U{T{yGtu+5w=)\pMjkhܠv;ka;�H׀ o*e_mJ[]jMhz5{΍k<|GD>|6�7m? 3_}]gQ5d/e>7_s^}v;&cGwC.;e#~ \zP鳇kWlcJ0Чwč{3 z:h}oWl,׽5<3}4d9<gn-sl~}2 ){kl*{1pON UٷzЄ i]&Яw]}gW?MOPIVNAu=Ji8Ycy!.kSBcs\18T;=SŶmJi(p<@ *oH4My}CC{]+y$q]ɲ�:*XG0#pQ~E!(=9Њ~@P[6 o.ڵ�/s/慾oVl6GAgFˤґP�d6!@% ۶3 cK.={^ճ{Ux4lKSV۶h<(%_b(p΄H@�g3LjZ�4</L?۶L3:bѨA纮턭I q !x\V &mj`A}9A0Az_A �AbP95<DUœ9>E.,Gf+ɭ)+(˥L(3p<5@5/zB =PD .%4^5mޒp$R=&a�B(QR11p8⺌s`Kssƶ 2Le A#G%!9Bθ"� Eqt|V ('�O:!0OyjQ@ 9 äԠHsar88gRU!UERѓG:*RͬR4d}QAʉ. j{2#rjndرW׮ٳ&|3_td�0d{D%!^Q$ߟ/^yiʩ'>`ŋL{ilKY%^Cxэtث\xŋD mID!5ukխz ?yqǮ<=//y]U+n~mɶi^1cA:h 5{>1퀇~rtho9̬.۰z\mץ뗶~6'{ؼ̲ׯV7;d ޶E=ho<񲧿j`s<3s۳R'C?h^*;jVWp81A*!J�`<tmPӯ׽<c)?DtΏo<Vcj[s*Iz͘2Swllzp8hؖ7<NjMTo۲=ڧo>ŋ/^ W>)P{y| Y7۔2Ь>hs|S[6m|xh8G_>mnhdm[o;Y|Ó\Y߮ݴiџɿ=XLW{F̽F`cJ!]Zǐe[7;b^=j]G]#!!V kJoLud/ޝݰc uݏbQG WdIɔ��L% #wkB6C ! JeyН$?\V J ~gA�� �IDAT.H[+l+ XD 2(TzTȏW e!WS'_P"ky c(nt*АP-UY4+edx<F=ss.MoԺis^vUU=id2@@_~(JRC׳#KR)TJF8qd2)s`~ }Պ.!4ejm)t-"1qC~H>`\g(I\j|a(CO} (uّ-%x�.P8@bVY5Iа3ځ}{Yճwum۶A(5mNRE[eY뺄�,XV(d2ux&cr% .:Е#rt'}RlfQJTVSe;ԋJt!g΅Af"DHTL!ێDHxpR96~ =#tB+ם7P?>z@_oaK/#F)>W_LjOjٟhEF5jnacò{��l?n8f b?mYwgA{9jԨQ#ה5ˆ%K%J~xu|]8sN=C(wҹQb[ӷv҉C~܊ ] �Sz9=y]xhݖ v뚛BaVUVF/~_&C7f|]\rkw?e?o.];־~zoy`?s3h?I � U߮~gsgN(v?x1#vҟ_?"! @A{k] f_x)ٸ!rz^U9}-�0j/Y‰Ntz!r,n=^aϢ&߾ ٮ/Gګgjͪ-V#G5jԈ~ 9񶙋.~do8YNjq:?7nzU9)eN$=r {vئtvd%Vn#N֦O>ugx?Һ׉3k۶m|wKTdbfӎ|.ѲiS{ݿW;ic+꿾{|aXjIf֭@c쉓{{X;bzy0E)?@}ۼzvV%H)5~9iK<�*g-6뢀鶶L&#Mҹ�0a*I `0 ݭ3WQٮ# @G-= ^/v~N|QYJ8%wDwztZZu ϏPꭨ~7 b$SL{K;  biIDDɶ7nܴi?<y5┩p!2u]7NR)q\ɤәTڵ;LQU&WA,ˠ eF@d\f̏hP~4q'c۶AkC#dbEK 27B \{D&R$DIQ8]"Uo<xP!KK[۶oߞJbm~eDߚ~U]#T*ڎHd@(f=km6 1F͸dy0L b(_EEU1DH0!":` 8�Ƒs ra@Ue,u.ˏA< %D(*Zz7; J?kr 2K&u;.P@A(ڿNX\Dŵ!;dlN=kEV}p-u;nG-~9R^y-G ˋh'O8g?k~M;)iSo{a>1A:ӫgWnXwsG{rdrۢg26zЮYf'GoNK }ÏOgvUgI?_0l8hSNiV(>t|Μl u=;ǵ=OA#Vԧav4Ơz}߳૵˿y5ѣF Ъ&žzcۇs>~@4jO:yjWA[m߲r/gh$Ai%Ӥjf[K� Oꔶ/c椳�{g-ښz6hmif/^U/Wǟ9%˧lߺnɂ r@"O>Э]r݋o۶aig߾8c>SO# [/z׷\Bk?[]\鴴dkol}ݚ/zԙG=yM//Zu{ŋV gۢ;M۷),ռEb~dKWy'篻6n\M>>cC(-rsF�;̚7N9oBνG>\y>z ?^xу|[nm]ڣ_|pyX1txկ=t˚YOȑUD-85Wk6o^kw6~ w[%*Q12ww?ᷛl޸Cj:Y},ZigO]}ׇ5SN)ss@v&p>'7aW/zV~y�ة'z_~ ʺ~>oݾ'4<(~'b>~6.{7|ӎcFHE!J^'HҶN]�iBSA@8BH3#�d2qdiP M&1y<۶ڎ#eH*?hSuF y@BhB/;mP4_Y.-<4*X64Muev !x<L+lY;3Lj٥rܸqG~qhYxaGB뛛0+,˒ ł >0 !+` ,R8LO��eB!b#W B1Wd~,K)_B!ιTU({ H2TR  9.x\ i2L �ps&cuYeEWмn:A{YWѣGSjm޴e˖-P4ܓ¥iu666 !# $D�$ZBBݍUu]J&{X;շn}�sN\0jA.gMYf"#E?yMGc.h;yyZ>fT<"'wC/{[o0l\3:J�_;ktCnZ=0C3E#?}s_o~Yx~Chި_!1�?={&{tʁG?ߕy+Yk̛xcQs޻tĐϺgA/u{E@$7|kZD;z )4Q 8-O̼?b^]FhO_[Z0qWӟjڧp5a��5iyX@Øa?7m'5NC4O C}k~#'>杇DxÆv/7O>�bigk�6iZe7j~qE%w^q҈ڑG^*x<x͝w׻]5s. '}}[~ġ{?%c>_ +z|o9>EZ�:3z:u�ߜwMEKuWk:Iqm6S{!q؈>7w7풾uK\ZE 3ҏ3gFG,֐_?Wyyozn:&]}{Xt!xwЏ ~E<U�^[À/7�`y+Ϟz};갋G;�DƳS?gm=oY1d_~}ՁA1_O{^^̈́HnAExyoU\_/BỬ8xݯ2flm ;[|o<v#n_/߲'`Tv-8z]H^߰xn=u޺`&ݼGq��cgJ S&v;zA^j٧ NB"i ytVo' BS; 2OTKWubRiy0(c< 1�A)?d �$R 0<1\ϓFU/(8rLu\dd;=8ʱmʐUxv|~Ε:l褧E9 |/b-槔 6_|"AD@)!YZSIg� V\״,9s\+ 3 WUh9;XIgăڼa͐MG,{HL0Ƹ`�K4ɏ2s@a7)J@ДY�D"@P. z ( WsB As�� |QI1WB5bJ!2 0 ۶j-'Z%E,}rƈ[RͨqcN;5jSݣw?{"<3_zYmikFCMǣs۶ DDZ۷ׇHkkkeY[6nVQSӷ=fYr=|`aRO꺮d2IA&<c܍꒲Q�΀snh_THEZ[)�P@5 !2!hiV8LY~<,Fy`W݅ds+{'��\p3O%f �BǹpO.9;w};N)ѿ>xknӏb5CۙoTK`ԟ_SE7=tU+^?:k_;+Zlj#>忍3D%y'g=:NpuIK?o>^o00,g l҉'C|嗯% :l ACBg$I:wU;ROӉD"�@,3 4LP ['o;S "WS�Է:o>E$`B @ѶT -�;s(j*\P33%(q*u].cofqƐ˲ҩT:t]7@!D: 0ةt:|ɂ 57x%K}{}׮]g΅i23QxK! //Dnj@TFд3RuT"ra! _0Qdӈ*)Qs/PgX(bT*d䃱Xsr?!Ģace9Rb,Ţ UTrQ�R4M.P+˦׿k7B* ܖl8i9_m�ٸ 4=0x qW_}qTz^ԥB_r(=LĬԶm˂DkeUݖիW3 Ps  zOVM ȺUvu_.W>A^vTp-Q+5/nS]B;}o1α}sMQ]4㥺Cn8{Gm{K0[~_]IGPD["c" �_T/ϝD"8[JMÔgL:fR2," .8g4 @4 4 14MPjW !XGg5ʂNxsN�UTrJN(za`9<3"J*/cvwo߾ LlnmIg]t y{@DB"C۷xYLzCݻg25A €SڡćX*!+ۤA^"vo'HK4B_ *)ChWT[+@zL60@d{{*!(!FQ9x@7! D"0qq;)\Dl5#H *|D4-k۶me--rpؠLfڵ6m@ x8N2fd=TQYإ[=N8~4�<n �!@(%eT#S�D"C2dXEEz؊˥QsCVeCV""$�rYF%U?q�g!"� oBn{%= 4 YPTȏ٦ 6:+WT}7"Ńw}_dsn0vqA.vJT$Ƕf?Hs.6ɕNa "aJC@.H4vƦ'H 15 ;5eia�jPDu� ѩ𿁊UY ; :Pa; ' +FN P.N)TNi0%|8fOzdN&0l؈iӦzO|nYVyye& 4*Tޗ#oh7BiR$$QVƸy2#e(ɿw~}#TA E �@�b�p� K 4 ۵] 5@T0`]SRs"HB\ 0lX� \ #G"T2((/+''e�"q T(e\˃d '0u)BK#�p#. lz�"P�L�"%8@~ ƣ{7A˰ù?{…ZZ{"T8M)Zeg2sb1!Dƶv:qj]bQ۶W^ݥR꺎@�%0󋒨ٔ1RRjFYY#k  lilYn](d2AD6{ `CSs3"0(p%0y Y5?S�JA�d_ Th]nmEpfv B {&d+ij�Dj.Vt5AgUZ3AJTD%&bPY x7ȠYb"SlƘiB! "RbX&45 J ̶]]:(7QpYLp\ g3BCU ^,{dܯR&RC ]A:|ʼnD5>BS׋Nsv@㛻Sq($?"ZF|"_XI,(Pd��sC�jij!@S8skoO%╧qj: 8 Op4PPy[$f!АSHD$B�(#R}0&\d2a19iX�) ð@ A3 {!`2S�"l1P92d "1$ mŒqv)a9@>-mA!  &�_z03 "@)zlqΉ)r@�9x�$H�%2Q)1d)7!�Hp.!1A\x  Y]rm*^9lD[}5~֭UݻSPJ9g̱jCɖh4.#aZZ,Xhe!h! QFʠVT:bkX\Yp8N3hXF[ed:cM"VDx@) +dq;DXb}j444˶m�\V95 itF l De[@A �JdmZW��A �s$A壠f%6u&}R$�_YƳxI,S ș D$BAf!$o䂡JTD%*Q[I@@Op(Y aO"wZ>!D(w]=O30�)1��BF„!q\ iit~JbŠ�� �IDAThUTG�J3},:J \Tܱ2~ׯ�y),PWTN/ .@#@.H$?/E 42JaG$Ɩ_|ƍtبҁ 801&Rv ð P�@!xByW*ﻄ4$r $�JdPWbH)SsB~< $jR!Wo �\pz^pL$z36"RJMBxBp9RscHJ{QY�%z~RDs a \p@JaH?4DY9fMw !]{TVVtnjag2D\v&#sCtY|9szlox|[ݻ'5+A!<9Y ��j aBEl^fh8/ZFbh("'dld{8p³.ݪPdVSc3GC�ΥKZFcQi^A �jij“uY.P{7_RT#yRJl�n�,[@qeUe ɺځA%N%*QJTD@m~5JgiE�jFrE9%�8g@jRJ 3zyeQB;2#c:!StBelBxhZ ty'ݎ_S*dLWIp3) !:w |$P9s&ӧܹs2̀!sO18N:2 sF#-{_T^.ՇCa<)_0O|Yy+@ 7@D]KC1ffiamTyQț ZHN~52H,�1T|s F􄠔G�&��@ RL:JB 4ޚ.(onn,2 9 &8#7MSpN)d2auѽoٲd \O$l'�YĬYq c"ۂap8ʬ]y@ukaOD8眱P(ܖjC]*0`Iee˖e2M7xMB!Y>63V("0곜#!$ sM bB8˦c| Hk�!@r/ω,VYCq.%/IS ˍCv e!y;^D%*QJT7"EB%~` CZE1 O}\ v^5(αv]u( w>ԍ hG4E޻�RHR糨JB3 `uۢF)0zxQ Ax�0L_zv޽I1mܥKBe w !e !PJK2 m.8nL"*H/òا>E+P@")P/gS黇 A"/dc<ɕS⧊ <0Ƹ !u]Y \eMCM|Ł)D4=ב )581��1$FYYuuQ9w][a6�TʋD#zkE#팓vR2xpիWVUu-/O$dq@G_9'AjPJ(+3MAqk{vSca974̐iZ0�0 rCض=~L&o:nYs]˲dL4ӮfH A~@֛$�@> Bz6lYZ'xEqo4B3DD$jA[Py 55EOа.%*QJTD C꣡޾/43" Uy'iWj^ywj:EA{�vhE մ ?pO!'CM�)$+TtBPșUT � 4H$< E7c7Bik�\er;䈄s.j"O4`=?7G )d@Bx !@>Kɼl'nBBp=wLB>_  ܏D} R�`DcT&\+�fD$=vR`BީLd΁<N1L3!` sH D"~յI9E,& ϵxeyt&aG mi^_^5ڶݖjcJD_!^!A%e !LtldiY۶n4 5lt !PӴ{˖-H$Bf(664�2Nedðج,QF4x˚\d^mv"odk" ^�[=>ť)e%s�9])@�<B�=B"dud%*QJTD% P^B Tܵ,M$Az ,|[GAwy3AvY+ҏTN"uO AӒV*?A& H簣)け{�K ˌ| ʎzyn2fYF(d pؒ@%S(0zyq!\p.xlː5QЅ"zD"u2E_uǭ�(}qU Qe AjPjP@ TϤ| BaiJ�Ly(cdo=$pH!])!iauo^_P p8tk{KS*8p\d20i82=H*p@kmmVՅF[[[4 QKN`."a+aemmmT=Pk$zsq,kll\f F͛7wq"c"Jݧ{i@Bl:0+js$V*er!lJ: # |H#-84,Ch2iSC=[D%*QJJf]rQmD]dp:Am_]="Ap|NXzyGX2g T&otбZqUmyf%{^@QGef2Fr+*tR&#r9w1!a#c(p!<#�/"0YA4cE@5d/۟9/SBN%T3N5'QkY&)q.dAG��\0.6BCz:pk={%GCBƙd3K`cq1j0] Of,S�a̍Fb;i!!f&cصkWB h42q}@!@#A(HsRuQD4 H e)ZZ+R2O� +*lܰw Bf7t5t{{;"Vv)O�!RfHgRo(y Y^${5>@P RICQA͐gIh=�@2"|H!-_ nH$@U7!`Es%grU|*INO.X^~�Ax AF@QUҶmB+c CZL/k  �>`+D31#�7O�b@S[{hbC&Zv@=E+H"J!P O^�׵$c,v�T#剬s.D.]n~ C+b(QB!u]U zߊl5% zVi<W@͚WсƭPE$9 Wȯ`5Gl/ -;HRiCr1MJ;j*HO1" 1锝LyJ4AL&%9mR#ɸD2;N%Bh&d2)˲$~gPLDDu�mkB.-m˱rm;h�m=LҫVljnܱc!$ hiiJ$7mnݺ{FJR@>嬑lBADe BT!EDR]DV_&}XY.WEFu٦F^D*ȋmKu~x\}wIQ?Uw]]v9 FDQÌY'zttg<NE#(e3ӡQ55= ~޻g?UtS5}R&WmOmFKwq:}w~:FuҁFʏ3B&OsxhrHL"wؖe7T^܊a""eѰ.9!ȮJZ?1i(VowiPΆDHg͂v:X�~(*e_.Bg PG6ZןDD!0L^pb׏h4jv4ETS&DMDyMtz2׺IT eBȠj-eҏ@ t}ze#؅Cu=Q(EY*n p!jq @v -Vyջ(Hud^B�9x$ډd3ȩ*P(YK#p�0 PRR97M1ch$Ixܡx1X9iS��SXN(NE4\5نA (@ ? #<l"32f "lA4Q0cH$K&e�`Y-[yi2ƄnD"�ܜ0Mt0 nK rpfj1UDjK!DV?4W;F<vla]F*i�@UQsih CP�]A䏀ܴ,9|BFmFmFmFm@]Յ` nCwZH E墯6!^s.= ԶX}vtڲX,Fijje4Mshѻ�p~)6\4i:{jIhr\߾Wz@9χo!u=)aPE8@hTPP'995֊LɏK1()J rO *S( "Di{(pHbE7R ԉJG)pWtQJj ݀yy2)S!4)vC&z대s;7I>T{B@zC"  V�x9M "wm- 0\u=[>=+<D(c;^\+bH-NK՞_19 :W,{9Ag�<86&ȋQj 4!w℄68As.UB6'DjQU�uڨڨڨڨOL� }]1Q3V󶆅kYwo%UBufo]h)rw\`Jf BޑGSJ)u5Zu3:?�R>gB=/6|>;B7W O pB` 4X$$}_!� R]B!RFBpߛ%õB]3Q˕[W`!NgDB�+��B'KqD R>;� _�3_<A"�?T] ̠"Jg@8�?"J)zRj0ӟ}\�)Y q.@'e J Vq2�s bAOg5!#H)!y1ĔC%dq2BqJ0f A�A!�xV6Г"2e+ oBĆCԗ� �RpG9pD\Ci r*NH?P_|SğSs͛ڨڨڨ@Aࣁy/?FIs"SV(1UM``'1Hos6B)du]۶UP5<k[H7 rŰΪz<h&�V:8WeuCIyv(a |$ȗ7 c!�"�jCOǿMW(a`c[T%$7D5MNHd(ӺY)STm"7@HJ !j-ojJ�'U!*$'B⚃ N8 _ ~ AayYT$�0B"^�D�u9RC*b JG4P)FIҢTɶ29<j!N"z�? $%�6-G`30O$S0meUJֳxfx5_ZC>2Tn+OD˗>B&PL."s@J}xQQQ-FFGC?6N!@3לUh#_ "П!]7T\oHZMBwZ?AzOBp]! v/ׁy4/Uڅ:r�(Ȥ%Bi镵B=,7z;" : vAX V~+Pw^\=H9'"^1LyƁ8�RiDM} haYtڧZ"x{GU!@2S=� Q(J)P(2#\7!OpB (Q-I)4�$P�K�uM iG 2Ӿ@"8iFlv.arΙiDLRHqm۶ JhY7":QzQk>%Ss "gDDUZ2W6}`rJF DɠZc@("p.s^Ji�1FchAۨڨڨڨOlWo-^+ȼ S B\6FG88@�} 24MShcc)Nw1VMZ af*7_SK=P/pΗz T7z5Qk1$' +S2=4b+r<r ٞ W*NJ\"Bϒ\ R*%l/z?)8#]C ~4R<?EPK>O=*(>4*_8 B ~W{2)e"T "eBxzX1U'Sb !y\&SP섐"d(QW %ClW)ՁM 0PPJ3aPBG* �3 :?Iq$}DH(Cs2w@n:~w{ΨskFDytRJkJE'HJ$qϳmڨڨڨڨՑU&PcYt( k}G[[Xi}#"!QNڈ|Gfl@ra *,R*HU=>:6 m'{ _ Le)yAK݇u]+tŀ 4PՆL1R] 3$*D*1_Z*5/*<iy[;VtWG[eZ--BO 5uC~ NO.'g#20(\ n\9V`) S�ydA(Pb6 ! @r@(c)�E!vH@�D >�GA@z4 "rR@ �7B1ʂ4TDE "ri w"Y/JeE(ryEDj %2d4(fy8yayFTv ]�(rT:|?~ \Bk5Jl VU9k 3d4GG@yPB)=(+OFmFmFmFmR}hМ�� �IDATD!i-@/U !! "a]Zd1(+5P"%!Jjd.�aĴ1̈@qT&StlB$ yк@|X%&_{"\aBc!æy+GL>ծ,_3{y,, @ PGAIAjT0 BdJ'_~ 0ydM)<YKj~Q̞(PFHr*PB/yz=!@s W9p_ H,Kzle�R+�`o::B| J2̈R rl!8p]W98 O dK.4NHE#f| ѥRj4fEcf[*a �b1ƘنI A,MQ":C!MVJM'7$K|S][]4J) BASO#Rjj~Yd#%㶥0Oa5�qŢ6smkن_,^ -q& .0s״aC>;}zg|r4O#~忡 MQke7;jh }bmBAP� aH�GTț:`Bb#`T&2D+Ox9(c_{a %"d}E+t&ꛚ;w޾c] Rj$n*m{#qt=P*70B <q qqr a]i�yn$bR)J) BJ@'F4ՐQvHa\s8W31,3:{HJ(c9 p! D z;븮|TJ !َ�1H2)gEj/J)af1DفfҫCjSGO�G"!(@u#s@¨A ӵ*P�jT6 C.%yJG!]͈v0 p`ιk;#TFB<CeHDj2sLB 4(/c*hB$ %P�FiP3 A~A9K�zwlvÁEDx z=Wp=8!#bp>£y8xA l%QRCpp]9Qy+\;w\=3q.ǹpAڞ! L�((�92Y29�1F(]Ox@ A<-i'BU�! uL8�REd&̅ lN$�8 Py{A(.U Z(9i%)U Rw.ГO)" ��zDL Q^b:3vĊ ސ󝙆@#lhjޘk �h>~k?gw^yacoݍuq|޷wRtK <~-Ͽy>5޽'Z6JfW ~|ᐡt~Jstӟ[_9۾wOڨ;?|vsn߼Bt;ߠp"r7 +"EJfLZ&s!lH뵵^N! $BDDf>g ` 5VZ~C `ֆFs�`0#.Fx3KVVVvرIJ,ι�ymI)eLڜy)ͦR) t<> +3ɋ*۶0L4͈hYV4t:M)..cQ2eh=1 0KOi4X<ԥKƘmh,Lb:H084#KmQA>X2"҃QBKh. ɅJȕ G#B ,0+!6 Y oZeTfz!ރJbUNgP)@DB o$(�A(s q8Ts]_Yv>Q#9w(5nJbc̔n D@.(Lz\!(1:A"p!. WlIUg|O b0f0`Z/q]]D6)s(ȀVE#|O@qR[]>Yx'`%9hqFv 52M3MS֘#+J&{VMfd2.3/MUѷ W/ڿwi,Q9;>ۢYGV65Ֆ+T$w: �b6<o[�ى]#$^{v+u߬>}>eh{xW~`/NXr?jwuOظ /~չygYu Bp5y*hi'TͶ(;O7.{x#i,j_lc+vWOb7mzv5MebCc+>?Ϧme17Ənsu2xe OXyor xdWFHᅲHC'O>d`W_-D#T%̄RFu?t)r{js,U\ByL&egRt:NRMauu[644Rt*I2鴝]Ƕ͍MMMTqd3MS:`CRjH;�%tr+ 9vT*E3�-+%l^n@)K&dRTX$1 qzX,Ct<2:r!<纮9h4Z deeZ>Il ?_0UlB�8&"!oVB p W ըz^v:B o2E@U9 |QWں^?PaA tBxOI� @<c))?h$�B'":A QRDJA"% `RB{Qj0j0 A� JZ %k \9@!ιڮJt34ti/VR-4 \A 6thZG\4LfS햭IE%ɢ"2mX&aF*cێK)Y1ͶkS _DX;OUˉ~l�O~ >; S^݌�X?+:b'z?[y6;7B\{cN}eε<W7LlkO8w^߆pg67禉j؃sk6{tߟ:rLotzeuVgRr [%mY.7:]ݧˤb:_\_r߷j=6zG]i]w=i_,Yk~y唇ixڛpQ-ay7gY{ n}{ڌ#WA;!8罟7i'ZMȿ.iNtgw=?#*Jl(' 3$0 � BnI,p+ݖJo [@g:n&j*d2(Mt:ԔNe64ADmY�钏�evfhcu4h4y^yyyYY±TsFz+477gv:F\d2Hd2\)HcsHYBMMMR ,'hTaiA,-4IKBeJDHC!I&Vn ] z 4({8&(!$@fAڙ!2MwHz=LKAo:s f*RN"AFx\魈^7('8h"p\L(Z\ŧhYЋTE|-T"ߎs. 0!%!fhCOEHT?K *hT*aG}%Ech"ncRW, <GP`QVƶSiY5 ۙ~?{Qf7c; B"ٻN=WWi:osO}k>]~qU�|摷UY-dޢ؞Sq5-_9g.־x^z?3Ŵiֵg}\Ϛ3OsōG6pWT[QQt-*1c,[c'5j_[ @oWRRRRRBYOkB5o턗kHݲ姿#rǫw=uuo|酯]y.U=\: ҾWV,^C'z^zV� ֭{qIzlEwxo�1<8�=}Mm3m6qdL'V޳wѨ{}3m*FuqC*lAw]5oQK${P";!K{]ED>?#MSw5 !X+�ҋ=%=>m�M_0r@粸e~ ۹kXCpd[_1Itq}ߜ6QG1 z'WM|��Hr^vNZVrw�� <4yE5�'O޽oUI̴ULP]Ktz_5J%/\~b.{\|;7nUcwV"=y/]!kX": �OX^kwg_;1uf͓*:~ܟ߼]Ƌ:;o%8գ$-/RW[iC'9abϢ_S<;akUVƣh41/mFyڑE�U*};HFhIݏ:}m|xx$Zss-meb;'޵8o.Nn~nSf��gw84�;^yA/z;QU}t 3;tc\=ⱒ\*� Щ$fYʁc<5LPaI+AP(8Έo P OB)Aw�E D[Wzַ@G/iX$B<A&<-2==4Mc7B:;.jD噐%п�B 3 ۭݲk7oٴvӖtBT*#!h]H$L&f$epQ/~{¶mq.@�*=OhjjBD0dquL&NKO ]A䠏om%*Z悖$MxJTt_}8d,!?c`�t'B486 �_p]WhC2ŶdюbOZޫ"7=CW �ĠA NR5 $%)-sB2mI;g;y9s{ry 8zHP-ԯSZ€P=.\9J D؍B rP$=qBlAV%PtRRbEw� rQW~/!!ϟMV[Bl|f>[0+(bd%K[n0#QWWd;~K -r;n2?Lu1{u ^4oyR` q _OC$.6w=ycI|twو{X7��XG�H;o;ع oàb�cVq(y^5O\}+^N;^.6{0xEɛow'v$�@{2|c;{n^fU-~/7f<n}77ZX!#ܽX��pcl[o>;os8']|_5h}'ఊgyZӠ? �bO?l�bV {T?}{')'?qGݥG\Ģق7gohqG|sp3@_g_O}mÆyo\;#�`.ݘN;M}rg{GV̼gMؼ۹Eto;r ~?v]s<�}?,[<ݳ-?}n^!'j�dGw^,sK֯?���s;~lNuό;K<=u݀u?}=Ǖ_\⳩}gІCdO<. ̹S_/yu�:ʏkV/~՞B y5Iﯪۺ'Nd�nykʸ֏~i#^yع͝ؿt·��o>;ͻ}'9e'Lh#>Xl쾭TۘK˿!Nq5(j7%x_xoVM/h!6b.|t墏=Gÿ7eAddzt K8͘vΕh#V?~?/.5=?:k3S{Z8~B={.ԼO}~U搧YʼnM~^tKُ{i�l^ͷ^[bٷ/z9R5AYyz I-H7Aho-}ܞ[!̵sy_Aٙ1H$ʊaТxX=B�??*`6|!DA`j[/XEdȴ(gQy"\S9J/+//"=sO:}gcǎ-++K& غukCCæM555VmhhhjjjJ9X5Dqh4I@n0|/ n߶,K&Փ-A:477Kwt:-AgXAc֮VP!ɂPHD02kk%tG$2@UHT+50ȑ'DzH=?$PFAY%E.Ky@T;d)8#" ,t0iԂPz�x'gLRw[ykRs_>UUqz%yQ)4jJ6Ap+H6DŽ*|) Spt އF ZX )X>47+T rGJ}zCu]7ɸsĺMV$ҡ}E%�7JH4^XRJ)3&3^MxcQ~#g&H&C"E hjhIO:K>{L^>?Юk;8aaveqCcs((;MK^ᆿ΂w}"afolilDQMI\;Nګ^8}o{ѹ;JH*i Ox!sJ{'+w8XAў?ʍc;dֻNVmY1s>[q %%]E& n]p;[W16gW 8EjM9k3Rsl8gmglo{/?qT FG߭zW([&+-+Bd1GU|;ogb<<jE.dHV ]K¬35^}}+I{ot ˫:<m~; 8úa~>UsntL]soi_ pT_$vc={&3:AF(ilZ݊Ze@?PgwwUwil&;UwqҩwKzڻS'4&.Ntw5wnCc;ua8�ٹKNɭ"⤷vkVY~]n}ї[n9jh׮p)נ�� �IDAT}C߀G<o~~ֿ=|w<5n=aHeyqW=g>y楆Crvs뿍(mޗ:ṋcFM9fkVx}':x܃6jfQ֥gN=v<ǮmcO}owUYRe'xua75fTsU mIwxD3toLE3f֠X;A?J`'3 /{<#-zklDJ;v,5cv6#u=z! ~X#9=uoDw=C3ͩ` � G@>C_CϨ>2ra;LbX,1L4X2DnDl?�ec&!LBH$I$2_)I3HG c"fFO4664RӲbACҤ㯏<<p<GGuǏ?j]?{eL#RWWWSiu~^f k6lݴ1U_טN۶65le̤@ e,0T�҃(+C0B |x8-!kKe \e % a>!̬ $N!ЮH7 JX( AkMT(Zț#i>" /j",Z0)3&c&5)d�GoȂ@2>!O7$Jzo$"9%KsԳK2U'r'P<2DS4zGg0?"rqqǃ>j #š%ZE]- N Mm2>*tVrY H$'űX"(6lؕW^?~g8vŮm+s 5J!r=}^8oP�H(I\aSc3$-Ϳ/lڟg >K?ܹs=fر1e%n~YWkxΝ;wS--J@scDQ@jՓ+-ƽxq-ҟOWD;yLݒ&>qRThis^˟{-gwL|1~bE /"65mjfV+4/yA}z獃uUd~n7=_/ڵ[q}=4G^}^9k<C#5I=f>jw7*~~5Cxei^ Nl\Ih4Mz^eKΏ-.iT�+t:b㺍й[<ݾXsL87(zј;[RޙYrMG_< 4qy˗شf]fFh؇9[4$-nWnNKFQqNe�5.]s0qڳ̝;wܯnNhgl#N(jV]zuU<F^]qݚT|C#_7?qlXf}{ţh4Oܺ[x n]b)V=>b͚oWR<w=w ?Pk6j)[pfۧxu΄ׯ8lSukA^hUk考Ϳfl4c|+qG}zKz$W�)*.L*HEhMhng܎A*$-A gn׀¯VhE .C=e@Ҏjns2b&Kn 4 A ˦<BL.73f(//ї_~9bZx<UUUVUUE"!C 2vΝ;+.u<_o׮]bq6lhjj<d$X,3ei2F(eIcf@1ds#Fa(}+7BB]@?YCA @5Ry6!UT:9_g l50T�jp=bY":O@hi;V^َB Hr ftPGB*܋H0k:#J)mQq��]F@</8-E<up@BJt=O #D R�`8 C0 @.n \bFZ%ta<!im^RUIBFPoT]C%R6U ! ,ӕSb FM}"9.�AQq5ۗ[qku|Mu /`TUUXW\\y@$�!"8GXeG\rk]="sG׋(8 .b'-lI?'4 $9ǑRm` X|0/|wg7. jڧO2<lռ8@˒vo^ziw�tu'ٶ7s뾿HoOz~9P8M"6刻hkZt9-=H˓z:(�FEzaeU{ W,5+5o=q ˖lUw*xI}k>wS4v<=gVtc�C3ξ雇N©;~|&cN&+*:S"BtDdUVxS[e/??w~AϽ9ȇ} /np^/cevXuU59,f׌Uubzݴ:AfN2;_⭫'wҹ^ҁ^Q�V.Tu i}„|͇=ЊZɺ.XܸtYFJIV&nUy"Ba>twJ:NCo\18|l6*LF"anݴdWRT׬iлE_ߟ^'/9mpqÓwYu*`�Yl}U/{ 0M]+/Egu=/;�s 6/VuU렼ono/ij82 (3$Hʉ]톅*'J`F ^b`aS %|"@|3&,6T7,t 5 [�ȁ=NDImۦ2H)!@u!A0M&PϜH$3\&q˔RsD" M�$\9D)% mhnޭm۞˽j纮iCEEEDcNxss=ef"Q4h!#8͛KJJ]vمRbŊ+Wd2yԖZ-[j)xNgdDX$jZ(P�"]*l//]ChN{%IBpg @!2Ðc*P ČME eJ_~,K.q]|Fd١.TCy:#Z !(�(.sq�8<_ ��E`@e `yRa8�& Qx3b�$y4PH�PBEJ&q9:2( ) ]iP`yA)3 (<J ,b0R�Bv!.*hZZ`j(-UF0)T (�_ P4H r#ysM_QJ J)E^Q?t \))R bnJ $rfR[)k.Q˲3T󦍵 : 1,=&PMw0"9A0_#wc%~v; ̊cO{|rM{?}saڡVV藸Ggt귞xo}#DNG]sn{+zB/|ygƦt:F07!X~>1`%<{4??q_{{,_>vj ݫg٥hݰi[ `�MoצnLI3 ݭG2콛oYuElJ_L vĈDŵQfԀMWn}ʹ;{_?AQ�(cL9Dݯӷv7,p>~!>؏'bҹ?[nIQnź͋^抷ڟ~Ś9n8g:v:cn>U޾ �mhD*&4_/Xm@fu}N<p=ՙ]KG-{ۻXIO/>piGt%u�6`c>oM�lX"D!}J%ҾGw^rlbtWw5{}V k^VF;n>sj;O޵}ff# 5avu(Cc^˯ v9xnmf@hdSN%gFyut�v9^NPkX*jW֗6*ϩ'VpYwȁɆuը+Mozxr~*^_晏NʩPcJV|e;m5saZΗhG/mFinkVoJ!�@+}޷v.4[?<aNagҎP?+e<ցG:+.iNdկ;q�qQUK?^\fvхW=.f@HQhEW}c ɭ~p!.$Y\RcP"uCi7 +EEL&8!̌.D%DlaWjl ` Եn Q7j/a@Қ�H$s!Ro-D"&<(dB<OR%%%ht˖7VTTbe˖ DbH,N#J!%1ņaחZ!9UdO?.Iӱ9onnFi2"?^ӯOv%z7``Iqh<VTTdY…=I&´^H׮]ufs6Nח/԰B�C= 4!<I< p%9.XE)UH,t[y1J\ϪIz)�p<W>,rߧR)f~�w<Bu`.\h'vPIV)V!@Mp�)'Ԍǽ�9U&ιf꧄0@T*@y!Bje+9 B'"Q%ehs SDMԚz+O 2Tb6 3EnRo \ }T24-ߑ-- hO4_{JUWa | 2**+;2J]!I  .�yI^u/"|O/My]2zA<-%� %’/9q6=GL|BfhZ}ϭ|޸AẈm:#_^v{Z<_iT�WwոB?=yK.Y ˿5^gN|=w19�6Yu#zM?8sk2}߼atK KWλɁHN1z|uϻ/;;:z`Q7G&}h;xt�Ю Ĝ?tD_l2bks]-1}cw݃hi]<;uVy}O|F<Ć` j2F#'{^&�@bNs�bKʁ_vY;0Ͻy5^ϯOt:ni[Dn>m]Oyŗ\ Q}SXb /,~dh�HSꝹ'ۗXMpe=&: =x}i[?13zO[.?ܳ_9g{|˯?]qֈ֥hQꋑ ZeuͤnG!E$v}oqv=jwYN�8>L9Urۧ_p��k^}ڻ]юV>8>Ʒ6I\Tc%݊ -877g:{ /2ru*eq7pjLTٲv_-9GeFmTh{p&(: -�=8qrVRT|uwO?Q]mhnN}G[N} .<ljmq>I��cN|&)Ok[N:_hu*~nEbfѓgutNDK'(-7d9~Cp5442y  !RH)R>'TY;3 �`# v"Zȷ65hc-T+/Ҕ̓>}7VVVfͪr?x�@ @iF֯__UUU\\dɲ~tmmm.s΍FUUUi555yM) ( '(55<sHēV,ιJ5۶m뺑HVx^qѸ=p/jZ�Rޱ²"%eee+/iذ]*֭[7o?ٕV>لa% .z?@Q }Byi۶`)A9[BGR]ו%`Wg@w -sد}U-%@, `l '�.R$&qT N%N$U l,B@L0/aᖰ~[}{9֚scܫiPWkמk>\=us)$ՔU D4\jlpi+AR1ESОh Z{oasoIoCY "JfXU�$`˝˙j)UQ1 5A1-SQ�"8u;�kQDDdMWi42HD@DBlԬ_ە6q  ^Ok 1Ʈ) Dٔ=A׿g5+"K bږY�D(c ho{KSR`8\OԖ?6XȐ!!0�X9S?DxE4 Q@@w߷?v__{>s$WU8^ ?o{[d?eyv&/??%6/m??|ϧ&N}N>Cu' ْm/>OV/^7>p,~=yw???3RDЈl;k}rU5M @.ZzH+G23�B ˛f!@𤮏Ɯ6 *URvz.9DƍEQL&f.UUyT5 "\ 97,+,K1K.=<̝;w e"gH&Ȅb4M^fZ2O!UU3.]侉\~m3b9k`<~xrڵk׮=È%)/lBMG!]i}Z�Hk.R F]~4 ɰh.}`4"t$tA t,덶iɹ{eaL"!"%z^=Ha-3 YFP,$+je²i~R#zG�@h 1FͅMc_H�� �IDAT)pQEӑ+ qyuq�=�fvUOi7I&mLzIC(az/ƴjFpf;ru٦w+;N Zܖ%+iR'/"Y(AB㫪\(ZGX"K(pw&FZ~d'?o_~w{9{QοϾʽ>/'Fd'�dUegsȈQ$@9Dh,VKu2sHLwQsC ��9c At|˩u,Wk"vFz@"KmNSW�/=F/u]퍇a4 GQóޯYU̝'wVOO.)1^xk>p|$/�8 lzZ-cY1s.h:e4QYUjrA@s`0t7?VMS!.AY9{Gr"lB6 &&?\C]Pלs̬q{{M' غ "e[)CYLdѸsN.�@vOf.}o50A1n�kO/Gt+�̀" Zʑas3:."x&5Z~{YCc<Bc PY?tլ3 f6u�"7I� Y2h�XQH3*9+Y0 ~#6>ED#zCI뵕\>I{SΛg~ #-SghKKK3GBd{{^\f}!dơ!Pntd1D@AHHV3NM\?3n>noߜ࿪d';y Joߝx =}2U�io�DĠ15�`U-`0Mp+|Q>ST3p8Qmos6h␼f?Hh;ԡ2E9f=sN}bZ%i%cb" "'''"`4x|B8<<N7o>x<.r0^y-Ŋ*u]^>vvV/=쳓>S/ />9^=,^Yozr69K~T!^9g>`B },zJȽR/__CMIkGD)Rx\vQ@UE9uFTB[ODkؑ>b[K^ CnOCFz77{_iFP^�Ec(7"UԖ! ۿ",F@cK+EҒ@ Mf $V\8Z+L�B F t-h`G�.(j5 :יmaK"Ҳ/ knzagIjl76盆׶=M*=kktCwV?53GhB5 Yc"&ɾ.�6]ǝd';NvצາEKo9(!ptfMT!8< DE "D5[xR^#o"ەR?bD=*Q|BŢ5&$joB@4Z�!ǣ|zҥ<Ϗyߺuk2h{=R$9sk�iES7qT'w}!cb9g� UVUY ׿(��"^r2a4GÕ~׻}p8#M'''|x[b WzsxxXUՋ{9˫W:9c2ܹ}t|@'zUP/@ylntn_|:uGfj}MsPG)L O-pPR &�1`o{F:㟹,[|ƁN>Xxd}Bp-+$3c<{6^z"@)2nȌjAzk^ƃ36 qh !65!2@By&O*r1"s � c0zkP! @EH$ Jt\+S}?6 kBrjɁ^Zϙ veߍt~8߁# !}䨪2,K,|EPpFwXcM@yYxg%Nvd'_V�@h9g,==۟X\.!(bBHP'j=^] DkIAcIDZz< ,Ik2$/ HLP/"ZFDEEM%Dw5uZbgEQA]׏?: ٜUpx2'/l4p8e̜[W7YPb`].xoX999β9Anܸqp7>{'|w].O<ɕN|g??}u=6셓YE .($Bb�#)a=7+k� %dܺf�Hsq\ j'vpAEQ8x�`eo&cB;MP Z~@T%1M }'&]ڪiK�B?[ h i颦b,u Q9tӎ inQmJb�AZSs3iUNҸ4kcL.tɐlt9\.vЭ>"#Ebɝ &#�pXdsΒd`% gQ~F- Za3i(?6)cl=ۖ�l"̱E"C[Em>ڐMY0i\M=̾|K{ ૺ$lK)2[,uq,A <�݌&Rvd';`%g_zt:/Wk<˛dYd;99I]uUUYc1eI>�+c((bmuS1ܽ}YQڪu]7�f[ksUU)¼c4MS�cUd,3gggᰮUe�J!GCc5b,YbQUS" P,i,˜%dyFunL$0\-`` �CLcLf~Pܹspp��7o|`M= />'us]V<k.xG=WgY9,n_̭[7Vu8>pʍ7nݺU{K|wU5!%Zc`$D-,i0SM _!z:u߱w5aj!IyxӚ@B8P׵A!K@`YLDD8<XGV "ˬDf檩RǸY*)FF #0&z�Mm�8F@TV SY Y'G/ �8F&D$PT7�E !b*VrG"@¬TNh0D}m4V Eg(Q4g<DY�Jl,c Jd`!cPH"I^BcVk ^j|hmnc뺶dtuD8 QZ ( wSZ:GA*l 48vꀏ5bBF0 l웮z62": *5I[¬ Ym z B彀r BEeV 1& F*d';Nv)(bPq@WyX,&jl6+L1*D/"!|> h7M>d2N|A{x8B!qQ4]v{8ֳue�,K |>/R'%@n%y�@YZ58B,kipo],7n0("R/uUUY!J yYeERX,2c04MS�D9W&l7M墰.=f1>!�Az0͛7c=&"'w1/_FWW^?8KƠ,Kҍ7oݺÓ;oy˓7~{쑯#wQ<sG~71(W8.vL$|31ႜ4ZjϐWR$BW)|@_!rT:alhI $i<ЀFMt^Vcj4%({BIRop:2�)�K�Bйd!b5c@9ԍDȼjci2E#0r\^JQeC$*iSj:t?qD" Z{1Bk0$Z54 �&+A�3&fcC&^�"sEE>IF�AaC `jms _�`3mAIds^G  EO2{#� "�!BBZNvd';ymDF_l�}c驞Jf{Y+Qs y( crNyey͢(4,l�#֠G3c}4wZ,ZIaZL{TCK7MР<NOO1X,D97NOO,FO=?ttdii�!W\uHdPSEp0cԣZ.DiRu@ٲTUcl1F5h# :{u;::?<.]4 n߾}uMM&;G ]|ّ|Cx3n^_w ?/y/_Ͽ}z!`̃�f }"6?/ )<CZ>9b!J-|o &uVxk|ZDVK|Z1Bh11zjq|A$؇9Gҷg;D��Z봠m-w` q!"%/r6B)(5"?EB !'[aJ'I-lY6E(iꏮԊyjk-"G5]Aح1Y /4eKp Iz(}X!@4MKr:n;y^.B=$sF  �5À8 BdAMNvd'; cElyQ۟Nf"hFZ6<0͔I�|^`]u!s�:bn-˲T1z%+oF3c|,3se1ykA]goݺcGKbo:.0SW2i ֳ٥33%h8VUAIњVU&]:ЯOPjfbt\- mD33!k)"*|? W\sr~rxxgŵt. ?7 /⭛|ʰ,NYw8iWo?~H^�/[IPLvts2O`6\FDDODӳ(6VJcD�ZC%r5Da99̉Z/HC;3Ҏ;4?zנ7ժ`1�P- E3ŃX om-z~z )`Ң 1jjoC!E1t >!8� �5R$c  !4MvQ˹XLWp~>W>7t`3< Ofؖ  s:tOq[>>�HYH,Rvd';N!H7Zm,ˇzHᴞkW1f0el6^<ϛyᇏNNNr5N^}8P&(̉{4MQeYA>/ѠHD~Ya6bG}Tˉp8tMSJܺuk0Qe EX�(,ˋr "m,ZQ5Qc}y: ,˲,"bd,(1<ωr&D:NW!ƐBEg2?Z, ^.y[kr.F[G7y<ԫG?,˂_w 7ɻwO^|KW.zbv gg>d)6}[}'sBw">o e1MqɂRlByaW(Nm:UU!T@KM Ҳ:0QH'�2oL "m͑DsЃ[P:.djV&x#k½܁A@x!io橇}2/(lDYL~+�"�H,Yfo[.I5Po֟'-t� QD s~~i+JNzT3&*5޿6~sX]md׼i}c|h�\iƾ^[҇Npc"w7w-!M�VB2FM^p9{?_ogQ^3N^Ӝ&K?zBkoQofmhj/ +Ps�FO</ғO>˿ˊZCTU7fZ O# _Dԡˈո�f z08W9tPPu��PUݻ׮]FMs폺ܭn2+^Ս h~4%lyІ& h"u-�J xtuU%h4806y+k8z2/G�X.E"sfâ ݻgB&^yZ!XӺYditO6K_tBD*s\y) c"H"@NiQ ,b>_J'j=1�D0%$eo"Bo$9_Ƕ>p" 3vUK( >`/=^8˧H@ZA5vQ<۞i|RWT"3k!K.ݜ$k!� I)inSDI2%iihSPz-ҎCܻ"U4iiA$]ٚ-Oo4jRj)kh\)+پIsiںpQ @�|!(OO|꯼ g٫e9;yMb~KϼqBd:}YkmYJYe9ӻw(z�i_\s7nxcˬDòfS"" 5 n�+Vゖ?�"`�D1E1H꟤C1rZk2bsϽL&H;s"c M1u4Hݻw�.׺\.G!i󼪪Y"MKȲ;;;[,Z-!r""@f6L$1"{HE FVRc])fٓȾp3 !�Zh~u{g(f2[yM<(2T+cd\Ns׾^O1K�Ѡd,xaW*Kvqr'I1w&t!X?zəwAWG_z"C%Ԓ(1f֮CȭJcXkP#�`-@[!`�DٻpfjU`sg&h1%*[ߨigYu!h:Mh;!pÏ+-Uv׽uPT `c*̚](J�ФKOfLL՟܅��� �IDATu�3TK9�AE�#skKQڀYz~lDn{Ԡ,�"((m 3�xY\woe?1oMV{Xm%:" H \;/YF (`K7M3_ d';NvƅC48FC5s9d~XUl6[VeYfYeLVfO&l6>!+Wܸq4A`8@DEQsRb fyVU|>WQvfDXVM@\w'"sN} �[lct>kDYZix<L>~r8Z*<qòԊdt tgeeCL9 ^XV fyynڪ EX ,r�RC-rTӃVЉ H,:~催Z-�Vh43l1Jas~4fM 'سv GwJ1%`ODз zZ[�(@a0Vrt.F,Qrcu{ODBh@  -"aHOy@UV꣸$-R%"V9A!!L6/f-�[;B[C(#y3Sl3R''d$,h1hC Mc{El* Jx(/(N]?IB@=R4,5M RhhTf2eYzRiYL @1zc2-bXץx.v|NܧZBױ GXb 2AChEx>=T;d';NvffK $,u@e APOeYVU{p>sG"UV s[{}eVcVH( #, kmL&j`h0˲dKޏ&pyFDǡASyAXV(.]~+[nݸqmo{qGEQ{|[[3?3?G!<Êdt: P4ʲh :{EQˌF;ϊ<l6eK\c i< *z\@>![�7ֶ+cчI34\Y"@$\�9+AX8Ec�,sm{0]t.2%'Yü-}XmBAyW�?"̑~(;ܣށNl]y3iJ*M4sy1FOZs6<G_W.w`,y "Cuι,=D$}c5d@oSQ95fPYݹs'˲z֭[Y5-HO!"C4 c(JWGib ZQ'-9g[ۄ +(nsȚ0vc`Y޾�2/qdb,<ϫeZ+tҒ%lάa&`@ -2[3JHDIU#5 fQD:*A>.3Ա-t>�Z+@~8CdJ{:]rTjT-[h%?vF/e`!"=v)*DNK4o!Rck6hM@d� $~<oy_?xd';Nv", cd[2je zZ ˲,AZ*�b1L^ @u5{)dlV+,MӺSj13y>V՝;w 4ιjZ˗5Lࡇz> c\p8t钂r\.~~www3 ++n?seEq(`0Fggg4AChv4) "-hlhL4ri)b0TUa"QԊ]hw[ ƨVQ >߮chuѣzDe."K\l ]l엻^Iv [X"ȭ窨La21zcG$PQS@EYVժɹqd*u]Ey,EIDݤo{Yjݛu]+{S(�friB.9gATRoEa4?09tW6}U�",m%}HLDELM9 B-n|< "U uI~2M"IQ8sihO�l `#hkh<lku^۟ E6~tq>Aii).hkq^\ݕ,i*;@+#l m,�Ő"1 g0~ ;Nvd'19g �Z�5]ݡ)nZ}T? Ÿf 5@<"*NZ PǸjh/PrdBΗK 888L&n0_.o`0k˯Hʊ( ˲W5P/p8;999::*𰪪^z(tzMA)b6˲l2E!1ٽH; DeEYY YQ&{Ƙr9w6*St>(ܫn $&G\}LD1?*'Y8Mf!4M|f-A l_"o^MBSY"T 4ۍF�EpUU8uznX4�04(FDkk<==M.z y@} %-a8YxΆÚOD"!9Ǔ�Qkz+�:fG8c[%D"d1Z2˩A�B #9C7EDB݀i 3CdaAg& M:|<ϪTQԪLzmʬ� N9ɏ� �m@5e7cا</.lߦ;s MBBC( RWl B$n }n^LP؏H36Ȇ~C32Oȡ7֌ީ~=Ɲd';Nvת!nl@WuO/̪8CgggbzmePQV뎉zֳ2h2 "UUUU "{{{ dR t!.K ?<<F�eb+qD?Wյk4ui^8881W^]fA_n&`0H |CUU1FCY:?JXcGDgsj^I΁Azi[ B&uΒm.�>!, ~"H=|ݰ53Խ -C ҄k<#y1 n߾;888>>ղx7a檪PI)4{||Gú' t4:@-`UUgggu]T.t@9:�foFTB.6Uek&ᅳ[_%&SZ~1Ft6 ܖT L.1{VUF"$%Y2iYX-s�~}"�H?W*I->QІ+Bb5DRu"5Ӗ}%{/ Q.5LDΨ!"'JDjQdyU>{m';Nv5/gggԬ)�RtՓbz@<g]-@SՉ}̋AQyf̗y]RTNZzd\BG�_p6 `0888CΝp�ʕ5{n0G^dyp>[FD<;;C0EQm_Bc3WWÃP7ntP2&7NyCKD4 =dx<VS"X5M3_EYxeY|4fkiDn~2E7v8d]e׊@CQA{MzV̅ᾈb=: O;D\T'S ΢c�̂ "y hAL� ˯<g=88yt "HNN1:"xSY/|>ƘrId }LD/fh܍jݰ,NjqH(5!L O.M~2:QO>(WV$r@3.Պzum.@Dд RRas=HlQR�2(!@ iORii^`>Czɿlt� ah<"rP 6<o?Y ugEA4j;⎅ST 6 =i@_PeBvZ^� ¶,M럼d';Nv#,CKIn@1f rb;V+u٩�Jz&d5Lh4RD�NZ8zĂB޽yIСsK?fa?W^C A)9�l86U}eɤk"+B28DD4ޛ5hhcOUq 3T9L 5WYO䵋^D8>�0K:QhՕ-X %x/y}Ӈ¹p{n=7�Tuk-W!n#$wO~ڝ|r<گr諿]<d�捣lE_E<ω�5{(rY׵#UEB*E੒bf^UKi]Z/JDZ/^7 WUn%("B9mZLiyv'FDOzDzU41cLuB5N �r.@maFDY)dFv|n}+\7duh DL (J/ma=B ^k}pDrW22&ЕB7"5@� H-!{W?ӏ|L$|'_~?vΥ/W7Ryž}?# {i,w~'+Yxl|K>B7gC~d'yCsҗ>r/!FsoAl�dhM孑Ɋ�@ s9@sWSLgn}R"c )*`Xgt:W6}h4"jVرj?VXZcZ\@'%̃ xrrrcc=vUb>en]qrr>/>OOfgӓ;_ycto2*,KKF5S`0ȲL}:ӣws҆jPPJAƊ'tf@gi H(D.-V`AYs| �4S>~}9>Bs7In֣TpAW%u:WA6m=ftcě/kҧ=_Keʧ5ZcVUu4DŐáڡ4HiTb4Z^t2dY6:P<Pa>WPN=v/\rI.50sNQ6Ƙ3 vIb-U5EDM!"6j7:-oT �tY<G961L71h5(Z-%nm"EfHdZ݌�Q<*31?A"ZѬ[(|e'Q[NΏ1}e� �0D画pq'w~O`>| vHOg_?�wʗ=U?LO[PןI|wt=_s?Qf_՟l'so{_~ϻ_]gS;kC^=p;OOYrғ{)̬~f&C*5p8(mį!̅Аs1TMcHrΛ'wZ)ΥVӻw.t%H܈H}r|>qƝ;w@[`PH|Gy$p||4M9/=CZ!ϲ[s{{{M\~=Ƹ7[ON *ܕ$X.Tx<V6H`.]:88`|mfXf3%9g԰bFlO$[ Ngl�|l\Y`4�q�5dBXrwJ,ͅG>  MN তRaarz2>P�KSO_z׻Oo__#<ԛ׿^'ɥ"ˋ3;zX,t/H]"&G�FGڞY[J~$qJ0c7c H ]@]":M o' QMVhKsXs.lpoohYNgZ$%Y(QǦikC1plmv1B Y5ucaլDD$61ԡ!M|ਤzLzթN,u(:0GE^^R-\;EQRg*Vp>`a?"QڪTU7s ?28&Y9ժfcm $?<ò{qHO}{tP_?;?]w?\䣇[/: Kzy0??};߯?a>c&ѯޯ{,O8ݼ>N7K[܏җ7 ,_o8(˃7{~[[ü8x+k'_(3QGk1ػwɯ~�_ï}Z]?~];Vw|[_sNcWo}Nٯny`oC~_gxe~ ǝK^}M 蛾;8E 7TU\.)YPӄ`T"[,+q0ac kf`44ιa9&0ͳ( >%,ْ RfeEcY&A$Ԭl>],dzrrg:=WbfPf\tp?gg'g'wj 4> G<.]WMDYc*wYLD@vX>Ns%JdE9gZxoO"^}!cE[b8(TbXDoY Ec"1Y4b䎦`d\y g2gdDBF$bc� 0Frpp遽d4БCn9cc,8k3k3D@"h`By A'0&d "14! 7׾  rR;}ICJb4B &@:",r0@(Clb.L&�oݼ~rӳZ Hq_~|v|V+ᰜM_ycٴjcD2@�#�] XdUUd�YDPC l׸"ϝ@P7ER"F"`�-i #G`!hQ"D!h` sք FB4@7PfiB#.2jF{p<wQ\k0yw%*9' D6L0`09dL LBHH��� �IDATVՆLJp}G>ٮzs{'ݬ+]M%H}f0 W�V2lEn0%gdʓ<d@JT( &QyWLd\_> |D_*"HDQcOhy* :B4Mi\ ce  q qO  <`9*||ŔbJ:4k�t^E2�@ G'LS�m?CQ7#Ti W$SR!`dMÒ.g?x-_u7_v/?�{/^΂9Ow.ze37nUK?㴗nhmNL{/!pִy߼zIg= a>yүgzf;'7Mm0km哮�['an[ʭ'yViOlG'Íg{p#eچ/lf0 .?P.o㯙ɢ߼nاenwX)}źse{vC7ܸ){{8k\&6L`p+W{ijxbL󏋲|>o}}?:Y#&_c[Oj{c`�1`\.Gw)ٰd2O&h4D"v,ǓUX". S@ӕRF"l�rӗ#v` > j !$;77776llLU,be2zςگeY2 #ĢѨX)wAz@Et&yBDsK0(@~#H2]2d<AE8甆Ԥ2,`E5EO(ݠmP| aMo }<�К677_q]rRKm*Y3r9-:$&^Tw$|33ܪvK;z.A("C!`iu۬ Tא1$!YC4-;oji^r9ɬW_~rukV-~aCƆzɛrtK ֯㜓?OV[JǤrKMNT<-7/b�T*HX纮iiry*/z9 %,ò�a8}XgPghK)]/|>y1f6L>oim, N˕b`ؖm $/ hSY�ɭ+O/n<R"JѣmBYhUs73_/l[MrXCO ]#޶3,×7 EEuvC?҉q3+cL_3ƀm~TzO}1y΅{7,Z܌̓_wg8pWpcnD�fiv;{^m61N変3K6<q-5xn>V=3swߝ*?Lc5d/ ߰dYmXaoz4cU?R}]n{Ror_6"%j{n6rW>CV%s?nzR^>T?tjw8xmXly~x9l 浯]=|>Ovwko'P��G'< �_>5{3m{Nsoэ}D"Uv?:1#5W �!;lxȫ?svnl+m)OQMƘk@ۧ/>hTz?�%{%fG]yWu~ jk޼=vsj[eʌ?ր_9Gϵ�\t箉=T6INXVǘ��%c,:y�[x}GHƺ ?WWK�K8ca􋃶靌{n{>KSE^znQ;w+ުs㦷9h\.QN :|R߾Q1ˮO<�W.HyR7! /s޽Ϲo[5Gп&i̦.bz#?k`u$R3h>BH֦\>o~~Ǐ:cwVD;#�65c=bH$IN,;k�{I}ruOfz.'ڟ64jVّA{_0efV-nw闊D p O �/t?<�7]Ox5ܼ^}(̸a6%lӮ2ᢛ۠.h}/yq�lxҽFUG-+cA=9/rv$EcX"X<h4ZL&L&�rW~skkkkk6%d9xҔˤ=y9;H 2T*J`A>^eY[&7 jKF)RmG"?3M9d1 _&cgL6N[[['6�l:0(N tBLqd&HDQtbUUUx<XMuA^\ c6*D"aYV:^vmCCCSK8eTUu-Jdx�n�rX?0P$U`\\eY---4VضM#F#E BFe=R^ BYЕ؉> x>8e<$s,&N2 6k{ܴ軅fZ|tukK2*A,-Ce$<n>R 4bs˲ \sc18M$IR%@g08700JAf29 vq`*1d 4�BB}E "A0 g2M:|! t=zP/b(!P?4ѩt*$XXT8:Fes^.Lfuz="BBX9SȱA]-k!znw4%Qڤ"�f;VΌ&ՍAyKkƌ!2=n\0{XÇH;zvu//j^?mP*vp0r�:np� 7_罿?[;;wo6f4nj?o䞇keٴ?Nw!Uv/IckSsT˞}j/~;'�ne`9K{Y9"𵩟|y]4WXw~@v_h��pC^yPm.x~[g}q*=㷯0v?ܳMՍ_<Eekot$Ԛu7uZޙfɻG:>v[ʣ/}h[C[~lUַn?;0a~ƿ�rOjiyK?s[{ =9�Y|>u<5Gbư[l >,$~}񚕳g=w᮷/wr_咅t̀6Zԏ(eWO}x3�?`}̢_<]��`qw|>166tQ|?]ēg g.a3`>k.{dp^r?- \غÙi߭^5kۻݓ?M>zW/xqw3yCos_3[�/>h71ILM\_y}m{�w<W݅Kz.[r4F|py6~ǿ|:K:6orsASiP|z}x{9sµ|1L(FẓXwß.^u;o3r5{v'/�g7}@.5a{*ޟ7 ?pM<zu׼4gOrw�]\l{Ɵ(P i3�*RI�'fTe1'D"LZ}i(_KSX.K 絆}_űDU#.MTm( \?Q0~Rin>'5\.G45466@6KaXyj^TURWm;@>#Vqe 4֬YD"JuUh<fElrԓB%&)+|z;-XP)]'9$@D7d2&`$>,vb֓¨L"JMcpzBafHâ16BqLFD F]vdW^ݺuׯ_.]zAS""arRjs?i@wXSP[8eYT)dYdY-��0/м qFz`7TJMF_*GR݊V\>t'C3irJR!ϠR! >^ԋЮa)`Lˢu*Bx'kϥeg<-Vt6ܪY``ׇ~n-{g'>R{tF yH!ϺM? ̅GaFt-lMg#xx23.W׏oެˇچafi/oJg!,D:~z{ yK8o߮[w>}qYCoq;8<y(QRyF#㪩n9M d=}yӲ/B=b}bI#xMk׷AEpNJt5_ξ6[͚V"6}0KՍ _Z3.[9(^)oW3yQ G9OㆣrӞz/Wڐ'Ry{C ug-b&v96DC6P)0㉧6rG )W>7S�vݳ#9znxGydsiEg>tWNѧ}ݥg@I *^uЉϿΚEmD aWZyuߑ.rc'xvg1+��<Q[ףngի㐺N>}Bb <fOx+>o!u=qU @~wǮQ0 #~6 �DA}w;.ޅX<,:ou0_a{N۶_1oK{9q_y? }[/SO~ew:G+~{Y[SvP7h,-W1ˏ3pϛVs #<yC½MƎc&>ζ~=vZmmnu][7pܑ=zN<Qu5}i/\վkU-ϿG^aN՚dFj`J?혃euîٳ??a{=btCƟ3il뼹G<sP&^?O8g\.BoӮ7S}*U�'uV[ӵ7Lϓt9mFm28(_z U=@aNo^}'75 ι` a(D83?JUWW[;U]H$u]�!D4#a"ޝk91pKS_((!VRfsL6p b|>_Bss"x,bs75_nͪ6m,+Uw֭[u*Eh1Fa4̈]ӭ;IXp'4"!G+5s�ip2,S�B>M2l0 Du:&P#@X2PIsi�K '܁07P΂ќ!.]-|0y$j557oXgGL+(G0зL4 %%g>()taWv8 GwKͅNʒ s r 2iGcTUMך.D*UŴȄ'1Wp=LŒ R9G e+_*=_*Kx(#7M0,%+Q>bLӶ̈i_A`Z7!<1dL  AzL0lbh_هq.ɚLLCĄN.7ahK1P ƠlKKX`h T"e�Rv6Z#!8B"/å ȼ%8_8g/m�%<SL: TGՓop_Z|͊¤X59ssB&[ܩ`6 _~v޵q6{Μ9s̺mdlkO~׽_-[v[Ns [w\!MZZpnyџCG-ICFj{k3S;">k~ϏCՑק!:U5NfSs@+GYͦ‚{ٿGVckS"p׵`ICsϛy>{\4i'󫜹o;`>qHM?oyu;ڵ--/=D"o/7m*yTury��JB>_�USG[-6˱"�ڹzrF^}MϨ>l&;o]:iD&ΚjXC"H$:衵nӦt)Jyb7+�ub}"a1짿3gΜ9?pLK= Y}܈jê5E?]]B@| 諒^|ըu޿xp,D/kn$jo[Xjc\0w_zG윉~t́p^ۻ~]>w5#u:Cp'l= M_?)-#P[:])'A]_0W~͚f>C}Ɔ<W7C=xP_-Fol%SI(:lTkGc 8p` 8ih4Jc#>� y$g@[H$A46|T8 tV[xOI*PD/FrFnXr�)ϙB$q UUUU5V4BPH y*ڥI'*~igO NBN=iNI}.G{9x#Fݻ&nP zbƍ MMM֑dȷ �.P)ofnݛ,RqG&!7à:vtX,iRJShx&tᖅ2|eLJ"M0H�",!eJinnF.]q~nD糈@ޟetny}A}a))'@S�AZD,s"@.[@|>Ob555UUUݻ!ps%�epB- +(,>c080ZCjj4A�/hmJ,3;Tϊvg�a,7*y*˖Oд]xMU2 $*'UeWC·Sv9}~9=tlmC0STw ї.?y+^]Xt6fϟͰ=z'YWMr1cƌ3zp#ao}��ocG'+F1[~=NU6z̘1cFSk̘.+'fΒm _3՗}9[xG/})=%+1�eL[qĻ?<i{D &;^1b~Kzۭc6yw_%wcqëS߯;nK�� �IDAT҆,|mY7{'nƘs.c=#O�^/i>uY,U\p7xQ$wOv;M(8nGƀ1 Se/+ K^:gIA^L!?C{wj&�FK}wOpJ# .n=ۃ/y?]e8,=z>^}z.^W1 =f̘1cF/ϛBaesk{bՒEm WҔާNJ5啷=ⴃw=~ë0;KJ_u+_1[XO<q$Uۜr٣M0t?yD1]Ҟoa=vZoF<n654lEVlY:ӭg?tYO羺1;8"L /^Q|I/Yݻ㎙k/p;}}/wQ#D/] vfrʵPǰۥVRJK)RrD"Ֆe:�JˏFv,pd2 A!2-3`)ڇ9KȩR�c W(WM)qP4p92B LpA$ܒH$⩤K&(/ךϔ[�XPp1.Sȑs.RJJJ8N:޴i5,YT߰at:MNEwDjkk[[[kXfuZ9!a]dS.wЅ:CK Ͳnݺ�A8ò,˲\8yɻnwm4b9#0)e"$,MoCU܂BmJ40ӣ"� 5"װpr9D4jt]q|19 3POO$Z v^4PIK*SE"6pegC֖e+^Ɠ~*J%RI;)x$[,+%l;J/ òiTs&C(cQ+-l6dr7 Z0ae?6͈e�] "(-Z6HCt2>Cگe Eە<n[:1i/ꑧ!@)䦮jeӯc[On'-7iOPW c$&G|NP(80Ɯxvs/~q<n9<>繿XmZo<fDI;.Z 7yg\~߾rw9} f=Ow6Cʥ>0f]X5ǞOQNkYSo<v¯>s_.)�2~q׽ӈZ3g.Xfu:4k=1К`ms̈ %w|oǟ=: jw{'K ݿ̚s΃~D5{yʇ;~lQo[]vúE3rկv= /g[ӜcFיVu'^vB R8�,o׷mGv֖ Y;׮ŭ}Ou/ƭ|g zn+ȕ/nꂝ'ݗvW#Ϲ|77^lOg`Y.٘q[Z ?銵KzSI?vuf/_a9Ͻ7_B+TܙWlbmdz--~agO_u sWuW>?#jc1X;5w=>]qSlY�'4.zk6ԯZ7@d߳N֯]ڣ!kmS~yُ,Y=[-KyŅ]GzA ?K׬Y˷<ewb?l=vZgv{{OukV5Ձy9{e>~t9)]x{ 8ַ^1ˮgIg&klǧtγn~7hL]j扦O:;cm dLa°8u񺺺x<嚛 a 3&H<aA/Fe[s Bij82iV4Y(zb#6c*i<:H}4THSDњ�"s\&xL%8FI"#y�#"1LD Xy^ccccc8iF0`!�P *# BxuFzG B~u=MdJXJ;B~ -kD maP !8J>:*W_nXJƭ,gl4 /J*Tu]y,J)Rx<n&< Xz@(g" )YER�J4hRx<N1RJZPiv޽O>Æ ޽;k4ZfEqH^JuP@8:@*/NӰPM6Zz]o(e Bn|#dWAH8@Wv }:!gNecXc -2gh@Ўf^w?Əg.h1(YՑ5 㱉o?bg\1ޣ;�`G_>mn}nɾ?274}CϜ8z!O{o*Ad럽g//uNss}?NMRS3x܁[s:тű!B2Ku3gYo_8jy}U;��+hK[S s=G  5WߑwGp>et2]8y`a}ݶ9'nZ’r>dN/O�;|d<}`(@r#OFYC?>pcN'^73k8rm k-m_?yɁûO+?2�^8v�@{2tҫ}~}yc n?'6QFr2ye亮Oܶ폼E[7Ս<~۾쳋v|TO;W�V{E;v0�j۷SdN#\yQN2pn~g1o6[7tI7zBg1/}n;Y#kB}^UUvy$r}ܵ+:tge�ϻpG FynAgl�q|SvTó?u�b{?&;À;ܸpN7Tp7]ߏ좝{zpo~Ѷ#czɻmůW3 CcuZ~d8j}HqlsCm׍ y-+hڍmF 'ԑ7;Wp#Y5`zūz~΃GLa~}��6>&OI{q4}ܽvs=$~A@(�)mH0X"vE8ֲT<y^ LD9iVzh"�P*BmJ9󪪪B`Y"s<_vsD"ѥKOJ$eYAnmcn8qnݺs---�8N,m7qq^dQ ιy<'%JbO} D%@Ƥ~vG MP);؂#c“@N�SYM؛F48AV(-B!DhJ@- Tӟ=ϳm[)E*yP("0cE9Vdl}S(r= YE�u0ڪ0a �+ y;�hVƐŐҌDqP ]1RP>6ϼxteM/Ќ- aE<JsҼELRqLtZ}t6sP9,<O"F#=9W <IL D&tKaR!} 4m}P_({L ZTk^*>UD_~sfO?ĀVt5;$0 ΅9+,"&/5z4Xuґ!*5 Y0ڨ2a;I%s:1EEyV1.c|`[UU{o6;>#+r3|[lov6^ٔk?粅LK˞۫/)|NOl?= ?P-gm72zGc}PR*pciӮZ2T18c Bdu]J*FtzՆѣW,kjj\ٸ1EX(K4MC| Sc9 0(7Xͅ tyGm . h4JE4�/j1j6 ljD"|޲̚Xd t! 5匄��(D;!0�v4d2tB-ifqSRJ_)l6m[0yR>aβRm7J)r:A^Aj+L*,I)p+4w]7AH'`F|iB)$D)E͋Bh-o4#0{ uI0pRJ~,D)P RJ6z5D7B!*$1@}= c9Ǡ |k'ZT$ˠ>aIQT>---l6\Fw�\Nky�Q\PM9i*bev ޶R P bD:D_J2˰++5E!'<%$~e-`P SgڮW"bR!`(P6^ P1&R ,l`)�Wq��ų`ys:��i~džPU<îxUS_X5Gt?M ;gtZuZ4@E cm|"F mT%(LRv7NKDhd"w,lkJP#i )M<77`J<`q �(m۔q�ezq='dTp(*>:OIP 83P1TE�,3bT18@q3M3(<Wf3y!a[]kk4|s\}v6 xkYHI;\Z rR%@�DI)X:sI{u�`[^>""G3 @I#"g(}vs)L$�8>#mۅB0iT7B#4˷*T#Ox**+X[!7J!:´9/1()²,FQI!4�CP YÀ8g\BEy@UU {wí8G_)_&�ҷMNbZM,FCxD̶muz9yഴ9 r@q<Gp* (R �XNMAU/4 _I9G� QJWLJWl+'R|hwivo-�ʎ "Vqb[ F:�)Ƙ2 j3@`WmG4 ȊQ-A�uKw1zƌC$㊡BEf)@d :GrGW4}_?;`l{kSN:uk;??H__uvĢXӈy­i6%=sD,---x"8d�ҳm1r`d08-!,6�]_&*H\Y8Lao #fq\!yt QvS8U755}1'IQ,ˢh`7M3r9&ڶ-)Lah,D-)e&K粞Kijj"۶(ם"I|!If7A?K(`3UaN�lH TrCC!l68N4M$4mHX{�yRJi iXh“>lߨ)C 0\l^/q*P /)MieYBZ7*! _ Wv PU][=zExbң3!:5˲h(4D"Tqg 8P=sQ`\A!GJ9 G뺌R]S@}F8dKqY(1meׯv xs Y]vʞuB9,EdD*3mП(ܜ-! R: 8#֠o #ovZuZuZuZO6pDQ!hpjA UDQTw B VJdfed�Y.2%5*6o R%cJ\.Rq$"ARJU>ҥiC鍈Ux<w]׶mrl$!> M@q$YG5HhX1K/;jFIjcV4VmG\uBkk3fUUU%mb\'tEc<0< 6J2�yi.hDT*_)E%I`Řyh4s],~Ճlj'b]t1 <jf=#.@J)}Ezx!6)CK b" JR {i鳀ҰL5, .x>" w}8 )jȤs7tQ}Pl4FD9�<14z(s2 ˲"�LC LJ/r&x!c))InP(H_{b @hRs [DOY(%}_rD)`TJ!�p`Lpg��E`1Ɔx@� �R 0,u\[J|4a*0uÎ[p=Ph'v <>h A*�d 8#�S �8Aē %mfL,|b>^M 1AJ9KeJNNNNV d7mWfsN|>OgJ>]Oﺮ.DtAPH&fB(iVWYEt4!/{[WBo\i*H m[ *xGЋ@%7+6o,-8,pZH|�КgŲAtAb"IaE+g+[USSN4ojd2J)_aPВ`a?}C ɤeYl=۶)o IDZ v�qEX(%S9I d9qF&-WO.q�L.]:#bkk+ *Vو&}_kT§ u> `O M`!E93v+V@Q5�jALpQ<T!BhQ2�Y6\0o_ *ۧGY( E/JĢ|۴|%Rʗ X$ PJg�RJ8 Ԅ)_Js};%j#��h !;H(?0bgS O9y!QvdtCEDE/�MChEd VKbYȄ[9_E 269irm/\@@ +`.\::::mD"Nrl6J(U=hbHԶ nܶD"HLJ4MӶB%0 a7R1a�� hж-.mX}|" d`(R8i7=UJٶM\D$x$!B'q5_ d ΐ[!3aZ}T+"X,&=��s]I)B) !"D}sHӒ/͠@>�40:- ߙlk4bh<a$!,Y.Ѩib6:9#}FD<i@`�� �IDAT a�BC/eD+FU(;=mޕ'g!B+Y,@D\: Zœ2 lLc=0S67^S]2\&<Ԧr�}қinAAj^ZŐXD ڱHJ{2 �BJJ)]Ln2\a��LaR*PNEmQ�` #P'ř 7�U&ιN)e6D(~!^x=6*^OdyXGF-@R`!zS?e3*|7f($J�0@(Y<(PY RرIuZuZuZu-C@ y+:Auu5m|s^ݥ+"%L.O󹾾K$n+Bji( "1/P)TJ)`p<9qu\u "/nϩ%9igBP[[#BA|TTWx})c|͹ҧDn JLS"2(5�@*H/x2+@T31/� Hy8=RlL&H))(�{۶iZwC*gYV.#m � zR9ϤUUU% :s+Q2"�gL1&�f��#c/H'$B+UN@ ÄRТ4т )��CX&D"„łsAlK\}4 Lmh([kQ(}h@bHQ 92) Ƭ/} 50<i0sf C*x %XHH$ 6,VumyNEdB` ?xO1FEG`)@87JX!(@< X BeAχJGMY�+x,, S0[LB1 @�pv[֠(U<*!Q$U{K08g@Dүt ȋ t`JuZuZuZuV0 @0А+p|]]]&kɴPe2ax"D R Xun 555a��GJܸs�� L9q<� *F8#9BJ:XL)?=%2L˲|CD{mY"CL!\.͚B�PaR 9#kY---Dݛw}/NsM2m<�0 )/]O4M@|׶ xPp<wАv4*42i8)'} ښdmӌEJ)4)4X"a|qb�΄i�DŸMæ**d!RyQ*�L)ҍ%�� ="A0B@0`dAJd* a1<7 IӰ�A! ɐ_4G. 8G A,B1) |% eT̊ 'i(ھ;NS9o /PS C 0 "9Q)xyQ"9P|L $&xURB:;CCBO�HI%}  iZ>(A�Xڸgt&Ҵ[ad"8Jn!s;S M+�JB0]_j & .R )+ V$�1B!RBu|1TJ$Ua�}�*1dL_�ƩYVBB~0>L?G=9a/)K#1 #0x=ׇi<O-�0� !W39TXy/i"M'*�Ί!"(%K*vAS+`n#"anBpƽ yWjii{hv2x((Iiܰs+E7> 0Q CT*Xiiii:h� B�`E"s7mڔw4,FDێs� A .ɄaxBJe<ao.{|!N'h4J{$w66M2My39@bXIGQ(S|;y|kk+!O*Gm,J$\.j&kW-.5�@K|A8rR|@! #"CQ*-700 !}ʿLu6mLkâB{D\J*8_ZcV ;ud4n_;`g'�T*,k*%1E1c 8/E:SPoZF1<`UI\kK! Ř@}?.E97+4*daUUkn9I$!) ((b@ň0ʌbE (snR@ rp{}?ֽνUq>ZO=T:aN]ksI]͆zږNgK' AW{}Y-?П)c(s8rdsn;sptֵypU!aVHҠ+~ɥxT@3�d28r@cm$"jDmLQ&YBaRӂelO490^.5P;kǻ&vap�D7L\�Ǵ$[z &[wa5>2Uoac,&T^Yb.t#n8N Z̅S(!j1?$q H)8Lȵ֚He]Z98$M۴M۴M۴M۴+[#R)EIcccR[tH^3.2 3HuJdtFTg{" u Zq8;::jdv{q5GD4M4Nӂ. .L6uVʋA�\1FԈRʪR|$�Ls)@q8Zk`=md�JJbRJ.�ԠQ: :j F#=u5 $!3FZXB:E8.Byф$hNrhɯ`B3lrn91 $frA1hv[F�h_)Ll0]Ƿh Kcv Vzpݵp^wW0P chL EЄl' �Kɚq\6XN%1X#K[~^Y9'YM̖ZDZӺ8c W4>R R&TXpђѱjsvwwϜ9<2c=862ZPiH aL{Q!>Da&Â5dAt"=82[&e2" e[QvzOdmjrUۀ-|px #@mkl� S=B0",S~|c&X)!%<wttԢ8J18cҠRF�r@nIcLR6B2e1)K�QQdS>D[_2+_WMϜiڿcXkl$\. bRڟcZk.0MpyG^>wuZ#'yK)08BQ*2:C$uݱ1*4|r3C#صV 'i TUm R<yĂqhlXGcrS{ � CJoRT|hh)" UquFO*QВKY(RQ'Is~PrJǁ6lʽPz sOD2�:�Bw ;@^M'ºczd?A& :N?sKܽZ^r˽:FΘ̀s9: @tR8R6?a-ޞނ&5dP":Sը*#LUz�gBN+/W\dYm CVϭKu}͒Xy5y(�@+CdXwNN?K'aZ H"4i>#0V簲,L Z5h&5uG7& g5&l9wegHK?4[ ` cĿм d| :)RX.넂Ջ/ B81�Θ0ZC. 8M$M4Sd_bW`Ͽ2[5m|9OiX۶ǿ"SA��9-WǵZM+ p)j1ƀ3�HZ i\joZ9s̙G5G-[7�RXCl9&Rʤ)2|ףO0TQ=ҘR69BAw98kiJ(sN{@.yyQ kR#: R$SzxRXL:2�@0ɥ0RbXc(Bx8+3e@%I81.@k)tc0 ab!iuZI9F9GD,sCq`@%@#f2Ɉ`f[LcmI2l ,Ms. Ik cϽ#O׉ NaPK.Y$q-Qiw]3ƤJѳX8DMQ$o<),?0)hISf‰6c*Ś,)FF+hQ2;" N5i0!KG7wX}}6|1L J:L6Dս\:fo%8XNńLaW6UDAa3O=>Zl{G/|^QؘmZOg)_&5{|v�Y#goc}&z9Rh a 〈Fc�R0H8'ܑLJG4F1$7 p@6UɴM۴M۴M۴Mۿm#Ur*AݎWò1:UWGw�s<7 /^xV=)HE>cLɦZZ R@>/J�G�@)UՔRj�<Q)u}>27X.SxB$I)$!`2RJh(S<c~5ÕJ= VCH\.y)R*w'(8Q!KǕ (cl6�<c.(҈!jrg[ gZkck4ԳĽ>GjÛi 4@[f2CCؐgΩN[=10Nߖͷv㞯޽'FFұ2(C)hhR.6-A6|bWШJaHq)N VH㌥iHGsۯ3�`VJzꊍjPZ+cؠ c̀L ;e HR:Jٜ̄״b,C].mϚ8^- �F l`2*8#MmÉ,ʼ�TH3Oa-ie`z(LƄ%*q,\O)Ubhi(8gqmP8we 8ACu3_M۴M۴M۴M۴Sc 3�< 9)jq�c1d,b Ϝ9c˖-֭|w kX,%:C`q\:(�TpORJ4}k~.^-iasΙ*MբKRkqQ$g@1QGE @'8X(? u[DGG*|PHXf{ _g(�T;%YVGGG$a /pJXH¶]ߵT� wcpSG{ _i7ZC HXyr$j$̉Q1<XU+n8kwk֬m+U ҅$N<WVM6~4ũ(>N%aāIi쉵l<l0LJdea8a?͖ɹ~c#}s-k+x]{& VΟ:1Zr#g�4D9F�Ua16^4]R RFelKF>Wl!M4I3fdU GxshnǴeϧi8`r >uml2iB�Mbw;ug_eO<Y#ʈ1n 3隮Z^hL �SʤZ;:&I8M$I*3U2޴M۴M۴M۴Mۿ}&YEuTNq  A0.cP&QVV6m^`왳XrCJ%]V) rnj{JD\J�UD WO\׭,t$V\Ά�S(\cB)`)Hho B@$RJw(us 2V�GJq pkvD ð\.G՚R>5 �J Ub �`RTʔ@"�@GjyTA*=(]EQE 8¤@8tq)g!n'źֲv.R{A @ c#9H&^QxM+!.f̄t<')y -S?$Pת,̳ 2D0¤@"f.1,6dJXRKlvdMԫH(MwwGߖ~KU5Ij\:BST<Wz +_� nYG1_fs"Ev,z{gT*0 c|^A2(q"ͮ$ `cn7M4ϬA|L)SlLIK;W'dsWͳ] RICa&T w4/R`*kp뷌Yi뺌ZhhxX"e0{̑B*MUk(Ur絕 B%hdp3m6m6m6miCZZ!c"]RMc>Gm*8qX\Îk׬`/rTXM6:IS]16J)Zk7!}�D� RR@_EM-4IE  SATX d&_kI<g Pp$<NSQ8J`qpL҈sH8*gӸV6AK:V*0i|zr9%zarMV BQs`q#!q41 |ߧ�yclddmQ[l` =ϣ?ӡrd 8M,qN4-HxuƘAc'1Ơ-MT!,Fa[ьk4SP% !#hgAf�X3$t |<*DrQ@BEg$@8)5ȖJˆ{[ l=0L8%�[.Bq:UJ#d"/2� K*ǀ0 _*9I !p&sj0C.29p F322B/-RNA! fiuA 0w0 {ZD Y2\@ݡ㡁L1fX-\%SZ֠T;lj4N�u]%zd6 ķZU{;j-U@?C3@/y-a>3^08Pr�wf 4U<mK'a\4cnAfӵjUΓmmmZ2mڦmڦmڦmĒZ�: XGk tFIG&5~yel, Cs <"/ !;jZEijPi?i*JUJR:#$GJ8CRZGI,ֲŧݼMO8N麈qZcLZ}It ( � Dk͔R�¦tzh켅8I$I:N.9s #8|VBP,$8MS"Yj7rVDH)BaJi P ={-2b17�� �IDAT:�JRQf nDI"X%x- NF[eqT8xog592#(c@B(oN0Dǎ#"EyΒ1q!,`2NL: !(-^V &r+gٙO܋['RBƙ�Ƹ1#rD4Po�AuV�&,1Wϫg4IOlBd �jr7B%J.53kvԷLmBv΢d�Z �aN|26~{IG<{L=d5>#T h#26) lP$m�*JĜqq h�Й#DĞr!]7nRȗv}8pٲeQqJ=C7z@E!,ڷW6m6m6m6m܆m2�)ɤ8 D\cyW*;:IQ2<ð<::62"R2`:`DKӔ  ÇZEߦ�qHRZwi5vTJq �r~ ` GZu6`Q-DD8C@iT$bE 80!!eXܓ< #jbۚm$I8&A0o޼JRV+ce9?300022AZJӴ^,wزeK%y/q]}Ƙ`R$iuoxDQ>E ˮ6.*#JO81Ic UdLӄr@ dY QeOO8:@@z3h 2&Q 2ԠQ%iCD G7Rz- %eo7W(ʴ0 e'쬞k]:wa1%83h`!RԀJ#h1ɑx 3 H4��0M},+0 Y 78†")cf&|SຮBqvl)6=ڍ-6qԲA^!8虬UMK1I5k ]rmOO!O`ۖ=6,5;,ȆPK8�"K5wi<CJXm<Aldcu\\_ac{{ŋnoX?wsz-[s֭}===QV6m6m6m6m�MO8zZf0�hj-[ѡ\qcJ)G.DDB0` h�T+T\n8�(%10m (�rڸ\j T+y^P�$I\mkk 19".k1JFhhelL)@L&ϧiVkqV0 G!DWWW[[[ytHzb)%*ݼqc-̙S+ע(I*tA~61Q:MSR7!6 < 8o}_R522B=xytt0 [ L| &!ƹ0�9 wh#�Cn�lr gqY4m-TiV ֨rhDd0ƒ(δm|ژ,턖Nna5&$2*^rl\ �n2 eoHTZ;`"ؐ0> $l$rg!;Ђ'rOZ6bݓ=d:#d2o L;igc/kFZg~l> cL0nW$R?"&ISdyj|V4xcLV3`hjaMHi6(J�0[TϚ3YM<ct-P \9cfo$[{ft/]ty}}[]azlMX[.̬m-L=y7?y]{/mzOA[et[?xW 螙m6X/.~`{8ONttӰ5wk{nj[`<Wfu/om :+{y/_{Qը4&1 /W*qDqL_aٯZph\tN˗/ZCB24(8ʣQ*DbcQQAI-MS cr9 `�FIT*˔.*sA[[[wwwoooggg>w]T(xƄ`- QiQD~PA1_]O0.M|c/Y'm>Iry\U*j"#8J۴yxp(B./ש2J;B2HXyٲM6T*r*9sfP ':Q��g\|>ӓ$RJ] k*d`I380q B adddxx8V%IgKR[[[PeJ%mµ ͺ�Y#JtبQ\0�CbHD5 /q& m9&Dl+ !Z622Bx-8,G-"FuC)r!$'4G&qX̟ŷ-"2`?(;s�0(�L3L�Sb`!gX/@@e 2F+7` аπL2䍢DA8;i|, kf&#]->hm zVR _'~a#B P_whh( dh;7ƅVmr񞴔lF\}X,S@Y`N(jZ>G:: |=Ilӎ.9cV (e}ڼy#_)l^ל=l�r|_”yη 1�Cz~=A=xkk:1xQ䢺/c_>pnl'GiRs/vauOޒm2̃u/UXxW<vڦ_^#}ko~lpʷ[ΫG^,zYZ~e{zz rByBPsGFI<46?<842<:66V8EQ&0!4&rJr9 &y:8c|==꘼�OsOB  4:F0MS*8罽sikk7�0$@^+aH A1���(]]]Q?oZzƘ|>t֭[6]%j9/"T[[ۂ f͚/a Q!ykv)@Y&4Nc E݋a(*ZF Yh`"iZZ iFC=r\�c`L#45u.!<0y-l4V1>Kg`y1l_' lgNiپH"ڠA(!1fA6Зu � )lm $JXB⍺ 6 &C/ ~= {IKMCmVdW˞ǖcUճl陖OwyAli?)׳fئ`}ؖ+ۉMoZJ H^u}ߣ,.UJKl!Ueq]v9\y;n9N=-7}y 'V*rA.7y6ܶLmEoIgį2P~ХAw~P :O}^7, _u…7l0[n1zru]v|oo W/(ŹOC 9{N2e>cY?J1AӔ 脓?w׫-$R}g#:toASu$ G+N>qwiy9iC /鶵߼uƞ<-š.焓r^̆>]ٯl{M/삟O%7\w X[o�\Cv{~}N]+r^29w~gaY`@f' $J8hcJבxn.*9s{A!+ h9KT=6C##ca1\%7]0^;gQjQXKؐjw� h>SɿDEck�|>'8M2 u *P8I4J2:<262GQA, A>W,{zz 4zA pQCR###6lhVLMLCqq-5L)ET(Z{#$$JRrS~p\&a{{{ػjh -V;֖k±"d43wa�jct&IPU⤪(MDGqZZU� )@&[) Z h!c .AAS-ˬ϶e&~Ѹl'@,AVhHz6t Ǎ̤RfJh#Qq@ATҤ\)*pt#P3*XSl?g"b$I5'-eҥ~k&e ]Lz)ld.d|fkDZ�d�d6X6R�46%lo' &Bof"Up]R UkUt�y/\'RP*|; Y=3w˗J%@=sVw 9G{f5qXMZX+ Iݩ Gw=sλ9�wvo{>}? 裿̱{~sοc߼SϹMx[~uգ,gGύwr N3Dw҇w?}䜓>{K�_<[O{hrV_SWzy0ЪK=S)fOJ7?z2>㒻?>z!+yywe))nZ˯7|Pn#}Ї.Nr1LƁ?\~N}k&4$ĞgmbgCu GK~6t�])|ja83qN<^uϜUiOK__V#jlCRˆT&h5֨0&BgWOPȻ5wVTJ|Mz�7 ql4IT L 㨮R8MhI;(INRY!D0·TCp(=444:<J\.G* {^t)(s9#jfPJ nڴ_NӸaK}BlhQ*=Zmlllx`p````ppph```֨GF)/�XyFiq0"JJ?1hp{4 A(0VQ>}?e$j-610 3Ru3uXSOm\VJ4NM&}=ד'MCR e {,S@CMڒA@ AQot&뇉fŶR)cI〮Cq)({5r1f�h(bRi9@{&NzrDC3v8vTf/ނW[W'bo6a'qXD6.C0bK<e(7FWFo,b?3fٙ bT MASlƁUI9EQ$. Jp~(JV >w#=ԷqSO>ZUF6{vKedoSzK͛|ٺev_M]G]tٷ}Kv8;#_?whSk~TM>k+TO?Doخ0sS]^Yfi#.Y.|~5HݿK?xا/<aˎ=3G \ӛC7n~8`3k;~ON}ߺ}fsЫf.xo3{sM_ϵgoyS:rQ[qA|-W})nydhsۧ^G]FjőM{8_M1_ҟo��_?E�M8^ůo1'ʹ5^Aߩzo[߻~ ?1]wG}讏oaOPڣ?~׾<7߽~I&jrc뿿�O\wSʵ/z.{2�#43:A>qGc~[n触}]gϸbxk/eOUBמy��@?=r7Nzzw>OE1te [upħ~A~gq<y.A~n'~251~?o;t^ik>~nᅠ3p\Nz#'zms)�`uK:}сgf|zǖd}X3/]{q{.}&byŏsvX/<!ϳ6>ww6kW7E#� `goo}�z<-{Xג%әY"z >��̺T|ϚVz;oc y뗮F=E7쎜w,~YƪŁۿq>K~нalU8cJ ξ%�o8sw\�tI[˞>彳f CvY]cag]ܙ #]�;i\7߻he[k( ~F4BP $ra7 dM A50Ce\:Q&4~|[{3KR[=WjPHÙ PhDiX J*29箐 `HbQZ0r$թ"\-7JG$8:I bi `8T:B"7ȌD1_j+K8B2*ֹ�2\ՀVs=/�X.Wn.uJ ҐVGXMၾaya�JaAF#X өa%w8�)cgRԢ\Pg֢ 0 2]!2DL֬xkEmvvBBh9p$ŒhTVh*!:q 2Zwe}פJ)**4Q )\%t|ף Ʌ q]Q'F PGa2V+itjBhh@_ Υ3&p(|sɘ@dk4?K?$L}b،qp�MG)`bj� ;@80"=w`P2!40\sЌk.Q: C&Lp� INRb$Vm' 㹀?T 0.@04MSb@Z8Gt2޴p;'lj� @az(4@- d 6^`b8UkHAD�#%'�kT `eX`Q�~1(o8�P~1Q]1˗a#� 0R$TJoS(CZQ z"q$Luʤʤ I7pN9΀1\VTZGIb#rDZutW\ȃw}^7?ڧV\շ~m59o۸VNNbX!=��=r-ï:nyg"/+yJ<܋?횤7q7|6ݭz i%�nUT��;/Wdn{lh.I$/K+?NC~k7ȶy~s'!&^ڟ}3 �xiў쀁o|{1kp=["lΖuSu}Xؤݗm7Ӿ �� �IDAT;yw;6q|go`7&9˷W?2j�ڍO k�f槆g,;ŵ^^uNϟV묟䪛>_/v|ξ' =>?0�P{^]F<|c}^sa39�w0}sxst-_p3ko9o_>>?޸:鋿bϯꇾЫwyU7~-+Xzܽ7oU�[o\C>s>~�OV0^}߻g׾_^և#Oآ즵w|iۿþ7\r몧{؏/]Ww~wy;qتn\_d7Rwe;~pg{΃_tӓOV~昳~Mq8Ўn} � kW<vEkW=yo})@xӿ߳삿z+>I9<ڔˎ8b} gwRNϮG3e@?}Յ׭B}w/=m60[{Þ_O#{dq۪M}O_ny>sT6fO6_^-~Zǁ6p]h�; wJyg쟉›̾ƇU6ɕK9^bW_z ʵ<xϾG~ꁗ(`HL3 a-mLqZ�\2((�!Q҈ pƅ#./\!W(J=3{{gtvwr‘Lpsϥ�֢E 8#>HB')si#i)!Ic j縞y'*7i::eD#"*MҌq'Ql4N(". #U&�@qMZ @z֭ׯ\0cu{g}^jKj:j#Z-I֨Һ�2H#DYE(/P |vl�Xmi(+PZLiKMS5888<<lNeXd%0�Nbh� EacS,9� F<9Afe/4A;5jӲ풷 \r0!D "D0vɳrAo۬@v[SY_}Wf2.sY`Aj0=qOHLK̊PIcϒˌd�tA@+(YrRXl<�aXǓzXC2�݆Z1`)zg.?wmR1J[ׯ]6gV?ɑ~0h]䂈_?9>�+&_(fb*cSQw%g#s輔R`+WbXKʕ@?d|w'Ni /65\A -8pN\=㻾GYfd3o/)/9 sፃ2W]x@~S׽Hcr'47y nz{}ջ-.t/m\R'ŝx16FK;kl4P~`ƥ wضd7|i?}E5y;.X[.:U|"/rg}bK}{˛gzo]zS,zëW<=d vZ:>L pt~~{zw{ۧޱnpt͞3k7:U;̝ŷu<6zou|8w޲ؘ Nz؄֣+o�TYG\ł_1>oE e߉Sp {'N;C��9=sv?]GtZ:gʇW>xķ9 v;~RJ)Gx+�/Z<wƌyK'˗ jG6nǿS76^w5 qG7_{s }.|CcEoߵgc?s/O_^9m{-ǽ~牏y.:vWqE߲7Y|Ƒ~C{|V? =p1k:e6m/ڶ:d,]W?v? X]w;^<jaj__z|#v`SqF~y7޲[n,_6z- z;~'doL33{z޷xw\>C8iױG@-̙Áŧ5/On6~dX}S@lc{Za&precL"]܄�'guB;V*uwtuvvwutuvtuvtw葮# R)PaWj5.E]΀2:Qi&J4*PjXԪaQ ihhmD׃ApAGV!�uTqR7S0cCdzzKA^Cq9a )7:6nϭv$=1s�ƴ1'ÑeFf8"3 vl`<}R "sn͋+eDe"UHYGI($� b'] DHo+3Aufې'6 5cƌ AZN5`:֚3M=cODSMx:1uZ-HA �AC1h-K跅>T=~gfe6qiS[[yR`/Z[ a t"UiVR@DJ1zDAC-Ek1uh4t$VRĄP=1.b{jTٱpٳf ŜrvqQT }Ͻr�XXJKX)WPzzo|;yvseoNz`߿oŊ+VJ|T6j .^lA:gs+VKwP-W(n쩗Ckݴ9a(~=ziPZ<kRMwx^nv8^ɗoWhW4cv}n+Ce6]?h]߻ײVޱe zj7?xQl:9}՟ovGާ薟_>~O=<6w?} jX޴it7}{]j"Sx j�ۊ-s'7٫x`דNX*�&{O?n-Gǿ>tI;{_~߰)pɑm~K6%C妑䥶bX ( q-0[6o͛ߌŢw_v+VǷ_߈/esR/]߽k% \d>nD@:>k=y/>|k:28˛j= oϳ6ͺq^ сqJ_Ӂy|늻[ZF {ş:pͯlObf|An]oKA٫?} {ڊ];ⶄ'1i&XdAppQGο7xCS-7]wңXܕ<oZC�`R"z[gnDž5[vgG=~en[ujk'(u7x8X "u)#q|/5oE .]2͘={Vo^^>{u+D(S4JR 2�^B!x0("ApA)_pǤq ιCZiZ644 ;::|{{{T*͘5sɒ%mٳKNT?;nK 'Q>2d$  kL `D" !0B :eNw{LwGs:@}gvVtEXN$I ,S𿗢$CA ⪙]c}u\jzMtiiI:<( sHD\;;Hf=fT y c FJ PhZYPDnVe8EJ#q) &ۏp3FN|9 ):Λ{`2"y^{E%t5 -G }U09V~uaѭ�=H,~?)DIw ePpTYG\ @CD$!"Y"U7-ajΪM+jgƐ KT ${4Z-˲^GeyV%z#M-"B\l7rb.k٨a^u9F[/pՇ~kr|늎u֭[w~~0\�פ~~ů7dOlOe_2puzӻ[֟]k隫Oz؞&yOy럙_ͫvܟ~o#Nxs -;7'O{/x!˃rZ=1+@|m${ ? Wuĺ-W}䣏?زK|g ;oi/lvĩ#^}ڛ.מ Jvݫ>Ϝ^I�O~gg^zGnd24+5k&W{/^C?"5OuMןU8z;4?gοdS[�s>?t綊V]UY(K_}Uų=tˏf=vxFmվ[nݺC,yWtGwNx%ٚ=v7Mn^ `HCI/=i}럻p9>chͮkۻ%OlaNn6宋_I^Wo}/|<&u+6m \-<Z1mmwmSCn}qծk}/崳쫿˭ ['/&0ﱛ|-&ٻyϠ=vEkO{<_/\s{G#E/>qXnP`ĆSvxQp8J"^DXı<szf}]<Kl1MSfH[C1`dcj==3bU̮^3b&Z&#YAÂ^/uz %Ϡ4sYg�2$H^:sLdkVkrjzzvvvehRRSY[ozmPbT ^4߃=7 Y߾ mÆmݶ宭n۲}~ &(!'$dk&n kgW&&z!d)(0k3D4akjf]&&&Zϼ(Nɜpa0F#j|>p!P\i<#Q!ktr-,,t],C 3kT@xY!!0J們?~1*DYH@ƫ/]㛇fED=}BE?(ЃhC�r M Ca2"x(wUPF*sG̽WZaC,ql:ZDZ>;VAeV|Ap[Tv5h,}#H;Q<�jikwsss[lٸq ڼya~^ czb5Bch~Mey2bkz{r$u՛W=jK{) 6v=55=Zͩ{5|ϼo~d=z4g�E>oÙ͎{sVwW}߾{B.6vZ?u'x?ė?w@g|gYώ '/y__xo/z`>�3ѝKK4;谇 7^?>dW.- n3_Ѻw~范 qVͺ{Gs:[:޼?o~zCm�Տ{Z#;8)#t~_Ƌ>O8~ynM6ow?~iK_|;wiC& s@k_.|Nx ߻^mEIo :[7l0xKO_?a;oُ~ugv W>[>-6z]j9uzӮ9唗�/-fss=\>?y?eÍ?t3_ z;.;7oXՕv/yWʵo۷f|ˮ%KssnY|[zOoڋOu;9lO @qx_~?'v@xK{G;6o^O>ݾaӆ6=7ϞO^pwYѧz\wKŸ<|߹?;nE{<'L#Ϟwuq~/=GE#~1_;6q]}ş}kWxM?=NzQNcf@8Y xO8؍\o^~iǶ�'o{OxޱS+.]'\Qv?[]m}=uֶYkS(*/# XM�e9} =9%mmv"F "M7j ֒1hĐ6 Eck [5ZٙVXrf劕WmXz]fW6J,$}>Ͳ;X Vkbj9fm۶vm~wn9C+mkZ,Tu]=[2f1ji`5M-woݸ-[,..Ja N $R|Ս\ǡq7o馛n馻gffAHY˄#DM.%g#T̜y@st<28G 7 ezFMDFCeF8P_g%2Wpl|[eeH#nx8(]Z*�KߓaoXo8}d TFX^=1RPq0a 3;Yq|X~s҇G:�sniiiqqQ>]_򶨴zE# a�?B:KKKz1sG{. Hӎt݅,͊"HdI61Vkν۸q-߶wmٲ i;rgiDjy ͽ"?]77h*Ḿgc:sW<o�8: k.:U3{Ob_Ͼ?~~^N {rԏ_sLOR>;߳>tY|>;Ӝu7~@foe]u[g9 ξ~!}?h^p�ҭ?e?qNaݽ'i???Vl"GO~'\asYn'g揟׿s|��O߳ 0^}xbi^,}E,٠¬�� �IDAT}>vz ?cO?p^Ooxb�~tS[�tIZI_Ì}ԟUsN8dC//O>'k><rӞ/+׮=#xX=_>Ux;�ѧ��|sc\sW>=ewj<M}y»/w9yo~![N:wJ_d׽ O5OovVu;4:_^nw<{W<_G^c]v{/ V#_s??}�C^/l:�@Qo;{Qs_W/@Kg~GvGsTFM˿w5wfPWq.S<;fAea7>8b}2 ]?{O~Q/g~?v\$c2D{yG{^;Ƚ۸yGw-Og>~`p:~֦y^xi-}UGwE ��{K_CǞ)U:鉮s^|`ewg{_|% HC�I/<^a/aq>ci>b<щ֗eÕK%d>k մaXY��9'DH�^9y}{<̀r\\>ˋs3"3 &sy�jXsރ k<�f/gNj f.)̽kL5@-D&ZjKݎV|Q̝ dlMDh"QSQ_áBf9>(4bdŸrXgAZMs~~�kMXk M x0!U^&"bd7g[kBaAhfr 2GbL"_?&" ޓIDDGwН~c b rg�<C ,mDKP�cxK`p\*uU;sFha_1y0#NX:&"i:aXPdVSgyfXk4^O#cQDTW՜s󋋋~{(lADznD\\Z0ƀ&%iYgɳ'-t}Dd=OML"B5k<߱c73_>9/{w37'~}_ly{?㼻G>XnqM?FNyӘ'wξ{=~W\|< Ng9x_g%}`Yk}SZ .x˻ގ`�pD@~i +xG4@m� AVEEi@ q~x3,1y/CNXIxF@�`oA@v0!`(5{V*5!*ͦ 9vECeY~b? v1!Ey 3 WBR9ԌeAFh4]V/�`NJ! � �IE:ILNOc� %„SQ}o)hr(" x.i*bqs#W>et@XMlPXG�xGxt0W<GBП z*0✫' P$. aǧB'^Ci`%KcAh3Kp7 H\ !ɰ|>?G5 L_SVU 5SKUj͛7_uUNDPuFC,Ӝ](z {^Td.MOMYkmꜳƊfkbbҳ%5fW;yhݯc#N]Ong3?7Q.7Y~n'ܿ41!Il`&! .1~֚8&e*٭-aD� x9Z�P2�d� "y. �L,,޳sZ"ʺ=uj $ceYs<2tأց�/:fA)qAHAcz $@ь"YթÅafsZ YDXu"k!t^D "g>\%I؜sv\肞kEjvp"^@jZ:-y&j i,o6XNG>/�,� 6$�,zEBR&u:f'5A3%�He$PV1S[-Қmꃪ"$I AW!@c@ώҕEIxcHLؿNXRe@8\P|.<X 5Gy"FХAEigf*;Yn̈$I� `a$r ҋ/ Ce lu'$Ia.eq* *A5 ;jT w=55 J]/6m?T7Pt4M5@N< ynnZ?99}UVu:]/(Pc!z݊wn!$l w[6ovd[ttc\jh hpĘtϿW m}AQ`xΘ4B=7;'u.W:o@`"2d+b SOp%\)JLÑ7yMW4 Qv"X_j\V�!3{!RZ!)Ĵo1½9 Q  bk " BQ4DU ZߑEPАm&*cEƗR33x`yl63E-#˽]yјZ$" ٱN�Pב{�4~sj!6hA˜6 8h_cX*f"AAG3j5U@eIkmwXxWX =oZ]|<+^XWn+EG8Ƈ1$"?$cD둣X*I0 V(+,?8T7;<,0o-+Beq';aT@e]V1KP:ʒ KWPeV٫pޤ~4 *A.)]9%MS՝In !-H'""@NheDM!Ƣ*M #�c/hL<O^'̵Z=β,I� s]]fS{ 3;1iLcӘ4nb a'?>b>X`L?13Rp|4?Ԕh* Jc5)�Bcß1@ �$0CpH0MDiDjfQg/7Hd<Ac@bk�@P�Kik4,9!,cwyksU<^fn6�h05c%CDn$RXMsytREƖ�:KZM"u}p/a"Yt XڊUJj(- [T Cv&l A@} 8�zJ#4zT$񷕐Q1[*f*^e`H`{y�$BZ&K $")N=ܯ<C@8(51ӆC6f}@*lQ>CUP}e +2 aX*x}.EuY?D^sLLLPYj$n$8`c͛7T=, W� ;3v:zR4Vkw; IMKhf3;3B(L#5@dZvgS41iLcӘ~+I!$1UD@PT }87) h@zh3z/ /ϻ�4W& ( p2DeqcigS�tBDkQHăހ,"`">d YhM jVi9OdE 4s>mY/<c/} {� +f,YKd(Ɗ�9U(MXIiؼ 0Gf6e 3SŌc {,TX2RI 7O2" "v:̈Gj,X0 aQ^4ap�AMS6Uewc<{RQs/fYQxD2ٽ(Ciu6ŏh'^ <joD&0<r{)%vA"vPE_^I{z`fy?gӋQʠvX�jyM+4͐:TŸfpXrו+W�dYY*DVÈzkuaA�mm4xkjY͝g^/RLJ5KQ9Ә41iLc!M?<VH()"5  P7c\ ~3 z!KB…=Ԇ#qN|fDKVD �aOByᥔ `hi#"ZM�20Q<{�ij0!EDQ+�^uEXj5& ٤,,:e❈YϋhE>Ϻi-9�HjhLr]kr>"dfy$`aɢpz ck(;)?T߮s�[j5uF�XFj5)3A-D1`+02HQxAD@R,D/B"�hX2ePBDd@Z0�#0Qg:@pTCHHfpG,\sza+ oA08S;ӟ/ou|0G[sZVi LR6Lyz]k^7<Rnimi6۷oOdժUNsaa49VxaSZ]֝oqrrm͆nި'H&�fNY941iLcӘ~)rIqwD<xT+ᛱs'EP<nbժVڣ��T/0-"̹$B"bvFKX^ *PD#DmXx;4D ,w޶ ,TH #(( b1 !0Q#RXmRƤi"JbM4l6}e~Y,!p^jռ{O\#g90*frNˁ(V#+ ǭ2P ,Sa<[\t̜eY<xQO~k,P)Ē\'!0Mro)k_],9BMbe|+.�KpҵDχ{+QCZw|՛ In E :P jXr {҂~Rsp2l#S/!kmu$Go$ZMU�/|Xk(vBR- 5, ~*20qZMXXX5�8ZcII=1m/`@{LLL@l'I4�2 Ve 8741iLc'D*pEr{!:P,5Ɩ"*�GD1aB.CQ-!0N2C࡟z N0�W0=�BHJ�E�Iω1!,TUQPz`! @AdAQ73{ (@$4@EĐ�{AZ�(ZXa6DD6SD7( dU Vunj1=�Dc4D`f*`:Ql"b$VMkPHla5#y2)]1,  pFDQ1bt">\Q9Dg�pcqhHP@[ U0p,KA@A#Das0$|5Coޑ-2F8z$&)N[ީ<^Jd X\\Ldbb"DiAXz$Z0x"3T$[-^mkKЬ7nC333SSSD4`$1Xlݚkg_$ܸq?RѬ7뽥EC " k_Afƈ2И41iLcO(LRNHh1 i>P2]\rp9 Z$1V+wPCt.3!A&G+k GF;qh o?YciJ�� "AVn21FS'Z5�H�.BBN<{@@"CFcQzYC� w>'"^"NTcZyŸ b(ޞ&�,(RMгlr, 幵kE=.@F@?;[0$"]x;3UfY699E1%b Im2 + D+ ?"(,΃HqRX h Oi8 b]`NP#'ʨÐc0 �4ĴXo,hgJ `_eFb_Uxs\9)_{K|ϰ_ɨl 1BMLLi7�< ͯ LצU>wqĥ%m0fYպݮ:,pTXD5t6-h" MF9==1h Z e X> 'xrr~统=,M癵<uy zC&2`enLcӘ41鷞!@PE$IXEj�DqN<kn<"2 I-\X\ȐQIeC ^4>"�;i[ ۄյ�tZQx^@}4fN] Q?k"ȲA67}a&vD4=g'os,aPo4^0M"Z�E�0VG),ԞzhEm+'@fr]^8ω� gd&"FH0bc 0YCV�{τd c�CH1̨E< -L�jGP{UhHH) J9Xkls$F{IQ{OdleYƂjD�<2Zͦz=MX=  \&=�K R7*ދ8@ ‡X$PrEϞER]":< A+ S͖.Cz8ع$I^'NhY[U!��*KsA)z٠提st^&&'D$.륙˽ bga%^" A qQsQXD0r -9I\k=_˔;i媏@X7*;ifYj;`g),rtdމ_!ԛ  N"Bk 2pY֨y"�x_hD\լ޹FKU0??�I\�} 3ڴ {g^m|]^]j}~ [6l߲Yw]8=9f ;'bD@!TQ{&/�҃^Oco%=_5!gG@z@fD$$2dA@X I2�P}Ph +[=P1/'}lDZtA+fa~R|BGA�x(s# Bk1*p0xd! G3JQ8YHADH&�$�uXG[<b^?"e�� �IDATZ0񋂁Z͛'''1i@LF# ��-X4B*ޡZCQD !\�z@i dASv, $($Z1tAT) $"x2trhٹ}j � �YKeV }҅0'IbT}@ZI�c_76fE~R4 <f`  rbuŸ'�eS=PzS$;Pcii9799Y}Dߒo[}%qDYbDc;N$kmy֭ TԻAo(M8i\ Yu 0<[Zzv~n__Vk4Ņ=<륝1ֲxc0UXYiwwv1=P_ WczXr[I-~hv͋ra�CpF2}"Y¬B� zQ C6i ]^elP/Y(0鬸tHՂѰ  c�#auڗH,R?Ekv=dUt*m} 6ND@dz造)�P4!Y<4*{`N�<t?A0^E*x/^LӴHʂ"$tW4K0(2N,\+�.Ȭ '9L<,Ozyx�HiAN0y9yXVw04gA /6Dڲ$IKmjk֬vyWt.X]`0\{Z<ҠvH#PAQ˰ ՙ"-2a0UREX;tC(AE1FlYMOOjV+iDj}QP=9RkeC]t {/$jnkV @gi ^nh|zK=^ZUTIXZvXc21iLcӘEEAC)ry�L,hAM;Wۢ!KG� f!3�HaSHaP=za[A~ re9+"P+DBx_= DaqPe#1<|(X2hD�(czHʺK8}3g;Ӿ@*=g^Q&`<, `rr2M,˒$i42 %ZD$]B˺D.t�leY?{iTΣJ Q&$qe=֤,1<}#e峲F*_iZfh 5􆴮qHzZm~~>O3So1BnTs"9EexR+k;W%ff6%9u_ZZZP|."(A{2)~<!ZdTt*#Ӂ(+ĄB.Dji(@yŊi̴^֦:3i۵NMN&^I㙍xiLcӘ41R15 E0" 4X7QDlz3gEDZ(RvC-`3[֥TKhP(LzUA1\HsGpz4PD47^U(>F{}΍lA�CXms$]SHQWB�`AMHBC@ՃE:Q v,B~N{-K@ҹAuQoZw955 \GuɲLYX`d DZY䱪 Ab "WEэm{tjXڽm!x2##>>MpxBQA�Y؇ Pt<͂=/ua "qijï"!TMѠJjyf["ZFpƗ"VTvxUb<uQ'^HDk xЙۊum(Iu4��UU8�y1F}iq1˲Z^UZ9Iʝsݮyg jN�D:qF @2ݝ41iLcӘ~((NʈVqCʃ1 55De$>*EXjt Š0Ɓ�*(%a{;/QU^Ž q pH1>fpP9˲�DMsꩠ@)9cC�,D:�+(^�po0 }�c8)4ybb"v  dCq,Rtj 0eK0\{dEsx|gu ?KcZaoEAk*AJ/~C\"eeFGJ*6@ 1a<((71#] C2bi@)'� jSGRd@aTU<dDLD<g$peB.xO@,C0"{3ONNr>F߾v'h 7Pl4((a}>TP!f>hAOZӨ74EBMw@"b�Ә41iLc]%D@$Bu Ft5WӶ�] Q**"2f f@Tt �S$]g>2:�Y+ޡ1ƒ , >N b[v3t8#"mT@h# h 1#,r:fc@(ν3"0k� q^�bBĴ Ī ~LDs$- ĐZ2  0�Dz^jVXm۶Ї7 U)y.UVZ`(#C,#EN d"B\n 2q5WrYģ"+B>2? wc\0!֬倓cD:ѣ h߼[AgE")ufy9 .,�0/pà/X#ZʒO˦uIH'(� e:¼(eо֦yVo6i^ZZ{JJΞam "-DB!CYz=,/�]9$&,PgZ-2OB ""( !#2$I:K%kVOf'"�fXu{nB* vb iLcӘ41vj;!)ndf$B[q�Ee;)mPI)귗\DZ02Z[7+ZAJ< ".=0JT4McvL,Qp�nOaC+"A׌0"<�D)Z@h�KG D8!'IxJ=QimWƾp9 -^O,֮^:NPkufVfKi` �h"9 n"dYB$DfCiE[~o+fV8S ᇀQbuItG =b)ļWRfACԆn!*F&"BmȽ"j+: oVVtdZ<;2 b;@a13 bk4%{dnݹZd.$$"-h_9_4MT.sz^^OOO+nWZ[4*u֊*K"yhh6�w&YjubWD-Q�z41iLcӘ~r0Pn,  h s Z92 ;9�\$ƂΝQ0 "Z0,sHj - fCAPZ9<Ɛ@{H龮mj.J8%hp(CV 8'"`<!""%} 1. k tC! P=x gV`Ήt"�gY2<HD�� �"mH$ SRH(U@Te~BqaU3 vF]5-ɽ3`P=%ځ4Ϻiorr2y\JZәY15>zYjm#fYn@d-3$I{!ԓN7vV@TX Q0)la+\29*pc^cẌ́eͰ^ioYyNVSl7^,!aeYrΥOY`7QkjX 1퀶n<Ww# ̘bu dq;+\ û ńAC@e8ړ۶mkZzȚ^.fZ3KS^#[4ڭX0d &��[:p�R?"&T E :3w:,} "q$IDdrrR))vZO"�಴g&�Hz-�" k9IkD?:Ә41iLdm ޗoY Ԟ@vt,Ib}te �Zz8# j1qὊv9+E]a0)(4JEDFa!A`;k7TXȐRhS n7M4AM|-Y/4>E1&I\}"x`GC O_zE 6k:tj᜙i,..fY633355.v{l6Ed͝NgժU333˰Օګ$If՚hۭ5 M곪ː2K943 J zWx0=G 3";AETF6"jZv[Mq bI~_՚P #ḃ AW5LqCXΕW+-hm[5D^XbzzZs ՛fqnh4W&IbE܈;\RQj`OXZ@DNJ*F۩jp"J!fax ;I?j3u<}�,-t\7qw{?<mtu?v=wv wܝ vϴ,;?w W\}{z_[_ͯӘ~]BTkS Z h�� !p#EҼU3xv( {! g'zEzh8=Wà #Eљ| y;1·KSl# :8^:S8 cp.M^�#<�`/\z#!BCAaAMej .!5D0)T&?ռe>_Qim߾Hh695]S`S?j.=%B(p@٥ZkkkI4}c2 sL?K_ ;1hdO,C󨤾z}fffvvvbb"LbP %VI]cȥ=!-ٹG4t ErVp h &W^j5kvu׵mU+9l~rjkmzh6GKaj5)RMA\zK@U xu |\afgޫ \ED DHD0GeL5r/:`o:ϥ} |^FHKz|yz7T({{.GlO=7QTtYVgߌ"<M_~X>Ok_"e%|{U l=%_/kl'*�ԹD<dlh 5ܩ+0Աun;\[QQc+G0^tp *gc<NL&Wί-c+.CyАPh1('C *QDyu0lE@ `AE*@.>Wf90<A YiUj� l4ݾ}{eSSS�FJXh/..6ЙRp2D+t% 8<8ČHx\ H0*k*vx)@ux' żRz^4zN X0pH+/Cao_Efp|CEA;kO4M]}[\\̝ uTQEX C u*(h@rK$J ܦiyѨt8toXb }'<Ԫ_z~+=u}wK _ԇ6ۻq?~K~{8心u0>5x~sA7}Yj<oچ-u_w0^wTsrǾW/g~+=w^17B.>Cfq\s`=7?{n!71>]r̾~Y׎4?�2wdFS{|!{Nܛ>c3>Z=msa'˺6ӓJrW_{pcd=Gק3/sRyԻ~( ~-W=o^! +j-"BcHAGLD!BKBQ[G)iK3⎏o8g} �{B =^,r4v}6ZKW^$MRN8SQk9f^NB\h3tLSh2?,͌`9|X1B̄,"Y-ZJ@?0H~Z>LK][a:v=33$I/M$N?td >.szV J}39nPɌvl>I)cCZ'^L X,L;AïaǮJnOdrrR5j$2ȍJxVo]]nX. Z[𭗥^fei^�:NS0;bˎ"kUiXmaиU` " 9^ODBJE;S bvP#]F_˴;F*$sW_r>q5Wm;O�/yzuWIg\U�dg}[uW/}7l-|7\TAW ~v͗v֋O-}\|Yus?%�p?~vQj]?;ފ3JT" Xb hPbl`APnlQcF&& b-EDۣH}ݝ9?ܹ=И_Lݙ3gWW=]>qiWgZ7zlq5E7xʞ;/-_k8oruᘫ>(De/nYO3u%5p*e:E%R>=='}KV%stinLcQ;y΢/߾mE7vh[Aؖg3<W>w�(EDB<z#}VlP/Pj, !}ssdk8YQsg(S0O&L�v>$)volژ01f lnMR }:JiYQU\BR4eEL+PTWWWWW3g&lMq89[UUG+W|*</"m&C!o<<L6c&Lx~kXls]a%+{\z"-籔칶d?,'LKeV�(x~ K9PVW4Җ_RVg7 Bm߮JJ KJA<ܐRg߿Yd9XAR>af ys$ �:t J;1*H] 9PfLN-?1l=N>{t@Cm 'o>\5}Zy|Gs\u5�� �IDAT]a l_m6-X@3??מZoM^uS~Ӂ] #/a}}ey쁷2�ͳXc1{#z+?z/p|:Hp+۵o߾}b-ywoCtskp]Now5j䷖ s]5)!~b{Wm|3ȫwզZ3[s>>a;N>`lP- /^ٻq^]9 o?��Fs� o=hfA5n �Z޵wm>6'�@_?=l_yU]0ID;*j'eܡ[�Hz䜑:W^U>wG!�@ݾGu۶b<_O{@NJT~{OǑ`sڶ{Vt5tK_+(~Y[پߏ|K)IV+SU]JGMUtWry÷Z{u#oJ*}y{ ձs+ =䗿z<耫;WuQta5n;Ukث&Un=8�Zi;UݷK.u^}:Vx~m3_ɴFlӷsy5޿ceE~{*Ev}7վG]z}5ƅP:Y !ࢁUܵ\�T;)Uop[n]A2 -KK'/.^;y[yw S<iR_S7`#]_~Ȏ}:T帻6Xn3MTeRhڭ0kmj{qU>aAuU^ػ�@-Z{JIOwڲ{ꔃP3``뿐Y"ϝu:ݴR*kJvZШ>L�roѫ3��ỿ /=ixvWy1k^;{^6ci  בCQ 1`B{Q Ͱql|ley@hZkO Z+FC;1J T J `|klF <, &I5�v "�H5"a(!wQ1WJ>X$ JY P Vm8J 1@XSsVvDLRڵc@&ٸqcCCCss)ykjjj""'D,^3'lCg}[J4qrHVB(hh$!D|ȗZj;vYaOMKfsPL !@'8EM "qe@?`)nlluhH\Z& { O)\Ӭu9ϟR<:�l6drQIH+ nfj/X#(%!ڔi+=vq2m2$/faʝoADD@dmP5}+N}ܳ3Bf : �o[Yӿ�;[/:h`-RNDNnX?gEh0d�0Y�@=nq3fh-�T ~˦_,QPǁ{~W7MZq?<<g[mr\/|O;q�Dm߶G3_ufd!kGQnÊZ<Ċ^6|eušƗOX7yqms9 {W}O~ߵKݔIuyPsVy7j�pWks S�z ½66+U-z'zhޜW/ @ >Zug-?u=&#uwL&|��5'>h+xW<{iw G'>>mHm?ֽp n_~wu٨YC�ُ53x鬿L]-|=cVysW~4!`AO^:>z+3y_]� ~ۤY gx~gO?@-dҼmd~T͋NK/z^soyڎ76sާgξ@/ok39dM\JS1A1 ~pʼ^c�`.Oty;@ syW岥3_|wFE?8{Cs-Ը{Nń?20[^5e;ɼEߺm5vbygNʹy&wv8p.Z>n>:o򶱇޶>^sEiɼw!?{/_9peѓ0yz܏f}]v~qc>9?c/>)P^5g;_}uz̏K{ܚgs|ϟwSkڅ�Yqto{jiM<և?W8{E_Hr7f2WNI%tӒI%r{{Þ#[@4W@-|wGk'\w_<wM&7V~A#JEDĞ�Br}|c9Z1H#FAA8=CSSjv6Dëbm(Nl׶O'CaP7ީT8&`[H$aQ0k`@\BA#7=gbil~iR Q�i"H**" "PTD<_``#*L-B�yy1 KE0 g^(1Z Fð*fvbWJqx"ft:qjjj:wl"*A8"$^<FuH *�t-BDeeJf"b)@fE0h AbvOPc&ᅝC,xL' J4k?i17=u\<OzdaڂMM8터<;Tt,eAK|3is*!qBQԔn6%0qK)^%rYlx pߋP8R2R ,A?T*l ,Ar9BIEJ%V]]8NCCC:.X̽2뢙ji^Z"DȕR: 0([Å;娿v枳Ktck%U?v}U}ȭ谳-h}V9809US2hl �Yw\6vFB/;{n]c3Tt@= }t{2ٺN߻zR>/~|Y۳O$ghxU ksvHیvZ"%fKx~[xhY6k6̟p7rbno52\_B1}㪑][>_9365tyvˎO[q3Ry_y&8:WnxI!sA{rgP8^*J��f<Ԃ=ƍ@^p}uۧ cD=r;\|?ڲ׀O=<`K> oav{O|P{_UwǀzV࣫ulեv\r^5ſ<G#wc; *��D~öս~~p'OOnu>o,��+:֥O>tKUzr,>§x{~{{tWhN;gpݮkA矰}޿W]zVo((��;t֥<w-G<vۆ//S-2!oQWgP6g9KۑcޞI}zN4Lv.9g-��Ԃd'㉻5h_zpɄ{^x=d-лc Ǝ>{;_pvek}^s{~o?e=z aX/ G]]]n׷G[4y e_rWE7)[dLa۰쓇_uŨs^j wB2d uk*+۬ޞȭfOo7 zۓhZ?ϛ6w>d˺be� GZ&Ij& GyuQJ f8BJ@�A:(. GPD֜[PI,SEd[} 3rCrd>2) u$'3FIH)ȑ<uIl6�IH""BF@A V "t$:o AI*T! GHǑA@i$:Rad Z lZEv$HGj*BR�ͱ M )1tB�EAD۩S'J�"x10?a=E<#H OPGJ4Jt84773TJ+%* 9 dA.V@AI*E뻮5#= Pixt%&A`�@"(Ԋ!]8�p'l8{߰M"{t$&n,@쒓 CHfR.GH)GzR+ j iT!H'k@HO" $FM1%iI&@GJHC: @G:-`\+W ap( U+8 d|u06SVD ȃvM)D+#d)+) 2ۑa\ǙNFZ_X1;.irֆ((!AiRR蘴֑Rab tUaA$ Jkvq]B^!(҄S_A(xo0Ͳ#tEZP C)J7pHP |%R @�% 5OETC %(-)TIrP9wcճm 9n;ɳ�5բ)mjlp_|7{zQw-9eڴiӦ}jkMIWscګh݉j/|qiӦM{wf5wuoly6ԧ6d~7DW=Ū aU9 4svuv~眼WoNoU{7<w焤[|ݴjzKѮCi]#أognqة;m|ɌuYG.2}7>v'@/>O=|yq&kྺ~Y9-GuuW-^<__FC~W-]^\m1oЛꗭT Kn:hPf7n|T*JUtLa:]ܓ%ˢ^r!:tj67L\EmvNg��] d2YP+͸vT*Ju?iuՠ-ӯ丝SlnJ .ϨXA9M�55MgZdwCVkW..zQ;ҮӰ_(];&,MjImZxyV{r&uπbY}l2rH+1jŲٷPJR}; ceKw?~mT6ֺL6Ϳ[xUGVv4^mM}+SA h봬RUVZŐɫ~=%^qɏƪoɖ'?A]t⼖Bm>mZV(PW<TFI#=h9kpн$HHS216oE6@0( 봙mĬXM8c5Zk͹=r.}6 !8S7Wdk!`� JUP�@\tAkET3E@ 4)$׆6 TR:1beee*q@+R$yS:Pbk4"<}#�0 cT2Lsss.nSTʯdSPEL@Leu47eXayq0q0Q`k �9p$b>Ғ1|.ļo^fxMٷ2Kנ0 XJ! #k$qxyƼ8($ |!f+n`.oE_XH|ߏAA$@DVm{Ҫ \*av".…Q*ara`k>olX}Id @:EjR;|1RPoh7273%V+zJ͋Ţ'>U1dgpilU!}8jH .?<q?qZaÆ :sm#��5qy{ v/+ݶw_ݶ:lذaCj}ذK!3ͯMo~:a5~}.7m&ךM7-h7=z1 ڤgٻ-^ߔfHh?ѷs=ܺe"TwU 7:Ԁ<`cݷgakc'4?%Xos�Ӣy(h=1Zt"@: 'OLX5b�D.`E[ѫ[zD_pC^ݺ^= Ѣ{S'81O2Ǘn-zҹ[.]e'|W 9D$;_Esҧp#Q׭Kzт-G%[(ϨB H$I�Z`wDqٲ6<{I:0wm7@a'{~t M):u阩/2YӣG c_(xBޫөK'_e 'Ue7iV(VwZz&?h"n�?y3~";]?kkw+?(O2j:RB~W:-#T~vgFw~aG;Oig}!<}ؓZGl>ŻX!x.)\!St&$Љ9q:²Ia+�^G)P:'(cӎp&BY~h9CDziF Dm !9cruC|&* eeD Hм&u#PTA !v6&R@.(aآUY) 6+:ǃB#t|Ozn< NﯔR*r9@ydsf 0d6 8\tߦi!\"JUVVVUU1la""{r\sss:!fLSs:ȓNe2U|bvƆ\6KVV|҉=jL$܆V. *T@ؗeoKصxn)]z yg{?"ly7ӸuiWE,B2=,gѵ[V[ioGy bEEEEEEuu n& 6h(̽cdg-YkH 4>I\�JW Z#) 4IDR!DZ{Qnm 7OL"t),2^JFU)Gjx?fP8Î:ny Fxp ^ǐAUӞwY<ʊ Z<}W[?_y=O;@>.Q4^={sZA۱GxW<3sK:a }l?zͫ>m' V}< /_~5:װjO\} "ݑc|}m b/sk;{ء۵?V (2|t|_&M9E.yq�� �IDATMkf *:4 pd?Um]O6pd=N1Gp;+uYxcƟ4s%%Kg|egu(:W= -_狳GOL\?;͡~ceWs?xg&{%8?8m?9w3~w{N8ngÁ?;ҧ/]:+yfIw8ꋟ|EJzXYUΛ6}CTc'^q[z/YK�E=tG?ZP_` {οKW^ՔϗŬw 3ڬ+Ι2klk6yayFsDM_㕩 - o|#wqR-|�րsLWϣ.8s�@/z쯳"++ϖgɬ;w~NjUkLHu]_,Z7w=N;8my-o-\U?{ssqyQ}9~kիh mubcu|ʳ~gNj_AFfrxXL'm_<AeIф&M_l;GjjoIn'==i^PnSo˭rliow7Qqvy_}Ę!E[;W6d 8a_n  Q8 *N74L. C) PP8=$ض}'( %8,:) $R܇"+(h9 >a".�0^V/da4J,Hi[ JA(' uk�P "B)"PUMuϋMhs-\`*b$tc_b3 ""L&EoJXRry6ck%qM\66g2L9˩0J34NݒRgwnȷ5fLXҌ]JXs]DSP*9vDaDK bnez�ۈ (04m&}0FJ-ef})[i:&5D{狱3ΚQ8)#,~FJaɃmT'K</ X99h$2qT*Rr"UA0dFcVUR-e=M��qZKY߹hX5kq*ڍk9GdNjЖ�`Cnyºۦs^{]9;xBX,zsN{Fz2=z}6fᯟuOV;n*�ȱ[v0ۛ=mUnLyiKK ^;{v?)=Μ+T �Լޝ|r??U7v+viɞS/moٻ{NG>Si6Yfke=G?piyąJ� ?]P*3jp&qDbVOzKt.j {ǞՔi{ItS9�Himvߛ~2'㛋-N~3vg<>?}a<Ipm =OMx;?S럻.a-O]KG < !8Ɵ{pG]nXMG ~;z>=0 s#ȾWzW0ops.M&U3=5G'u1g?A<r® }XQZ`ԦYҫ߸z=)=Gu\^qk>{ʼn%>!qz��z͜yw�r;V4uW5Oe7<ϰCn|o-2rk7?MhwćO'=0~>F^ S>Bq+g9"}1{Oμ&c5c~Я6#Oy`FPAצvWOgXۏz7pט͎joLΎg'lw8O]rB-6?,-FY {F_KWi?ߩ/ӷ~AxDZC<!Q! #>g��mB�.\a181 L*oJk4~Iu'˚cs?X0x ڲr a^|Yr'5E'#"*6-j@&%5 !P8`Y=Լć\k A-]D$x-DމchP&@"HvA2dQX#-N#0!)u5760DPηhxCnk!6.2R0 ylEEEMM _</Vkf윱af2T*HGQ@A�AtIJt+%JL&tz2_5aHJGQrq -oF%UHH<<G!2"GH $-$_fJl؟aȒ�0VBx~SJ< k(vR! b"hI)]s4*~4_"_u,o,qOh^lCٲ-}h5c֭ d. Y}qh+K>VyWo]eAqL&rjvAD"D!xct!ȔШr\rv dy�)y "γ��5 Kk: @y>�&"Pk%0/V?ڵkJ6ч=|Wz?>yZ=E13<wX(iN5,jԩI/OsO su5^n唻O?}'}pO={MvjEw2޸ԪWwK>~Ȯv 媻ڙq˞-1U׾xf?|HM_F#6o!)}?VZAx~TTVp!�VJ͚9s}-/ʔ3 5+٬Cg`-}OM iNf$ UćQc�!J/6/jOIHCRU||2}k6FQ AZkkceH8Q @B0 LcaV�!@BźBcPX,6N0,=;�!x@ ]C HӮ- JIEaCV ;y NFڥL&�*B.)Ѕ$O.i@ �@ qU{ AD�(@D\<AR*zyX&M={ltKn FlB}*^)bZl$q]פgƫT}CDJa y`øVx1` `e$>JNUØRr{o1!}iSzI+ASSo2UUU gG7 {tJ).lyIKL̎ DDI1KL.a&@(4V*J(Q8rGx^ y+^26Wc$@x|J1wo}:ڨZ'`ⓟm7祿ZOwStIrc6� ~?ey7#(ۓ;}O%q۞=rE3nst�jr 4coWI׏;啕z~zզ#h6o!I(ۜV*JRZ\.'p}M@�HM�(PR@AZ�AJ� H*w"�dc1സ7%˩ ?P >!"NOU(8c(ʂ@5@ Qp4�42BU6rϵ$9' R;nDZґS"fsiT 0}Rm[«ɬn!HB5[JuCCyEla Fk+++ᜅ„b$d؋� \LpQ@BR�"Kܺ@DɵPQ, H펁Z찑R8p,Umjq4E=AD^_qce'2|H'YPJhPEd6eD]hiPp)lJ[nݺFFڵQتFR .!`!RHH}eU> |Д,!odt\A)RĩaR $j@ (tAő&KR؜7뺆`),9@- �u QNb k`s=s[oSu˗} /[w��/~c2=f?ܳ]!zʆӾSSu7{>s?w6j|{RJAiaΐSRJ0a%�ȑ梜IA2Ơ] g(my %3l6+t 6׷NB!I$l�Yf LXs �0ZX;ق Ԩ˝�$PDySHf(φcjRrL&Ʉ܆ 8el5R2m+GSKV<ls*u&() %pzG"298ZkqߒarU9v0iX�QH Yf~ \u8&"rVWh͠ȨE82,,Em+7e./r[U֧^` ;xonJHKH,\QJr'JRӍ(`KT4|[e$ZPpK]C,Id2R)8بjhh0}ИnH+{ !|߯4D1&A)ӆ&l̠履y7 4A&EB#(BJM4oE\[fR$W[ u;l*Zk"(TZ\mFmFmFmLa.PQ{."fYءcTe*:ltf_Yh[C+MZXٳ:"!-TqB 4G&Jsv\e2y a N.ZI�َII)eOo%@l7c .x<J1Fc:ek;֧uBӟHNݰDQt�)$M :Lg@PkE*A&~ rTUUVWK)ENR( r\.%"Ni }A&"`P`>l$Xb,3kZq*uccMMMJ(L7%?"*p0 "fǩbmDIE*^kMVO#\HiDỞ"8IM\%y܌FkY"r]7"u9_HI XtK 5f,FHKaoT6F2f.|T*ej<(3 gbDZ�A)|8~b.} Raqpb�^_mtIP0dL&c`9a^?sgXIV^O2adw@)%�HqD} �z̘KͲۓXS66v9Ӝ޸n{RJ^EK(r=KD$˚;'L&!lj{^IyK �F�)d[AQQQ/S.! @9IJG)EJK)lPj dNηډ8E`5Mi 7Ll". Z(6tH*xe/Ev CQϋضpB-` ) /fdQR vA}Emظ1 EQccc.(uҮ,T*8+c|l 43k[ʨh$TTTp@*f<pb+.,$K<T`gDU[-t�2F넉eZ[J)!J!;dl BtRJ +b3j$e#�C`, 鶀N^8Ԙy4f2ms{JR 8l/ DyAvElSSS*""1a&:ٺ{sk=42whx~m&!FYɰ?jo\555A^ux{ 77NGv�ZdrNrрSMt\ T@�ӾKmmuΝ 6<2Rc8ϵGmof/BAk$k.| ڨڨڨҙL55Ʀ&Ob2)EćF"d�QWHQ(-d\Ok#qy4$TiŪ8iDJ \� z뛓VQwa?c ly*0vE(㲶,8vh28GW$q�DM^ �,4gw 8 p1N]:sfPRʆtc}/~Aa DLgJLsy:9vAHDj% - f!=@*\r칝f<vOeL 9�'j"0#qxImc*.R7mFl�="SF,?|"]fVf5{!{4oEa[_IEW%a :Qy& E%$jv[R:;OʦYT&MRODLY,X'K$�L2hͥ:HiW:Mx IxO6J DIⅠT�@x .BȻ@GVTU͠A&PP__?wfrJZ+رh6K>{T+n2y L&Dke4mmFmFmFm?NDTYY^RTd~wPHQam> ogLcMDxxn/WƢ�(P%Μ)o`yCpne,2R?D ؀j,gX|I8қ61vg>;$ tPb~E2 J **TbEEEB)dWHL5-M@yY� Ж]vEI) hlldg슊PE?dmXVML5Xz"ƨƗb#na=K4|&DcEB0 9W&ʔbј;[>=(P - }&a)YVٲlψ=AfuUh4VsJ*d8YJ!@d7aS6YQxW,R1kl<_v~nOKdXm3v�Z;vBlܸ1yg2{yPob\)⬇ 'mWu)5"J)"!X"BRJ<p;vN&]6r9Sg!ũ<8!+Mlb7DԚ8 Y`4�z6j6j6j6_&T. s.+Wjjhܩ# e I tPhu"E�$$ IinD?M@@$�5 $d|dl9E؞D #;,x(̩fG<Ŷ}շQXA&V4=Gk" A\RBj�{�EF( @V$xXDI �BPJuڭB)0lnn֑ (�� 9 WIi@)H:TQ^؃ǃB$f<GyGr9r%t@Ҩ""ȑ"EQ{gD1ۓ]Ji؏�U "+z aΎe!\*2a�<(DDy_kVA@�� �IDATF Id�E3]N.Qy5DIV|~h!"jEFML>EqN\E eRhPJ)'Q;?ZҧKВs[ZzRJHkMJBWJpݔJ#3ha.3*|\N v5мm*! ސ�:(*�93g/Y@Gr˺w+\aNh6L6.-'S":qڊ]xXFmFmFmFm?IH(Epe&2AlD&`/_chbJ �||�¹$߶A S0eᄒwҵ< H)޻]z-~m_*11ee# -;ݎm_l+ +hޒ.xh") 9~(#׮߸AznZ(\¯rS>�T%I׃0lJ7Ү\4'�GHq0 8Oh}hT6bKUUU*JQ92GNa !*HP&q`@]S#$"%)ԍH!"tq.PdLr.m%4ⷌVZkqYi8%o*1or:^LJ&JRсU]ECJTL.kRo& CmaT+471^6º6Gx2l64tXMby^. 'K)/;o20!q"(X?fӶdRbyBHUVV_~9ˁ�!=]ЊHSQejjjR:I g YYlY]2(%�XohJ#�8(FmFmFmFmCW4PuMut(V^E@(�"a^BJ6DSbf9(C *�5 $NeB a�H�4@wc.%(iNq5 ʘ唈 ]m mEϦ �4LLȊ.ްy?y i$D'xZJ)$P(f j;W`.L.:/Bו�JH0SQO R B'm<D#I3� dd2 &Aלo+2(0,[%',%�D 1""�Jk ]ͅ8뺮k ؐ�H-͒炉6ߖd8ǰ˾ H'}0Ǩ6ڄdm _ 1)$$ C8gl|\&PVDKR$BE,[ZHXV ӊ7(e& ")++̈́WAdg;#�e2؍*GQ,nF>"I�>)(;P@.;#+Q cl68s �X=QBEZGʪtP:d4±$!$pg+҄t\�Oス;'翟j?INW`g_MD9HiR* :n'@:Ԉ!Ad"j!{r~(D ;.HJ+h_j{,E/1H6#\(5Hg۽B˗;Q$!1]|}G ;9?5F@ S_�Jk�!6[uMpmseD 3[߁ ͙85UՈ($"R5>*9#Qi?U8jB\.)"WqJ*2?ڲHT^:@�$FM&}]8n:a"R ցR@Rv\D0uѣ*S4J)t-qzoj(vKU 1ieSN WEyDEQĊ'J MaKh}A[ٸSslV3d(Ռ sa΅As gF;RJgv),˘)faJP}…Xd3t2A)֩T* Vt$ r<yD N}|qITV0XJ[ )OiIl2MK+kj2i^AEDqRR8 yeYtDJE\!)L\: o%i/",+Cx44p)pZؔoк#𽷿~&9m_IYW_oB1jÆ :R\ҕfgz)!(V h#H)j lqRS==aw%'3P3"bD=sxyb>sNg(($)9g0;ꮩ<~YVWx*L}(Acsp,=\Wd;B3Ƹ LU*M@âXWJaY6["b�@$ËA" 'LNV0OWJuF # \L9 0ڶTJ{D'T8Gj\81D3?՜sF %bQJ-%!"1� Y U >||Y\O^N8#� yǀ@@h$-;`&-xRXb<XC CC9R`9 r/J_X3I)=/oqDfHʫ#gȀ@" $dY 2휙]XFx-mnhhhhhd�@IBDe<D˲Ov,ͻټO&"ږ-d{^^}O$^Q庋b*FeYLF04;O9/R۶Ka�-Q a!ua8<F 1I'UR%/(hm[EC9wx" c1\Js"Z['&\c"5S�8n6W�|>ߢJBp@ȪS3jdPlIaZ.@Py,NW CH[`s[Ek-Lan܎rL&wIWC�dB#D!m 8¸�J4�ɹ�=W/ &$r"$+2J9)H(F3DB'\@ \� L BO@�9[Բ 9 ž jkS9(t)(+LLLLRY#b*B+$yMB|5}A(ѹachP6N}֑TXq {\_ߵ֫C9gl*Ni ^VoQ$"Y<1�*SVe5DP.`]@Ă eRbv~5]sd Yx.L1#^O.I'b~EE!-dDzdjID8>UVTD`c 5gS^<DWSӅ� +@aÅiKO*䛲\xuvPhQݺM;DTa 'HURt!h3w'od')pk[8mJQ,C 7UdZ-W>Cu M 5e zIυ>]'=eb3&@J_ 'k#äD |>=ef2B9al#J[Diz;��Cf9d2LFI(V^]YY,o~}6Dž$(T?P$POBMH0 K!g$@ YfGg>.gY_*q�I@33@LLLLM3ǎٶmY61' )ŘB_}uW6"Rz~T1@+uM�oP;,h D5FX %YR<#]0u1 WƑX1 MT$;pzhAzh^$""! *ՙ4ee[z:|0QC")$�RJ0繮9,[y+� R40z\B#st}i#@@kȈXcZ:J`lXmZlaÆu!" )�8scqU˪JǎY  vܱ,KH)t!3 ElJdFl`N/ZpuxMK) ^/%`+|*,l2XK/HsBYf%2L-9(}��*_u]} t=`Zfű< <IB }a~RK0��8N}˻txswRQl4TB5HEj=]`TfcH,K'ʓXا'MWE+Oə:06S35S35S35S3IYeBD,di52h`(]``F56ZKB>Jnf3X.w*pE*#l~EYLKa&4R5}Ue\�!hԖA<ﰆ@\Ŵ07 Uz9!8ۜ.-$)u#'RɄg|>jY0E#PL&0$oQŒJnBW-*XV397nlhhR* Ŕ:cLGĄkѢE1BtS(]ySiUF1F> uUĨZ] �`Cz3QN4iE]^ lz.P@eY1Ei>0xJpX�[97,K#KزjDM8t:˩Db(#-N2P|5-(<B=Z 9%"EOf(Y&ӶF9'QCwAm@Iϙ~ nœjH79OV$#Ιe^U(BJ 8 (UXU'SHVJQT ]̄_7D-r9VL zO^Na),s7D "f*؃J4bh:|c wh MDY7(BˆgXzK8$RpPs u(>e"ĥ�;≔lR`HD~t6#PM7WOc=7i&۶U<Doա>m1KJ[ BTUt錖!)&01fYӂ- Gj� ȧaiY "Ta0A)PuУsM@A}sbSyImgι,ƺ0ƸmǸ!:0d2e,/;d*N,7"mm]Sbv2 !,� fYhv>< S"cdYRJߐQ' E6Mu۔R&Q撖Rr@@.*7۵,+z a>$A ՞ R<,Gkfjfjfjf"Ip}_ H@@ RpڄO&V!CeJo AhЫMNnP|1-{{6!_dh%y[&,$GFН�rHf"⬠TE<EV tT<7 0" Q[1A �T@sa-@_H`(H)l6zBkSV*,K418N<.}dY(0he`QRI0Kjy)%aOB&-Z)X.糾$ADAezWU/Hbt:ͫu (͹jG~0LET*蠡Wٶm] @(OzA9e l7`ހ�1(B6J@䜫faLJɐ1Ƹ͈H��1=K!vGJNj8^B0tw٬ٲD\nDmȜ>붂?Dzk$?`UdYေZLvUg JJ7iR-TUldjfjfjfjf%)Xsas!pa;F&"b!|e4`PGkwhbC71U_yXg6~4M :39 AUa(@D͍Q Jb.ggl `jm-ߛ yG7|0B(0Vʸc$$D�:bK!$%bD�{B(Ψ|hY 0۶e᫉-C @2zB)-'f9N6Ŝ�</r F&!N *Z[*|sY��$d‰WWWRFu8iBT� D'wU@'zj6*_1 hf@ AA`B; (夥fz1 Ѷme"I2@uC@� Ä(Bumx "ZE<6h˲*-A(b*QPRJCWM*L ap03a�@=) TH C,S z.iRk -נx.J!Sq]17TEbXRLLLL!1,\CNx>8CF\kD1VO,)6TP}Vδ`d \Jcfj jdCj kBm_r71&Cː!00G' `f$/U=S*SЛu˲" t=e݄@ze9 P}+UDŽID@HŦM7զi7S6n܈r1L&+++-z45Myy y^]]ʕV^]Wא4�ضL&7Yj* -J$EUUUUsqc\NM  s>Gd28N:XWWJ^QeYʽ\o(#`f4ߨDΌ�6f b/ad[)K\j w^K*Zj%K)>IbX]RՇ\.743uW]J?#EO04y^j[P"ҜAX,&ܰaCmm)5|)*夹#c1W9"=lXfАI!GD�cGr ȌgWkq~3YJ y CR=E3$Qp �ذhN]?6o޲_$&>]旨<r֚-w +Wl ]25jmX:{_5}y*LW\35ӯE|2I0fq˶KgƆt>jaJMJcx܄^%n3!u7բ⫺�(لTZ9  { 怒+rսED)}# $c1RQ|>+ܩWIJ@""Gߒ 䪓&M5c)+!UTd۶*9r6G,fsd f6Y76f u $8X3@A`yf7q6�TTT$ Cxu˖ݶWm[nöy ӔF a)`,R sͼ |"!"ԃ*iOٖ1kŘZ*eY;@թ!Ur.pGJm֎D8#ʆBWb5DKT<cA_ _ ` ?9b60TiTm$/*}XRzasob(R�r|>^l <D<ͻy�H$6nܨefB 4]] k#XuʙD!1e(2eP5 6eUTTTWWWWWWTT(z7r92f4R5zh:8)v#aI��$)#rCR0"MmG$B"RWMZ'zQ2"+2& &%49׾>N�`M7?5I]:\'W7V_s%};fAL"o'r}̪_ӨIV_gdweouLǝ/Wn[._K߹iøzk?gUlEz@pDNDHPHn*p_}Z3Ø`('Z}Zթ.]+Q ;j m'A_<Rr�� �IDATP(TkYTeD4&AWU:7dPi6uz2!P1i�>(qOkS ,dey u |^-Մ*L&Ng2/F5'BA͖-;tУG-[1;,J*AmmM٬HtFqZo d2i6iUI"'KBj>cxtm!_,, |gT<Hsii*+ dV�ّE?Aө(C>sfl6.*Zn9jJ%aM%1xz0*{uC#U wM&b{nrn,gy׬֜f/y0=P(aȈ D @an"{B)ҺY&3ڶ`jl1-N*Wx2+>s=[&-:rmU{_^5T!8Gumy?v[ot*:樛?*Еk>avmV}ݕĒFtq]i)~̽V%*;qgػe"ޢoO2_DO߮U}jp!ԕ$|pvuI38g5DMyrVYUx[�@u3_wئeSYj<e?{5+~Kr=T.{ů~T&S}Z~h;W|(>'uȎ*NGfƿ2v_*?öK/LbU�9`! "8];` "|E_RuO8ؕ)hzmN0nfq&Hb ňtRAB RJi@HeԷsն\ 9:Vtm'x<Jk:Z#q+nln$/fƆL:I7f3*2c1˶m!/X}?66\@ec2V-٬\FzߎF_ !b1+XS7Zgc֕5J%I�p]WT,6ֆ-m;T뢚l$# R@6q ETUoE-/0-G*s]7t_Lj l۶JϺ9O B2"`g綥~U+Y*RMfh%w"Ij'*c%QSfM'O0j@R1*H"5 R;<="Pe[P #Ľ>DX٬^X K_�]b̷b  2c3G_:ǚҒzN?EQ`O?G@\7O}әߺ7ן8<�m`熮mW7�}UCvxuL,url.y=g:A|ؓ#O}dINO2}yNo fb#�2O_?^=]7nǞg_VdWM6}lhm9|[wt 垩~ٗ E1/�}{kn=>qi o#KפK6r+/N8{zbdu3O7z.^|"FIG=:q>1gٲM3l"=b}G\Q-7쥂!!A3ꑫ$+H"HY)tmdh".0n`nvo`\+i;`V[ L*0AeV&\#Ϻse֛ M} 鈃�y |eƆƆt:TDU`BV18qƬ92pD+\N8XnKk7mXfiS<yl<W[lYSStJofɃ!Q@Z|ܽȐDJ%Y&BSj&7ԓנ]ՠ4\ ln��Puy*T&>cU1 Ոჹ=e^ ) ,bf$7$8"Jݥ׋_I!PjWKRĀpfBSCzmi9W>J%mQV}9#4I#~^lH>OhHL6e PeUZXl5dӭ] г>?yk ?Nvʮ{p돿�Ċs'w(,s{=wuE=F^pCfKx۝v7_^|'>ЪҮ~fŠ+o>j@x!{,@u<`]*+]Hˏk{;o^*<- &q];2C'�k,ksƸWL#OdiܽEe.5^ڣ/ܦ!-P-8AϼݥG۪ޔϮ8wC=nh$ծ|ޓ{1?ιsǞF�&��g}ܦ+ j[$�hGxuA<77 �@:U'bTZT~vawXE8r�όԧuŽZuۦx��wFSNJxCys<#g߳e"^}64q~:bvs|?jQmߩ*Yw>$md<զ)/&�9sU<Ѳ~kb?svN,vEٮҺ.ܧ_ɘhn󐝻H$[9dGTOg֩; 6)b5<|ΕDm+�hkgmt*:F߻LĜ羛mQn߭uc;5{hLTwW2?};h[n{{+_.ٱcM҉y/ީX!�hU-im嚏o;fm;ޢCyiWKǗ.c6xeݎZu:QOT1͉>oWKךdeO|`jCdz۷Oœ-{yDCعgϙ?y>mS{Ĝ<�]g]uý'v2Y$V8PS*{n$$ڈp YOM*eMRN\uD� 9۞F�瞭WoS‰Z>ٕܭ8|6 @ SQ*'A�$}gD?P_]i)߃aB [*/^T޹<1 4J?Ŀ[T�h,4#@7�c9IAײ *K؄hYar)(Ix~.iݸqӆƌŸc1qrO<瓐Aܲ$$q@DzVĦJH0;(Y5-*۶j٢2e1^.` %U6WsخV&B* l}}}:<18"q +XLYIA"P'8"B^CXk5PEOY= �nXAJ1WUU9 RF%HXLyB\חRET]}VтE:S*��"ʇ0F8f@(l>dL}̅?8#I'XmbRJ#D^BB�ned#[D"J‘# zb1nsK}PJvq#&15 D̗2覨锛Mok ;m'flǫQhz{}^肁ETsߎ}-��{}�>co?u3gn}*�H ةWzKTsk  N;^b n5й\_^lqε##�րz){5$ ;[9U^t;ݪʾExdwN]c>[Il'?K/nU}dk|c]ӟ9 {[թ3$X 36 �bՏM>x#\)GYu?̟u=>>{ȕq7SS'sS]q¹Z0R{;hfu��ܗ_aGtb@^x9{\ΜeKݻ/g� ?18igrNL69j%Sw?N^|ޫ 7#_H%f?4~3uqO>EGwy `QO޾ 㻳o�5vEߺ浳|+0[Yb۝wqi=ҩowNg~]�rɣ'5aij3|戵w(Aa=޺wv�`j};.?P&XlW4(r=5w/H?v~mǍ?vMf7èIr/r7dz31|6wyfpU7Y;zpoh@ _$~;ȱuCj?8S%Gvޚ5'}ԉ/ W<{&[pMc/Vzcoc'9ܗZͨWf,q˷ޝoYSKWWo}Oy?Q^z2N!�o={b_MV%lwK)U^4]>!ֿ{V'*٧[^&&ʝ홚}/�gl>X4y]mXцqWwO__G*Tm7/@*)T4\y r ZnZD"w_(uc+ {]�qU_y˶LU݊GLC9n~gV]HI7!K zM E8%ߦH_E 忭b%I!Ć ֭[aÆ/" t@Q/ t*d%d2XNOn̶T D"dB*mIXǼԁ4V1JbwwI![Ny)E/7Q3B;�K:wQ�pr+=_zՄ,B!)B2Tɤ:;*eY8lLFI(A 0ϛcB(SPM sǚU )ƜJ,VpYZI@P"_ \.%md1# _c18/!՜JsE)B#n)6E] K4Fq[>ߗe2UQŰ2Dzȍ9yhc,˲ͭohW?*nCg{+tKz;eYv͉olhTeQ'I]}ʝ'ۣڞ|v/_|N&W_tT+QUn7O`%*7ԺFg?~X_`QƌrnTWoM2YUgx^ަMxy|tn;h]ԳY{֬jy=ѦoWz||oյ˭dJ6CG&t<دs]G>eR{tM}ϸnyȬX<;6�g}F n@oE1;~n;tFc=/{zucgOnzW`m ۱KCnk>G{>=;ҽ}lnϮ=[ضM⤞߽;aE{/qcn?fkv �Uwm<OW|3yZpӷ[u޾M`ٮz��hձ}?^"7}:ww).1Wtro;v!SbV?Grзs;]bֱs.:yuS\<Уs۶TQ[Q��Դoߦ]ρgtۿK^OC3&rtf=U[ھgm9ߺ?]&qo#pN<t{ӞzwoӣS�&VKnO>xOٽ{>^v>{qxiߩg%٧fvvhf!W CO|쉕p{ع:7ްvl۶˞gmzحc=8~/q O_@ۆs2 gskQk<`3"ȃS7O7վ첲ʟ. v΄kIivuN\*i 874ڼC^m>�7/@'FLD(bjW.Ky8=կ@AC17uSߥ5VGb͹rڶȽ6Ȉt?B}nݲkea<�(Զ+Vz|1q8 Ix2Z.Rƪ&# IARBT,R~ubl6N ṹlݦn.[]UiY(lnv"f̷tBKc2B54t@ac]Lq@�.ZL�&�%kQT(EU4`֌D"sdnD^A=au9%}Tmz"U^i,O&;x"LUV$RdE*YJ8x\Ddy/tq} gV<DJfYڛh�Q}\R2с3UX,AbHŞXb_:Gz̶v,D&P!'|`[  C`VlF#KDtO=:_!C2Yd/Kg$$Rrs#߂j<s-9aKw��1s PQU.^>yK&>jiӦMӻWUr`3QcC&VQ r?}o;]iӦM6TDeBf5=awޅ!g&/|6[%~׿_j{Pݯ��\[ܮK 'nQW32@>g4#@&j1[>ZZ2.;닆g݊37f>] 6ȴ>js]m�rJڳk0x+VD#Y;v+r_lco�r͒e~׾Ee{ $B5=WY[lئg*ڳ \Z/[V׮OfV{帶lכgڍe'K{tmմK&|Wnᬪd2Y��lQ lĪ3o-V#ǧk7lŬ|d%Bj1.YQEs:՘!�ʪJeM2!nU4-~;wkӢՀ>s�O<ħ-x́CkJlXdEj۾DÊ m{tC=+KC~}rAd<'_&L/_V߶{BW|CtZs5NUff'yGYsRƪ:|7 I{lo-Ӳ+פr5q~&lX7ქ^{}њMMCأ^3糳wWߔR'Zf3 ?XR$`@�dަ J/(PZc+삻a y8B e_dJ,KLuL�̈7cU*F-a* RbA9`'4}5'{ ǁK �IJlrJ!bXvL=?q 1f1cP $H�EL0BTŮ H) =4:l`5v 1Px% " !,5PlẮ6hFC+f+hR�� �IDATl3*Tz]iV#>s Q,hubrn) HIŔ\z,Kw+++cU㪵rGY` !G&ҍ,-,V/JGJnȅ e"5Tsy~I$C !p@I \%I<E"8mBèh6" *uEaٖzTQs�8@"ap,%vSCx)p$ NJt T"R.$0@b�$jvu#* #)ugP"hd4@ Ʃ(;mm"GnDYno zpU1k6Jc͔kˏ5{D{lx*9+wִ9|ۖG^=UcnW3Gl7}f,7Ys}KÚoW'>0g553-}}3nܲ9+/>3? e{o+XO/gS¥ZZ@\Di\s#:U"=}dm*jhٚ0�VnrR5[ܵo?/twϮ}|]iH ־']�c%>T[�[`Ucj+bҕЦ}²ͻzz߼8nчB�`5mZ|iع}fʂ5a�iv:m3]�%ءs;jٰbe|b�h۶/zޗfOZo. */[OZ*!Hcz@DmnLtio01X m6 J\vTJֶs9FW~G=�hQQֽSn}i<˝��` 8гKLcЖ'kզevM)j cNJ ijK/;wZiE._%ڶo 8>NJvk.N1#"I(|�=|2E91 Qli8©}ZfT~v!yWY{0.ye.G9/|@sn3pj>9lCe)VŨ> ��H_Eqҷ9:60Et@yzklj2b,Y6S1BFq 1Dz,F(r |Y1> % �_R^50J# m۱c@eAp([hq@CDqUx݊Ș'|=)|:F0@TVHSy:81�ĸD u/<_2D&}AXИڭ["iQݺUV-d2JY1[0L&#92H*|I$_q'=$ZdӬ ᎂ:P$ ) "mG1l6+'Zp %]=!fjpَ&BKf/H x ym`hY<ZR 1H!" BA=HY )J> is+On.ϑ$$ex"-u}_@v`'<!q6!$9u}_K�ռ9gxe`b#S)]bql>CHVK(YvXܶYHHx"ab 8Z x̎38mK-IJVr!LEs2<6cumfW&+6'?3bZV*OZVu}�}_ A%"mxY\gN8-@ s$("ǎ$鋘ecI8qH$ b (b,y=P99җc1˲cr,HDF*7F&VN:J a<0fcׁ$@H}"#'n3P36eUT1HZD\J@@L(@ 8v,&]:{.~G.r\ޓ�րcOʹzrqSoʞu'5G'.ml9ob<5\w My{9􉍡�8dM7 u/׾:k7FǞx~gzuc㚩O=w^[X;`= (־sQ#:wd~/pԠp}qۏv`5>x.Kꗽ{?6Z^--Ǒk۰x߽;(~ܻ0yHq�淃R3rN=;&h{dUYaG+qmةW=[ޛ|.-F:PJ>twnM<-VݻW}{ܔ%+~7}IΝM9%�`WU7êUlJ~s);|{{?[r.uRO-VsiC+|eƲe^dzO= &;p.qՋՁ4Jxͨ0b?UkW/ ғVte<;eʕ gL[XU{|W؛zs^t3Yǯ/u{sezְ Fm׈ԅMy1t~6&SIxԵ.H)AsYW`-t4=ܰ~�?y#}tIXw+rQGvt̟lͲDl N9ݻ/z+~ .c/EkVΙ8y^>pٻ;秷z+֮Y6{UXbF|+̚lC[F{<?8oڮű:aeI2 3-_6.GӔer+\:ٝ2+?rp<96u[;Ὣj#V<hG_^,)Y]uڱ_[1|/1Ɓ@(DX1f6)gsss۳KH;cht4ZTF6 H1Yտ$T_'I,H3RbbꉔR&R.$ޕ{v3-ɰ5IG#:Fm۬{>`P,QZ ʫ3�-ZTWJRdFi}֝/Jͫ: "ZmbRL& EXKUPt42=Tgys"IBPSXWWW__ǫ=c!SYjs W9"sd&>dI)]d2ʷ¼k-I׬MZx$b򍊍n"잹Mq+H� 4�_J_xB$ AB`1u'ŏ2Ng2U><sАfST.[fy6uy8Վr9ut(geڠ508RV,Pz&"%yK,00ڵh5mpX\Ҝs;!2/24h3Ѐ8Ds-8I&*Qu |JfIU:7H#O7zKOlnHToq9!eI>t@E>y|aK-/}qM�#z钶}]`|bX:g?}[oݿvw|~)�i?sxhH8G5fOp 4iڲ+n۽O+vM�5.I_/l2�OO=1o;֔yݛtHvYƝEZJ V{=;/'=?'o]|6q^ra8��rXY. }{^}; :/N}WB'^ۧ޳ )7ɳsSꢗ>x٘#}a7}È?')<VG^q\£w#6`yΝ^_{_0߭T|7n7ʁr܃3sbրxM*~/Cvک΃/7χ=vG?Ƙ̸z|%mpnxs^ݹsߝ(!Up2{=zv~u(OM0jTQ*ɵ0dxÞq`wb/z_<kA`яz<q�\?wgfvgQg>*]b_8oG'M2!ͯ=zȪcǵpgvg`sO C>.g9qjry{t2>6=mvxݷt3 m_[:ʿw9=z4Y;öڗ~2Yӓ/ۮ[<~>/OKW2x,,rll?6p*ևtȨ-\3޳~ףUU|goS'}D�m[ ]}=n�02<�S`ŎP480WhXRK/ 2� t6>s,!qӽ~,"È 1`A:ڜTgxmnMl͞SQL Afz,ZUh636mI_%cug$ Dz(9(s_xI7i.X$f[ei3ABgDu!C1"* ΂ZmK\.GW(rlVyh~۶ɤe'@M<*hM6|ՖcdQXW@kd.0;@HdAFH|EEE*jllT|W�ܲɤ+|"b6":#WJÂC'"b>1̗2X`z]1[A:l r S9�_ 勁jkhTy2H,{ӦM7aÆ-Z TRE466ҘYP82%\#*?TXe/�L~W>`:h1g6M<z܅4ѵig 3Z~G C=a\JH566*A6UrejaCxH28uUpL)*f G&a̡#P x`ՄDQ_ȡC6w[h۽2o/?^Ew~fҥn'߽xTP㇅w?_zުUY6Θ{Է(o4eV[)[W~<pW 'yZVWSzv?⇶( ՑX޵#.ZW/.Sܵ +ڴpĆY]t=ݟy׾?Q )7.!޶: ߺnc%)#2z\ 30 , ހ肢x+z-+  ?VEQ{`7o}UUf~!(UWeeFFf7ocܐ$6 +=1V6K'?Zj:σ��ZGt 7<y+xǦ.!"ci"SqTW?QZYg-H\|`\! XBbK �F13F�ET/Fu,]Ihޗg n|ry9ʍ~{%iJy9�d"vlʎK ֆ=�%<6%i#Л3xA KWho +YXWsO6]P#c"VkzzzlB[K(ҝNf^iZ LI\CbtfʿBD RJ gS3�t]F~>*"~V;?D}*ɡCq? 9MRJk\o9lbQʃuw1 YTIԫ &?DD ,ظq#/\i>V$U*=第kَDK+.U􇌈S̒#+M\J)4?Vx >y@kֲ1+\ĉ!ē]xhVN&)+s9t:_R/p+\YQsΑSYRؼv`HCzXrw?vWj_y/rIex_:�қ>}};}g· _WVK^ov#�ػ/|qѝMYyԎş8so]?Gv{:! oSJC+TgsS|FF}5H�%-/]6,o�J -9�6#%sPr&Fб#RLЀsN9Y2N}>H2yd`[ ڡas'_Wl2ʪV8I4VfyQ:" cS!b#z8MŒ\?Lmb<O)T�6I9W_%fHRn$I273KR ֺV4A ,+R/>"rn8T<P$т[Iܐ X"NRJqh"�9"/<IDj6/>7 2 PkA)ňr$n(SYkѡϐ{07pNwd$IKZZV+U1zG\qsvvV O2gJ&IU/ɽ5;ǹT b?&i2�q(û^YD,:$gZd 嚑csp$jjUtdY%0* 9b.o`_4M@} \tO-ddh(ߐ']y#qw\:SW}c��=Г8=bVPO=Q=VǛޢ7=S&]F4?IdtR:G: ?6H>,xrBuߕ/5~C'QUheD afOyD2 Kb8 @u(HsRoRf:I_?5qֽ̼B<@1Ay}{r+ c8޸qcJ%GeVjJ\72EYHDJocLݠb\70a=HZ+ كݤ1pJ٢�0 ",e *$c@ bP^b'Ÿ88r%r-E�^?wI) (-Bq#bI4\}ݓK,Ypaټ{oblbTUpTThnnNH~[8fZٔ4k".?N 72iiZ=D{_Ck4/$GtZ TKE!9̀Re~ ecUD@YY! iHCҐ4'-EaD 4)1FOʈ$.3[AyUiVZn!oYP63P�Dw& ?d_h  �A)tS8<( s  FKE-7c=u.%ai?x%\@16hvY&&8Qn9Z+ڙJ^g\⽅tx"+�09bB nt]N^jۈȊ(ss8:+T>֦^ w; lE>&C w6lDxƳ6"r:F6JS! *HEej>R ɬZ yVЇ@|6Ir%G;5a8Ir(e#\,PFxRg\{J\06eEtJyaH0 %FDtX^q-"0-I�� �IDAT%6EM'g)loT =/EͶ~Y/Bw2nZav:)0EK;�xdIN;ϴ)`[\Ґ4! iHCzU1Q ]DeKJoVBDʥ$%@yG3H GI_M'&Rg-AK�ȥ5|̧s!<|8ttwք#hM|3Xd4KN3u]}HF4MgffX0d3|SėZ}z ,3 g)󷳳Jq&/w`e!gw_žHqb 3D2r*H4VK&S8}^|PNy,Ā2Z&-+W�d8j(v3MQRynڴV^UHK.ܰaC}dZjWL@Dfp_m c>(Y{8zZ0Y"coQ+JneL�~UA+'"bwaE yEnDGFFF.kJ)+%N=l%N+*Dg{CҐ4! iHi܉n.P#IRZ&>;:"۽AJ#r""4!d6m!Oyį]ź1] 1#[xb+X^_�4:PdZ6 =@^WT49Ȫ[T%pqF�,�0~`mR+DHz  Msss6I9 MS! *WPJB7st0 3ALpl1 H1Iis")C v m(r W[kUZ8�@)f9%CsFzI8o\�PV.(4AH@cTi:v( 4f9;* 1EɤXtim(WW)իYyt 0 EBubɉ1 R3ι$M[fZe-pTJ]2F+ \jQFEڝVE)v +VV\JʂJ3Ʒ,,lZZ&4쥂ޘ/1koδ>%ċYkZņ8#O1*V$IXW$YA�3|-*YM`аfkxi—DD.qVHC*0� "{Zw͢-2cusCYOǚsq}X29BC4! iHCQd|t2l@g<(~Ct�E\*L/.S@MAm׳lT칼9X 8xҲ烓hųxƽ<ӻ0(͢Ŝ~�8U*fZ!R ] Kucͥ[t`pOPSV7!M`'cXtXB+u<3o?eK wZₓr/#جϟ#nM Q9Z81t (4Ky+q>s@�J?%SZ<@bVOf-333cccccc>5kFFF,X@iUHj)l,>66 T(MFlٲ֭K.]vm>idVOLLp=AQZ> MOLz_{<(:glBaRB2AN8*R&]ၰȤ3",0{M|.4c +Dc9qPZ8b Ph-! J0ᐆ4! iHCzf 'Ypg>҅Xor^o( 9[m,Evz׺t,e�~APlɖT"1E!sq^+p#ׇJChC|bmv1ز諟>R=goȁs Z rzH:=�pjf9'6PXVkQ;!8 ,F qH{X, 8\d!�$I LK}{RҼSHv-󂤐ȥi FE )/Nα!AMR[Z˪awFlvy?ߓBy +jH`Q�Xr/|ɾ 2R*stkF8"3�8G�\A:rfzFi7LP| M#!# 0& ~cLRU*hѢɩ+Vj#z ITq/&IvUfZCDRLDn`EZkTZY*@V "Y}BarbK:LXZ2H!梴$E([/t9U\ՒXwC@I Đ4! iHCғ�!x;�C^52QfW)^sPrp~o}y|ΰ+ F*dr@>_b>2l%<S!gb8.'rP)! ؏`c?@j5cVd0 6mڔ 2( NkmK`YIeԟp c’Zy63�sڟl\bBq4ٗF|P˪':$_݄@%NJz*f꒗.t1vrÆ0 KL:]ʓS녇 ib6InV5 6Z[:}fggGFFFGG9a'&tv%`e_ar |y!)9pR:t)Ja873n*/K}Ii)m||+sT3Er]J%%}rF/稲̅iTVKYTh`HCҐ4!=ܼgx9e^L{Wp[ɶDq% *eZ$)ԀιZ�0Y-S�]#2nM)J�AeF4j wA(:uˉrc{Ӕr/t!4HG 헐$2ԝ;k?+|vDR@dJF"sNRK\}/2 2RP YWMXZmsssZ[)78݆s™QKt:&Mmp>#D8w %}1y2O<_ğ29elo{p[Q͌DG%kt>Os *Y0Ri76Ɛs6M5 &fLD&U*v{zZ<p b\}sF#8Ytnjr;j5(\x10Ŏ-3S��v:1!,D*�=VK@U$_7+8:)ŁBZ\UoǢRWoUt]CI y+뭗l*UP@D <s=19Ր4!M_`uMM [0rwyr6~ā|Ws!0(}z- -hZM?ssfd&rpFJ ">"C@:Hb쟉P|Q PnRn@\H;v?ӃAzP1ˉ_)m.I14=/)syA5y~09N*0:>;ݹ9 8O/P~c4SS�3 ÈAt.6xh:D Pʗs-fBE4ND3M Pv|̛:6}hۚknڴIk]@nDv[)U._jB0;|s3\j=;$I%VlaCQk2)\f977WV0lnRt]j4%z` 2ŠD_&�� !g]&T@e n322njjfBD�eHEEdmey.B7R_}^QC䯥P\战)E7GIGN?<sO^ OC$u _Wo7O?ɏFĜO?;BS.n%gJ%$_"!є`a1:@�8Qn煂 R!=~o2WH5OQIl{/{o$/3%\b'"dUfj6<[50$8񍍂y[\�C)+:ifj$IۭVB ӜCU3\{ +Oo :)ڔ)20CDeLE!N uIUC!Ϣ 5]@2)�?b?[OI(,-%*"q<ۜHTT� rNim!f937דM"d^q �\w##cccz墤j16,jlM'v:(8@VsyLVcZvddDj0$z*6a\XD!tZN" 4*tRtb≈ 3C<Dϸ/!*B9MS@Η b. '( P/a ! iHCҐ$&A+9rx�z<~@D)p޿ B1g!Y!=NO~�}�=#KsWG8M)a9/q�zXv۞3}n!wƖrwT+O7>0(x'v0yƉ�EZ'ZvnkιPEcQ:Nҍٍ!eUL$I8N$IZD(4HDiݮRjldQg3TAB,ee bFAA|O&M/$ qQ�sJo ^RpNsgz1kފ~ "ZT/KK;U7M3[X%r"j@kmiV(@kIۚCD.Onmbi�t!g2h5Q榧8YtKC 333J|P)`N-1TU.09;;[MNNiah(y9 *-~ʇDCqhVF+ݮ."5'PGaXu1551yx2/<IwQQq-A $FO k�^Ytm+*,7M&4RC-4! iHCzs ZEGLZ*Df6R>j F$Jv8P ѥi,y8p>B;5 %(D%# NZ#*MD#^TJe Hk6^+r^9A=r4.$uÀx@%֪ q1TZƛ)fL%f%!r՗NB@gϲUѥ);PZ!&)8t\}-&ed@V6q6q6MMPMAK-[ݓ$iZfYjI4BXk֦MRjik֫ժs.]Z]oًA!�rjUըfZ[+aZ(v0ECyw"4(.M1i;RJkDin@36(lףaXVIJu3$ 뼸vh4MtQ۩T*&$I1bU QB,9rɏB粨 84:w kr$:V)QZs BժKR iZJevvŵQhT0�@T*_#B!4J0QCP2NZQv7I)6Q :n7j�,\cq<;;h4XV g(*� �h3`*Y5r>}]7H^ at8"_Ju;]Th8+$kjǩMMkPAtQZXwvyO4GPhpCOwOI5Yoe{]lABp��9" p�W[B�:J)@'!p rHCҐ4! IL"b!O=x,lZ DlFaЈN)J�9q-,#CZd?*d-Kr(O_$z?"s(L $(o�1F'.Tpsdك30@nn{9Y`AUFmws333jh$LNOO,}`lu4.Hqn �HCN'7VH)!uքL)'cg.aZq7N8%lE /Z'EnҒ9Q ztVhWb\G/aG)II)1QI>}]Y(;�8,~/nP~ҝٳ�8AAo 0MS$ �8 \A_΋ W|DT&�DռA$^h… h۳9FHӔ#SXmv:$I*JՊV֬?|y?*JaPEF09Zki7nZI{G&ЋY1DZmn�@`#ga3!l UCҐ4! ILJiT P#""4伕π _RUFK79a]B#R9J7de{uC<7ӹ/EZK@NM&�]617uArK5 ǹwBh1@ԨE¹n;;;iҍȈR*'R52MHWYKZ `VJ@JkԊ!(l3gm4ŝKmOD:01$I&1$NJvGFF@!Ǯcjelli+BI@FR ȑ(2DDGDi*ڨZ樊$IPNA�lDDXHsgJ$y�xs7..H}a){BQj� }ÇXu +Y!ZQ1: xiwŜԧ(u `+\W*n=X:NI4-Dd#K\">&%E3JzEvP jdZaRvbn*QJ%IQ,)=Gptd>=QBY՗]UsA2 Dij3M($_C엖05YCҐ4! IN)! "J )zo)AY%VCH`F*k-gc/ݗYgKX< 9K <qwKD2#'CnrK8& H=DFC{~!X"wVsBwf8jv'ͫ',\0кRDAvؔyR8<YȥV1hie1[쑀]�\!j5n0��Z cL٬jaW #7y#Z:31fђ'bŨ[hgQ8OH ! °iB6esfSƇ4 I1+X0śI2[8/YzT^NwŇşWgՂ:8EvUF^1|%f4#<eްapC~Xʝ]Hv4 ~F?Bcl1T*a s�� �IDAT$ٴiSVm6<)z=n258xa%3Ubi{/^RĬߟ NAc8Л`HCҐ4!=ҟ?��}R#0@~nGLr&lV!0S5ҚEg<H}l|fVұO,i4+ۿ?E8hWpݠh- ><~D XqW S0Fa`s\M;{8y*`Bip<eߣ_cƍ+ȈAE2hDeI8NYӣQp�,u&H(�Sb11sΦ.S*89 CA&LIA`Rkf$`T�;K)~5q �*DNuatR FR `rmDe j/:N*Y8s}Rս1"@V!־$fe�$=ov2ߊnEKL | NrYx)͕D,8,H<GJ)2ފ \AXar)D/Hy LfWrGSSSZa?qn$6$I[I04mZY|94M3%VW<M`)GP"->O- E=Tv~w#_'1eHCҐ4! orGty'#(Gtv?妘ktPjW}<xF (HK:RTyBP$ˮXP<$ѿ>|c1$Vػy{3p^&lYaX` 8Z{՜NtzzzÆ fhu8ZJ:666pxct�uZm4z-VbaG@RmnE0b�`ƍgff:qWdeyau՚|o\5t:D}��?rxg 'h]A%kF6҈*�2=ӷ˃#1̽ ժBefWONVu@FO_`҇~ٓe+"�؆2QZH]N=?>)ό(4!w'QpJmi\-]=J?RDk1"bٴi{JF333ιfq^ZW˗aA^o4ZRx~UH0~t]_o[uo'D{ &9KWEv@�@;o&6O{b{4D:gx5o $]{TgӼΆ{^m_s_<f;! ul{1/'?.?7RZ+T](!8 9RZG�5D0h) Il4u$D[1]?@u8)Na~FT7R0&!?%J4;)Oc"Txq<::l%K/^x-0@�!&6V�kAaEzRՊ eL0zd@&Ё!67 Ҧ&7<nn"FQDuZm9_Ղ(<mZ6jZ.D$M&8RJ%R B,ؔ 5KZ B M6R֣J-Ԃ&Ķ:mFPlIP+e4($Rg1w8Akd&+A9; $_SPl0Hy[$ 1I \%oG1wI=*a,.//ByN^DCJ)9(ϢQ_TJ1+\Kٜf֟xj$14Myʓn^1E<N}Pq~}D]$�P҃ mI0t&۠t]�`?7s_ݟc2Q0DIws~zxu+O:ây{‹w8c꣣yY_t.{~wD/zhcc:7D_s>yu?ӆ^G^Xzӓ:Ώ^@N諘}sL_y)a9ؠ@!NUhbWAArH/Tg IVxh6vymy;+୵v%iVZ n .d-\pHCk'I?Qv9^V$G J `0nONN b^ Ðó97hѐuqw:f/ <st8!FvA><U+K,glJ S1#VőGe" 3>J~>_XT ڿ}x)(G,rh\T>8e)M6d9]diQq�Oxڔfj -u~Tbe[TөjEә.Zhɒ%WfmFGGkڢEFGGY$ ֭[733!Le/< U'g�i J/$-G{){jel|%J &-vOfA^W?&w}︗켤5?]>k;?^[VFۼVƯiG#+<4Yooʿ?˧I;=/znzY}uY $LyG?gۉjubs MO1C֝*#_𘬨O�j _<<K3s.ڡ/N?=|,GC[[GqLn1/mH._P񗦡?./=ꨗ<YXf^v`cṁ`$bN4I&sitK"H^p.6q.E$FV|vι$M1:B@YƧbmh4d ?+A$rG�Je0[ԳmHpcPDWΓ  :;Q)@p4JR�(rBR"בS@JRەJŇιqSkԣje|)L\MDvriI.U b_T:ΆSs&(4aPU#Zhc0Kmp@.hqFjZr85RJ ׶�IYa2w,f,\-lnF161>:>VAbcgq)OuNyjVh3V gyۏaUZbj5g)(ōW=ו\ց1y-Cwvȗ[&>cQxo]g!!>bz?g\x/Cw""Iɚ& -?Ul(<! U P9bYr/_,ĦFIlٔ*4ڭ.rt`ZhŋOj1D\nݺu֮]l6}vx:$G4,*K[T~h:`͋*Ϥ *"3_*ADU�D ĉ C>,5]_k~x?|~�{)yƫu䉇sq?~.on=^ g_J_umWuG}߯`߮ {]C?pI�+?~ؿ^/^N[?ÿZ}[οwndZs!˶:F~7ǫr#Un|+N]|*j^ +c/>c.}ewwϹ=dG-7=qzcd&iQ'Ͳx闬KO| iHn w<C|_||E@w~�Xv7EDxFq'ϸ5 r 9pSQIC|#)\oɳ]A 'g_6>*L"L#W d>?pyJW�Jȟ}c @ә422P+MnZuju-I d_댜xZuεZ7rsӭ<Cs&idgk&"$`@~3 :Uc… #ZQFs2M(͛1G%"zHJ 㜋"oC]cG.y RK*&(=EyZ�߾H_/bXUEqD~r9|wxNg6 3pՃ7NNNNMMqTv.PVx뀷"*m2<͗U|]9쿽44]s" 4 cgϾa]VQo?hC޶ _Ǟx3Vmpx`�w4_|u[zMսy㍥{~swܱ̟mmz޴ͯza'?_/Zou}lԃ>M~_7]w_ܕ##+zw;?xNw1+>_L t$no<Pw]{v>m/\569~֝+Z[m}M'~#? M.տ=ԣ^tW'mZyO}sn^tӧ}��{8/^�kz|x^|ۧ @\9Z.w|kM�jַxu*u*Ccs{a/J@Ƴ9`E /zxY�'leJ}}w3?~]Z_Q_2we= +v~q?-{o;壵Uo,ڝBO;m?eQR_ӑ<H�\VV }NW-FSnJ]x>;XP ~G~wέIkzc鮇X v>v+F*ձ � ߼Zr1}0?nè%'m5 c>ra{mV_c/nQkNyӷ]҈6{-Sݻ-Emmgߖ@l_;��z̗tfaȭʼn|VQPrg/-[촸VYW;Ѧ+l;Q %)Ar?x'j#+u-X O[Z>7y^^}O_h%GW>^Yϼ .0ѿO.{3rQ1#_DR|Q엏|)�w9�+owx2s?qYyU禾U6/n}% �@GX�_ꅯ<oukXmouM[.{E#|6 8Ab�PET?1A&`œPv0l�7JkpJ9/? /sQYT297u9kDH}b? ط) TQjXŴ�:nDLsɐۙ-”\X,b cq2wyp XPT(<A _T*HVeb';qM8Mُ#Dw.g3nEz}hը"y d' _I06[N㜫kQq�=o"D@hctV) Z񊐼)zCvCPJUUkٗFRH*ghhP)*d$zkDeI  @<\)(EUCE܄% E߯<oOۅ-Z+M"zjW<fgrrrڵsϺu֯_nݺxbMBrͬ_/6Iȑ~EL)xJvf)s!;y5$.4[ܯ{B:^tƿ;`E훮cb]XSޒ.x'%7|?w~o %Smڛ`v4��zw\{c �G.Oy� cadNygn />ؓ]'ܝ_sƎaK�=keUQđ{.4 iǑ5?<u+v}^OfaS�x۾㧾z:3_>g^|m:uⳖ\qO?]Sڷ];�߲{ǵS-w<m=">p~ȗN5?m񖃎 @;pU;ow./| v雇D��v͹߹yC^6}ɶߛS3k:3ȳ]8hv?7= & sه<^{7~W)tu/;|y +p+CBO~tK|;}�O;o;'.|ۿzv}UhpWw]u~t\usn=SWpە_OW�_Sfw_5xjk>w+kWv?<�yޕ~w_K�4Wև}p{G0*;/Oշ]w3џ7èHnop]7N~}"FvYZ[s[Fp �]|s n>#{)Cθ{o譋WZڿop!N~_Ӂo3z}h[/\_1_syWC^{1߽[.;/].Yo;F.ķo]~?}}Bm_5Z[ԗg|'T͓fޗ~gkncQî?7y5~Mw[tͯ~n1wޞm8.'_39u;ဂa7<YdmL3 xy31z6W\6;A C.GDߨ7 K+s*u|.{r2gX">Q?%wvrokm,)Nr%Kj~qT*Z&n\�$i6RC3�ZFQ!musea @DI$uιԒ/Bg<ny!?%g=D+t&BLٳ K/uD'֬A ~<OvXL6po8^%vhܿtC>w%|%3Rۥe[b/ToMWD؂ &&&FFFG"H; P?k}"򯤨&PIɗ>X"D8{UTRbz9ɟbH_9;j윫7slanf>U?.:#'u}SVK!oJEgyX<ql2RώXųs1@z>d߾{c#75ۄHsjמU}n.y[zw"A�� �IDATwwmwVRgg<oێ6xځxV4/o9ļ[mUpN%aLkoݶm,zꎯpC<^_~ +lu}^ymD|O9l]q~b=^鏽l7ϸ7T}[/j}'/ʄJ � ;ޱa.EM?:'}5fl<<}[z﷜}֜+?:p'b+w9裟:vߛp[Yҕv.7׿?)5Q'?>gZՎi �W[~{uv\t񖫷Y��`uᲥGl;[vX|y\{ ;{[?m.UIՐ^ӯ}+uU qm%|3w1W-]dV76 f��FK.b~ozӃhَ;\~GugeԟBfVn|xg|�n[OlG}|˚{mWbɥ!62_bw}|V.U��4t.7| ;YV~uwnK9ﴟ,x>v[-]z>_:_vbSzϺ?C^rƙk_ϿqVl]V_O%˖,Yכ7Ͳzk^m :E|kj}}c~4ҟ:~󼙗|Sy6˷}i6 Կ6xot%ɥs;?uKq4y7_߬FF]}klvK?x|�`_?Qs PBF(}6<JvFBб]= G,ۻR rxoC1Qt~��7B< A-ے̇9M'9r,<A-q�3 K$C?XYAvL8֪g<DֹN;;7gy9Y@jr8a \dzV+sGjXt`\8T)f9;i8񢧟 L S3Eֱgj�4uZ�@)D՚2A26!(VDƄJP5""� 5XsEqƒ/Vq"bDdM3eG{~ ]>ҾR*P/>.sg�! A@YDA\^@EWAD?QAA0Ȏ, aG !!Ʉ$dYqOwI"3ϝUN՜J8Zċ298�y WJa?^kE$+|<fArX0 J?6ⰠHSa,{[Xtõ칇%LG ms\\RI@5D9�@v%QmSB%gj"Ѐ{3,Uy7\VR 0d0YD@ZͣϾ>5-�]=j?_5�#oCg7{y?G}}5k֬YÓG4B+Zv~o۞qóf͚55d'zⱗ#/} .:k k{܁֬_<5W<}V{?Ikl_o癘0/#OH '_ ''=7_V8[,|ͧ].1ȴ얫owo L^4uR:SZXɺ:mmvV_]7�vɤͧ&OY72y% nZdIS7/fEZcjZs4+/Ş 8ep!jQ@־9-\5��gd 5,ZĹ;4jZ6+-yVY0k䒺i4ĶgTn.S�3C2!F4W2'{Yl=cg| Pc'ݭ{pf̂y n>1ϥa`V-\jiqH=iƸhAo`djr͢w6VO+ c ?y㬫EynVG5y#F!y^ۭ> +#6 9_=MAU^7izۻ[lj0{{7?^wpm_dL+>ۿ9{?-?kå,|?k" !r&|5^M@`~I]"Ufմy&(w\ 'Vвpp RZ2]劗%*'9@i(|UP%ܒ9ճω�@~Vh|mZ c8j8U=F^1b�pοRjM9\V+0 C(S3OXKQ"%V΋jJE%Sqxc!12P$(�¸"cn#/'C9!]D\ ,vo{'E{170IlZ.8_){Wl>v۔IvO(WrE6e}(D(XƼe"oYIT?(r{~ʤg8_^PIIbʂ  C4�&Kz@sZqz3o;Oj+O<gEOzZoզy_=GOA8mܑI[̘1cƌ-|cO'��Ghm ߽xg6ϏͽhuY3f̘#ן1c̋?)O̚ݽVo]7mGQ�Չ{u]Z+WM[8YEJ\qK9dj~y._^=Wi}ߺu1=V.v#]J1SߺNó퓶t֟#=oc?b%Xr5wlp�'L\>?/͹'L(bt^8:(jE~_nG;@7^Z 0;="^Šu'8Aϟ=7̝=ןn[]7g ǏY^djEC.d9VS}WR)/hD=fz;|קxŕU5~qs,NUdIgT^ܿ C�a:-XпzE+o܉W'ܲUKpwg|;Ϻ_\ws{MS7f< g„%_Hc̼_'7z,oC7կP^wݮ%s  53p[KiOQ{AA򲜸`τ ݋rF6MWYno=׿ɷwwg~;蠃6?{Ըw?Oxav6Oiׇ8)m5H{�p0~L}RcBKz $U|I^g4jeR�ez֌8-7^pܐ,*tAJpAٕjy }{̟ضJ)T |)rw$;w Aَc:�pyDV|Z�JK/UV�WZk! f$MX_r`υHI)i 4q4n4QC.kXWRC StŊq x)!A$+kMBS/Zi#|F;+e,Z?Qu^DFV_7ȥE�yK_aS;ބ'r.7vc_  v)~\2v8=wXnS(vC3(Pvʻ2S"70-MT0[�Ad(KԴT*![(k wOɨ^vvVlb 8xx_x/>0Qj/\~rwlp {-K߽w K#~/w>Ƨޱp;>p[ŧo:~;vxx/~K6j5̹/KfSBhp1|``glkCC3Sk~a_uY{Vo9W5y̾[N3l�[I| UZ�//i8o< Kߛӵ[�xvꖍ{=޴xv8g;qEkxf:ỷ{pM~⽷>+{xTc'O~=0w#kF\3󕽏z/s�sn+~snE'9*!x?߾~}v=p}sgK'?sμх/];v5[w3%/}g@'c=}=ج9 {멇g.7~K>У/FEVC1??>5gzj|k8 `{FcDYq8j]f/Z lī >Uaǿ�vs.{*jW>&unuyfuL?uCs>{ۅh.<bZen>;^X=bNpzyg.Y<XdE}1|Kzva'~vY֌.~X}mIZuWoKmk-0x㷿sdz -2ǃv?ŏ~g{+ꨶY޶[e 9y—|{!;wM/skw?fem}a n zk$ EKD-+" !/LDD&_@KkcٔOY}*rLPPYp~%2Ik9?5[`d(aU^3UXJB c CZr d6Wg)7Xi![I8z|}}}cǎ0xQ\laSTf++^Yo}DTTl6WшǭVEQI�z_q';3A[7DYW 0T{zƌ[VA 1񓨝?ˆv28[iAKYV @P 0 G$ QJaڍ$ 12>H$# و%oG}r\�lBhX(==z(@2b,(D2{ ҐH>@.mG !oThA^R* ({T>•8ryd [L(oMLoUJ_;Zkop_!"% G #P<0  Ug@ҿTݟeT#zS/[1梃+�֯q[3jn_:s<lW/?h_k}Wom>zό n �~t%n2z;p<ymK{M6iv圇gR菧l} ?.�kjL9쳓~r);Cnj#;;y}V߾>^gsw{'߿d=�}'ިгM<'>77Ý[Ymtn~xǩ}wwO#'NO]6P�>{A+/{[}o}G ?x/g>ӔMvճj�ӯD[Os3 ݛßmڴOgy7q8o]->ki?BZAfs?8K={]x9f;Kyo't[Ҿ[n<eϘڅIof~gGw6q<lvԕ'4u<iF1%sv;oKf q=3OMs vhTȍ?'%4v��إw=OA?zʴ{O=E~wʵ{ׂ |Ҍ.wU ]{Ï]t{OF~rO W]~Dr1[Ol3*BK޿ 6~ٱ=~e]2yV{'/Z3ִ jٳI{Θ2uyr oΥЫ&5^|Hm5ii^ַ7v9+l]vo<(*шvLlp{{18bwĮS2vĘiG<o|tW3?H„R!$ C@ƀ")B -z[mB_w\FDRn%(].J8L~gšZ2%[)EV*H}ʕ<dH7ΰY:%JE�YES !L Kh+l.㊽gU2PYFCCCZmԨQdlj%(j �JmA&k[Q@9I Y `%^�V5"Pa0jԨj֊ $ w?&i81̹aYZU>CDd1 k��(OX)nm!6 >~Z y Zk0!<^r& "_IǻR HZE, 0Z:44Z}+Mp4fn6_ZTAa^%oZ===8ժR*2ߣRLZdgb| _wֱ@̎(KiB<@#棊AWTCBʛq%|L˭\J)O◂ƕ d4@oI~7q@7yBm= P a&Z+T 7FqYdIiU°ZQйg~C~"Oȑ#߶;WsOItw9ʾwmszg6]g~gfn4<ɛٟ~rKر;~G9[>2UZ5=kGSi%=oz ܓ?-Д'M:6:,{] -3=nd,{Oɿx[J']>U`|3A#\KoԯaZ?m)S]+`DO=^{U3OZkD�R�,�5 *KEDu\V ʎHkI[fJ ^U"GuֶQjގ�]"l8n-(T&k~^x/߀Uݛ^1x߸6Li \q/OfA) qtVSm^ 0Q c0 Tf-ՊFApc͡!ENDe|"�Iε@�ETJZ錉5G-_K"<�/n0#Q` 9wUh{@EI.@$F8:^ JINp""NPABHD϶ (RZmՊ>cLJJ*[ Ir�qJ]}>C6F چRv5-* j<&|t*9/P7�� �IDATL׉(Kg 6@r,V ieH Po|[+ �T�$ Dj58d_"Z3WlsO_nfߏ|˞z/廎?jm�?|h+pMw>]5N+S[rge~ťG�̻̃t KpԔ<g?w'}nI?uo{Nŷ:ԡ7 Y"e.Hk@V-Z t=,\*#0ifӂ@(r;)|.+rDd-R\韟Kv롾 :'i4_Ca\pW!@a>3*2IΊ/m,[tkSrTtVL(9)+WnxLVc݆<`<(<"% "RfEZfĮzw^ YmE,:f2L8 Q9䔱.��kP82>"[m*D~L.ZL G(2d,<�Kk.+8p֌VWB-˲jM;Gl -TZDr=y)\۩o3 QNy'JT. IZ [X&4D=%VRy4?0I$RE<FMDZްlccC@J7#69ޕ͍vתVOc/\P;5w��ϼ3_#mzofU}W߯8սEOȵO}=S$\K=u U_Ik䵓C@54`Z @KyrZfя }Ay]L}^-7ܹy"W{#p<`UvjcPUCe�1\wqu&"PQʕj5 j0\ݔs[k9-.<\M_GDe3NvRi&"4(DgT2o Jj5SVM!3%E5ΉzΕKs^*/fIir܁ [? ZjqlS  ~K0S(*R\Q0R�ǮY'cD  'H)lR³ȁ[o(#-a~, \Gr߼@<Me dL )"7v?Iqͳ)cb)D)3֒ҨP)e=q. /CP:ԡuO:J):1%ku�,:^Otf)U*\ ȅRZ&:P⡈cpqh߫޳3$L}� P:bvpl !GPV((."1e7L4l>jHd@lu';M6V;I4dAk@ɪf$Ndh*FB`K|ԔQRj|zub5MZ"|>BfT^gM] βNK-ԁ, I:Zg)W""ԏg*lLce0@z7$^A'Xciw6 G%"c5WWJU*RiZ(]�J 28U݆2ێe])p⃨0p)-CBk �%B܆̧I@nt7$k &R2f%G"͚-N:f)8�beQur[RJ��1d-)$&B"�k1]P:ԡuCoZ@J!*]$ƘXVi'ag 'r~ caP(~K(H xW(EMp ~t1  bH$Gγo)i}e*i%4+@8x )0(O3�( 8IӍV!bZZERNo<FW;c<cLZHhDQǛ ֌]X|&"0*sM%&Xb lM>aN�JtT~[$$Nb lZ29Qn8&w-)){cv2)/]U=s8@VYr[XG?4eV~_V�CR*~fWQ|y0[tY,N%3к±LR C3ܓei̴ oK e"_ 3=8!P:ԡuCoz") ؿRJJ'ơs+ /j U^<UZߗ2JΑBwDD4b\)e MFvIG/W�EsO<.y?MLq �KPdh,"^p8usB9 Up#,:jVjfi8I=2@CBePmm\AMRZT+Zk i J{a�Q@|VdvocDc*R2Xc8N_ΪevqJ)>Δ1X< #,Cw>҄� @JP2T*)o!5|נ~YAME-&/  ~JX7rE"w+*M 4C,@K(i鞌YoӲoU*K6^02~Ǽ`Z!ⳤ3B~0 bC͡ VG�sݡuCP:_E %K|VjD�C6H Ɣ*Vԓzw s&WJzZPsedg/d���2Rk>fwY[V]R'_ymI K@l𺸌C QtJe?% b;=۷l6 C.O 3R )2ֺgxjW!,"F=*D� aXCD+{aZW2Mkr.'¿�rHw[9fFqRmց.Th>Zq̙J?(mB>;)ᗼ*j=!zη |;n23t.+)q̞$Ic:G6y( ZT]-Ea(,Ip+g~ėXw g5ܟAָgn'Y�X,}ywlnrXAUaED]0`]T.YEMU 9Ir7(Q|c&P:ԡuCDTJ!;K E(sSR9TLQZspV)q2*WmI+6Y=|_Q ND� R{Г@ сӶUW c(*3~h H͞E$>R"R\BZyj5=�Mtb8n @k%IT +9K$avY/` x3FƘ8IR#GuuQ*x* RQE�9: Ƙ8NR>Mg+@hM2IJ@Z!kbh%C�! tU yzqoS~1%0||vUVի5EPT҈FbZXTՁh 69. MDq>\iVAVH y$tȖ)ݤE�0-/h-e KC-D|򀄊P)6BEƄ &#^|YA^;Zk>,C~ hJ I)A.C|`3& v ?Z)n8o錸eMy3ܿ tPADH (|148(mЩ�bNwrV[o:ԡuC;<.!E2yYpK?`p^mjY͔A '-.@YNuJ ޷Gya$ *,^˷{8WW b Tv9.r'nhi7r}� E6 [ד(Z+Jbk);]N2WCm3 oLGj5I& *ZVot%6棬1Fhb^2�Q�98J ԳC80;Ao5(,x,c]jZ(W*BYw MZjhh!n$ EdYrjL4֫r =Q_i*BjQ̦l?%劄�r.${j?4'r#_7xԦlGyekrۮٷH-"ڥAСuCP: t0ĚꙈYUZkw�u>ߊ׼)_Rn8kez0}Q_j5т*Y&xPsH PqpZB =c0b rK0v/bxY-D'6G5L I,8 nL1&METTPS!mMDMLtaN+3Wr$YZO ȸ03X끍lMPP·<grB@E<�`\%NDȜ^�U �~ 8䱨[(!UÈmqIF`*@ji5(1:fz 9)HK 3  /f9: x4ͥ*({4(X+ *޲ pY<T�DTK1r ;d>%bJRJ"&!+rEO$}m*lyeJ/4[;goRkoHoLο1{ա7>u$CoJ ҄JݺxZeM�(| `w\W@˸JLr-"(zOqz�L(SOl3e.Q7efB& j1K ?<F�%L;%8!hTZl\TM Z-MH /vrˁp(BꍮnCl( =JBDTY0(_k}Dr\(B2"�37iP e&|( o!) V- UI�m54Iˍ˽J9vXS@gBY6wnX/0|rBk_v`A:ɏl]Wl!"cP�`#۔V'*I O.yp[Fb 1NW#ol_̂\;F* Lotw9Uԑ)?Kzz}Z@;ؕRָ^OIJǶ/jUKj(jrfX/ty3DF" *g`5eyuT&;� *s@EU�/zt-! p4*v_Xea.hp5Ԍ-0 Q"_D.E:E��/Jɱ6MrjtYk[ʕ+$k* 04h4%/HXIxAE)1 *ka{sF7�gBA� D@Zh ]mWY�d=-m*�.*RLMiAvu I$}J"y $K3 Lgq4d3Hr8Pظ<0]|o?H.g6Z6!׿ 'H) lP~? 6|3Gu. [�ғ5_@a:ԡuCP%)isymOC9[e 1UJy?,/bs0X7XA5"RZTLVsAhNVZtRZiY- (1j8ՏHv2ٷCHxU /S)e@W)0nEhCTdl+i*xXW˞'z8y?Q8~y+Vt۞ƈ)Nd� P)Xt,ƿxJv`hs9 ȪoKh{}`,!9OEdlI 1IZ^v{m�Z9� 7&Zkʢ$a͒ N*K[Ir憵;ϩu?P9Yģ&VAke IW3ܑ-j)qAlGr`;[1QsCP:ԡuML V|zmA3_ӯܶ~T^]B�RPk" oل��\J; XXYt|σTmUhYy 2ʗaxrm9(vsJ)0dWXD2T @i26S B `I ,PeaD�("Ph4T� jGIŤ:3YkPƄ)*�dVv [rKAkm1]U6DRLhL@>A{ň=ЁPASsf܊n'|yo'JEǶN�l:[&\>ԧ؞/+V1uW^؆B�I$CBG:Rc _X,ax/Ʌg j٪7 =Tc-aW@\ό?nXJGLbۮvCP:ԡمi%z3W!cY0fv:,"FQO)uS�T]8+; n>~0B)�,bAαfe-bzJoftU.֝F,:uYDž޴ր%KcB Mv8g V*w0 ƾ'1+*DGQsp0j4 B1bcLFՊ AlNr2R X2]J-%FOO^k%q`~%|1n]` KX %Q'1B b2'Eb\GP6ZJԶ:OT@Y8-@Ňh0 Ru(RȚ6 $ y餺<)gi, "("dFėPYj=R>{Y2a>$HK6kВR,$h h1|փ4ғUnwfF@Z3ڡH0,p\5!" .F7;S^|*6T:}('!k+mNDDY^:ԡuC9!x󟩏1 JkVQVASdmb_)4ҜmI)DԹ'܅hIH9 1i� kI �c(IT $Ll +hc86l%UZqoc>PҒ@sΰ5)�T:="c&kɒBP  1_XJ)@k)mVRh�01 �ajS RjnpVqתUEl钁UհR +dK]H#E2O9i@KH'5"%س33 i@v"$&Mj 8GoЈ0KVVU*RRqB(RcCҨPJz k"E<pAo2#2v,'=`Zp蚋-`2p@j 0!ODU4$"DPJ@%�c VLIa%pg9l\YJKDV�dt&`}H@*�Qi�0,Y*m9\Z+kj u1Jb"�hЈ/i@&҈q" JB" KH6} J}qA)J",@by@GQRᙵxd5%;%�"KVU J$�YK�cc-t@jP.ff<%fSfj 1*B <n-4R(U V,ȶR X:ԡuCY OX'k ~�� �IDATB>kGpy"*Ja!Ho#=fG(*xC;X tIϯ�ZD1c$IEuF?!ﺗw~zep!ӻ쟗*uIȻI,;Xf+XܭUT (!9\8X*j1=�Iw6* u$gSU$aZXVU +&@R Y-R`TuJ)cRbùe;r~>/̎oB�WNqmPYID@F,MgZhT��(j55#} 7JkG\5Y?m 1!I)JO9E0dccHpn烛<i I@!o�]0 DFg~BQ`{n( -}5JA*DDI�&^CCCD&<%YT+yyw* v)ϾeFb#HIȿVT*ѡuCP:C袝!!&`]Hf>lP(�aJ[kSsPDI.\V-wSVH5YQ8IyCd��dv!c"FDZ 1QCP yΥOTK j1-[Vf.8 AMI%*.U+aը)K֒, WbBP6}'9(Da$IApP|e鲱cǪ  Y" P bP@Jn+*q@"J1NuSC#c]ףRA�>mZځ\APk] * /KUjDp>|U n͇ף%>}��|*O"@d|`,�Z9)!jY3Pn;pE Zî䂦f^AkðRT*ZZT0l6feL)(֫~Ԉie>1(.T҂kSE(݅.F'CP:ԡuчmr\vj^߲t` H/+By)i/8|ڼoZi90F~%ɣ ZX!ַ&BpAAWd'Y)/<.0!yL({eI|KK|=I0 kZUqk8 7yT@zeVZ500E"TÊcZi CEP<͌e;u.-q,.J r<42"( XEoWUԊC6j�Cvس@!|"$*8JZ(a҂0o+|+uD@FƂ<{9WXGYVkٲe˗/5_*eẇ@$mY'e^ȑ]N.O).&:/6ɬ҂e�4ꅧyM>a\kw_zޕ m֒r;;|OU e55?{i'˟]9/~]DD勖{1,e\_7�p&`,� VmTp� وaTʴI2r' ny].o u@K`4VNS(tځC[ʳHcB@;i6!2< Ɣg1ނn >R?`4**Pjѣ{zz� N 4 /o$".h 8(K @ VB"Wk6IfWV,_޿rFV\7[ Y /"?Y79;tZ@ɵ#W`u¸fr72>YFNOPHË5ƭѣZVEQDLOkR>42F e�t?@S(Mi}!,72@܇!ϿH;̂h9*(P+ʿO1hZ) 4/afkS#Ԟ"fMJ^W*k+te˖qmZV1)l ^ ;^ˢT5΃3p^ݩzRSͣ�oW>׺g|x*g^޴Q}:Wo[#'v+NjꫢaYzꌝu"3G߮x rL˯n9Co"j<~ݾuf?Siupox-k5u䑣 _KΆ  z!=TN d輋LK]�}.8#) I"J9dRs1$ߎKK6~T:΃f~0ooq>+�h4A%`<jbh܇�,kOIfAT#5Z+WJDhde%r$3,\$$%Y^\ F {XͲ "g@i f:TAj6VJU׻JeW+}+4j|Ƅ,֦c}Kohs: mW\(`O~`ۄ~W<S舿 Fcȑ===Aq̛<- p3X`0CbW<W$BүL,yzm?Υiן&u9.2䥇Nsow0{Yr|W{[=6]sb\W{7yWQu*kXCO]qΓF{&=:^yk#7zq{ G/\-F%݊P Y|gwyO?'.nSF룧+l /?D/k=\o�ܣ<dp_VGnؿ8=Xݡ7+:d_6T#6sn}cwS'gh: MqXUyx�J(=T el)q xAQ+ +(N-:?RcO h[kZR@ A#hB€,)gD ibϥkk!(J8�)YohQho? Zy"qmJ[VǠ0!k,%R2+TM0F)0C&N4FwVLZ3h$d92iV*.oWRP,/i-/# ZFS:u_s  Y1 ̉H)hQh kAPՠPC6z3�.7PI#"㞀 G?i *d"Ƃn`b+X�E$GNL* BD " %PN#_wYC"" *Pj c:\^ҢV|@$fshh/"i?88800*rN 6]kZKfr;CJ|E.62E{\~رQ%{G9Gw=-_/w-�0'\?[u!K?�==&pq7l{{=W]t7~8'nDcNl^/rq=q؆r)=G|�H#/gp9Fo1;3gJ~ t=xȮ{E,ftW?KKOqCO_=cOW^+ qMulJJkɾk~lovyDkduޔ~(8w>3w] N`q4?*=tZ�DK;XZ*pa7f{™[U|0UZ( X2y͏dD>v&rYEᢗo, �؛gzD _3UFR.�HpWJ)u.,f&yo@1Ɛ%�ݡl,Hk,#/iez@Ð䳿S6]aI &N�V8AZըRuuK`;9tQ6( ; p|yjd#Cacnj@w2+qQs]g[V$\o(ҡr<%,הQnrOG�k0r?& 6 "YKξt{=G@;);7·9|+5$k�IZ- k9܆Q5xa>Fvg9.47~", RQð7[TIXc�iOk&W}C{̘.> /? _m?y:yڞ;Ɵ޼�9\xU'+I{·oڨv:-Ylم_u?t'w:?sR3C=g|߽pEʅ{u3&mqW>ҫ.c`_aoӳѻgҒ/dͧOaQF5J=޳y:b߽ޤz)=GQϘw|⽓GLZ|ݯi5ḻ):;#q=x[}Wu) Z7+j�?ҋL��+价F7Wp.u�Zz7i:}U �7}zW*uԟ=M%ݟ~ulV1]w;y �Խ7[Vc'm��ɂߝ}k>7cCݧFM? ^Q 'nY,F =}ilQ;?~'tZc^@78V3e7yc߾=J/zִ*|'w>qLW%~nz:{m/KO]ѽևɱL=b= wڃ1�+7خj۸h϶+S>aռ=xjXާ~#>eLW}?yfzm<zM] k6nM w4o;c S!g5x�/߈g=T|HvÆ#amcZ`axii>3}\WgG]pޝhC?8q)kag񼛾pFwL^U?V5j]cx0/Jf};VFlIg7ߨL˟n܅�:$=odݵ�gH7 \}hϖ_[�`_hw~gU.ȮGj~fͫMU6,vZFL:�u&?!�OO{u+ハķo4Zi3'|kj4ܩOD朇:@NGרl\9N BŇUX"$QD9[�PV ( RB`Inl_A&~{519*G�)@2H^ I zAtpNyU[yo3:# D$/B(kJ*nK/9 {\WahzALW"+d!_XCPDQTVRqW՞#k]Z *!!{DOFX 2@ArsIfJqPVsM+{J^I6s4[%^8}w]E)nn*4E@ҋH X ((M )J醐'$r9g1̝{$>{9sfyg|;艖RjL&q@ @R*{R4'"@[}% ( e!wQsG�דhiXDIKDT !HHz돐# h]{q.Lƀ1zq'R[ juHRZ]F\x*TAFo Ra,ޠHnLKbQs,օ1}_v!�n- C)! p chw� ԫ- *H(ٝbyk܋T=i}yu5{"D8.w\( pXJ%u<=:DtG7w<Bb1$qΛUWQ͊sQ2ѵ)}V:JIܐZ/AN~݈2*гmk0 ;ZM5zXR�A,l3}r}(B 6M+ܩm%|3o^uk݋{{~|>2FsgS,��ysgΉ��}Kv∫VΚnS� 7m͋_[Bav_}xlI?tgk|.:rA[~xW>j8�k.l32 }?8]f) sUըֽW:^bf6eqm-pU__sܘ:-'^Is/s{͸vsKeAۨ[TQ�r7\'Բo!6zܡG}S|׿0oMxCd J ^~5wҟ,=| }8^RRp�@νCG19''|/o,Y<==?sfȣoS1owهv?7G|/pޙ=gWU:_~ꛇ{|'/[z-5s<ũ}hO=�O_؜s8wнgu ؼm~ecS뮖:-{x+gp#謟7}YP1jsvyg<pͱ[ �ʼ/\ݧj�;gtGeX=s.sQȨo;c[kezVz~<oO\;??l(vg+ߺ/|czu򗿭%�({Ѻ?>{K| V/+-g}gf{û^@-ԃ][K)o0gM~GW.~v ?xJ9Aga7__jo/ sw?9w|wڅ:'kӏivnߏON@EoP_zع/lΝ]|=0*wS*˃vkw| }U_z~N >ܚ£|+OOwzW~6h6l_@z.TU% Ա}.4M`W?%vT6l�d`oؖ.;mJ٩RN-+uEv pm9䔂�)Gz}VC 06g=^H Q}? R ιy\de|[!` -#6@Y#ե 6dx [G9-juݺu=Q"k81-61XDZQbߒtU2*F>B[u&[ e;m1l|W"a-F9 z 2fXzzz咔2QZԪZc&dd2ÇU`}oΞN bx^V ��Ə?bԨA@OOO__878B6S07ljhXUܨVD7r`S(4KFH4̧RjZ.{ Y^3 &>)ͻU-?R_O;\5Vj$){ĸ!RdoZ)UHw^dT+\>1_A?U?=23b~?1޹Ḃ/W?'�� �IDAT dY ٠�Ds[;{ ;'Π[W\}E6•'>aʱv|ƞC6˷k~v'`\?ֳ;tB[~_Z9v]/^_.M1aGfem-y~y?ƴ'^{NNj_Xz`0mA^\Ke_YVA+Km>v_HG}~SSF_zX7>7Xn6rӮD_ߧOyȄ9 � v '4nqc;u^}6=q3nso`7#3f!߻7՞ᦷ3v8bÛ`OrC_;i?zl {.; ;Ob|K_~nF!c1q\'j�!#wr\~hѣtcߞ9?fY9t>6^sޓGͶ>1yڸx;yE�#&l|wkF�7ha:ocFnܦKez?$o6aQ;Ӭ�la6?9w>z]OǞˆ\F3~뗦 <jfF1��GZOt.);>_˛?}osÃO٥nQ n?nλmunq7y#YסԞ]v8rv[H?!ںFvuwsҶF?zm^dBdLw^^~?,8@&>u=~w7jN=nmxy^[ؓI-{ԭzzmE<>q~SPϛ:1e7�Kb@Ed$.QC8[(H> )кYsÚ,L:?GwF61H"OxhSl`P - %~2E-0zYjLh=ڮ̑rRi6PJ`Ȅ@$B]okkkhm,USBeQۼED%>@:N[)UTuM8cM>5*e@*[Tt޾ۙ)Z$S@ e?4wsAqaut8`#Z9Hql&V2]xn6s|.[ \!o+d iV .q]G8ܞR\ ikod9'Z{iڵk|>ﺮaWTb6C�b/c,&!t`hBSb ^O ys\\e5O0IG##K"c:}_kU$KBXVLR'jjSώxYJZ 5?zL5!27%Z9 o;qspϚ�f yV*cb_ m~vh֏N/Zc~~K3f̘1;>7KoD.:v?3fxTAdwG(?q?.Z>sՇ~&BSݳ߉tnZ ^_U o]ɜ4e҆�Py-'`g<fnH552n>V\ׇ#w7d=v[-{{ֺgW mlAO /]c'0lY:7i8|i+-GYu!ZxI4vL-rAwZ"\dl+B8/]){,"-_|܎Ζ֩tO^0? 6hHGX*5y j)uC\�B{**KWf]S}rbu .^;] ,i!Q0S2� P-WeԿذ-&\fNޯi_}:R;x'{ϒ̿<Z=S$.^bF.ٷlY_ׄqrH>v8\tytR|b劥+O|yb}?3Oz4Q%]ջJ_MݯzmmNbku' 6jc=?-_AM}+V;niywBVZVΦߘ~l}{o ۪?j}!?[>}ƶ:wh7{�ф¾�'z9m_w1-By�1XZ>c#+<>ms5Sc4pL lGEm4cb G`'<vG m""%U4"SyQFQD)�0XZ0RhfVj:c&(B1z{\5JZfq%@bk:lH9]#[Z0W }f+Վ񻶗 $&)c.$-4N$"fH) -A3LZRA"/:B�"C]1,dRUe# j C;lai*UU:Dd(Zz5" jCĶ6-Z\.l6C@BBKq:>%Y [ntXJjp"$%gUUH;i&= kH*'S@85z б?&Ii̠1FQdk @YKqI mN u56ht^ ݼ" A_ Nݏ_xW^Z = Ց[F{Vg _;}{ţnߡm/g}f{f&3Ec iiai>fBi֌[ܳ#W1F�]×x/l&rŝ-7vf=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[wQ*!.ttg.dL,VY~uj[N_H̺FbTc^AI�aԿgͻSo?xNk1W�/z5_'9ì) 0rd~Eڹy i#Đ!:YѬkxWS\pɭ9 ˮ^;~QjDMu,<d'M%9׬�oA,_5o~/js*wSUւEᇌ?! ~~̘C/9@G˿:N_hOz(9O@"g>% a,IN1]{ۧh?mS^n u)$B rޭcM]"4x܎c.B Y=i+*à;c|FcrK2SI<0vBڏ N&81ԶM!sr:Yv6`v2 LbjBeKqDOIZ$RJ2Kηmm nkkc>h)Ry1S=l6~ 0ڀClƨ0Z:n땚yU $e55&?QB)C+-.rĤh$- ƐC>"%%)?aQ(.wD:F5|Ȩ#;2ka]cF =|ᝣu9+6|]]\V"IR�P*cYJ`X.f912 3gcYE4l>k[$J%E)[_D0QMJ"D%^P5g(8cǞ B`ܑDAQj>!y:4tDb1j"2Kۅ镽CS/RwK*R X1!0䬟wcWs^ZVkӎ9C3-..8g#lq OW^77?b ȋ:}V梟?` E|Dpdj6bOj<=ḑ_sl~K+KU֧[oQ?�}]?\G!/?~3ֻjsw]}}iw~qP|GGqѣk[Gx+ٰ×U1{h#/P�@a-)Us)Kp|k.Z%a=w.ʗf.,?ofAvS}�}s3&N=_>7s'@+qG|FGqG׊_~];֟ڏ=u/ +>o/{{@!>x /k wM_i`'أo\?WxO^ Cǟ+?:/_u_3[|N>{8a%7U>N񙃼{/ז\SX0˄f2{pd~ϻy+V\Ks@'C?𶯞 /_0sƂMnՍ'?s>ʇf-Yzٛ/vf58;{/^?2gɊ%s_i5Q}5Q>ꂥ _˟};;e^]R TZ TֲQǜwtgzIZtoDe,Yw>lSZ/.b)' {soyiѲ7v9W#'sO,\'{m 9;sħ~pl%s^BYqQzsų_}sEGfju8>I/sc3.]w\?ۭ?}�or>N~vo.[ly#<U.X{jշǺc{#F<jw~o><3GLIsm OvJ-Ap!GALf6sdi`}JK]dn!9Gb(MU)13lL}6 TS΅{k[#:4Ggxm_q6ahɭ diW*;zrSnc$a$P=߰@&)5d41&\GgT*RJ7;#Linh\w}=�-dn &>&<!nO^_:zf�q"}r2Dđ[Y9t(^wQ*뀎Z0ƘD:nT�#|ff \[![mL[/?9sXRC e$!N3EDO c, ÞJ16.aOqҨK\en֠-QSv�pM'&dTz!{!|GD3ZjQ"D$-uشUbX{tl67/6כWc[[YI.QpmߟMߴoym7ҽ&os_pՇ��;ĭv{^{Dž5v6~w^ԭr7|n<w~ί|#[t3Ӯ{�fm'|Рt-Mi.zkμ[XawK<3cIwB#gmŮ'^/L�*gyiA:{܄#:{~ꩅ# !7z_vc9=X\tq7W�ؘO�v4Yc<}d?ܤ|fXؘSoF<'N}-}ˉ;!G1�^~hn>~#肧SG}"?N4/g~x滝y<λS#?_Tc:WnW4i/?3]NtOV~}Iw8W\1/T \q/>l;v>">C3/ tܺ'wLbaO=zNٳkTn?y2O;c']|:5èS+F_Rv!MK}Kv:'|K~vQhWN'yiSh±'~�P}gf 3Κ٧߼h]f}Ǘ^qn<eBvKa7e'~[O@L7|6c¿ [Im)_wF/ZXaV<+hkcP־ ;__ɱT><C٤ӳvr.mG~sFᒹ[eռ Q[Ufmu!v 'M+ܷ pG;ش/Z5؄!m'^y{#Ć>^G|BlUF"IHDLe?@˴`?e�븙xIM92�gNPϚ#BPVL @bܬ.0G$ 6F]�X gO�1~\5ySÍp!IVD}KnC!py a8NFJ)Θw\r" (p04t?RQ y̖7eY}R� 3bwkdlJEo<"(ll2RUf_oFpPOWS)#F>Rv6zۀ1�`zhUS sj1I臎g(f[(8iZښMq/Xq)u�jx ÐIK9\dJJ�z~E(T*u $<EQTRJe2�sN9Lp&8�(ddXތ�$�N=66)Pu]]aPGGTC ~+(&r0 d׏ <ޙT:1AjJCϋJ"AcW {Kq݂鰹wc" kۯBnTVF., ҟFQTwcgŐ]vAcgMO}�9_ZxǷ` Ԣz?{LX<9͛L~Իw!yQ^N:m_}.Sy[UЂ[[ug}7nܓ{TVAGN{2vu{zHzG^aZ V/XTw{r;=oߥR[fՑy\teozhƟz{X>_+~A'Ld7AI̞>?C||05 Bcn:B#L@ Uۑ>_)^M`j4"�6 roPtX&ogmQ ��Yhi"+ Nc$DBÉqeI8!*�F$pxiJs$"$[2+ uc$AOQyW+UmUJI L\؈T;'APtD"\t]&TziE)!K5c~V Սڠ]ռLݓJܕmb7Z86k@'vٸ XAD,ɢ pLh5O?vR6&0÷ʼn$ c D\d5o~1#"!�(!\y(vDJvFH@@2=M�� �IDAT:?s=AP*r9(4EuP�182,\5H xq1s]� w̻֣1rp|NbwRXU3lGG{WWWT9wvvVrT(@)P}P()(Q!c [$OI)$(W)iH1D ""TJ2OiTf L&"97cpc>L?=IywYp^2/N͜=?|OW[@o8/-]_un?܍ZO%֣9Iw~Q#�Ћ]N ;&|wnQ<ιEoNgh[xb5PDX8g *@")[*49J)dI#!H\́Lj:!Qǣj*n4x%0d:vBQH A)Ehw !9G2Ys.Kz"cx�AAgtf"R"R($IB HH 'ED3T�9W�D �u҂x)_!cs aIH\P)ń�}$d:p aH) ԙ6NoZ5ub`ymᚮI)xS!֏0RW�H1d !2jaU#jPH2QP#ΤHQ T9=0\rH!JvqNZ#%AEsT GTUL* xݴ+,Ɋϒ2WA8 .c&^sRFa\"ި0b{Q"x"*)}ZdЬ'RD̈́ PNt=nVI)Ӌ*cHzK\AX pdG!"'J)H)b‰a@fOA1bC�(P@[Ur~+㸮[T 3^$þrer\bYJ{+m9Νr8sHZfrlum88!R+n&*J JZ !�H%лA$0 PR 9Lg1&1 j r-JE@{^Xt]j>VEׂ8JE)>rN Ob ;P B#"WxQEthB("@\ "!8wh6Ll=oSn{r߰liޗ=��;\yGZ69~l{ tsN{wϤ qkG\jA~OҦ|u<1ɫzNҫƍLZ oi?H'Έ 5't؎o^ȴCAb#qTSFJ کGIkF)%1�bD1%+ U3YIPE$ajt(rИ�Q6 چІ G!A2+٦= #㜋$�XWsP|^E_7pxeSLD0D*Q5 0 mmȘ]�G: V1mFAF8@[AEhWt ͈`HEbSc.zokRL?13DIjD$+GF{R]&5yoJm2&ɖR�ja Bu(:2e8&4+I Rq. YGJ)$eK)=&1 CPAPuLBxȸr8rsj<!DV}yq;:: !2e ϕ IP(ppdEZeYqLE___TN=:'cLS-uZ:g,I(dD2:;RUVhk�dY)%CZq5,HDMẌ`{)qlAD5G8Bpƙi$pahhh^[YVJMѡƩnL%X5DDHhL͗\vhT6ёF @�cb[(m&k )ܞωHВXc2tp}z=#r1vlPaB4(KLff h4jDaֆ-ຊ'opfZ`<AmYjT`ZDr("�q\s3 � Q )� PhDŽ(Rg/0W/ͼŰu줭fjfX[bGg,nv#)NڔZ)=YԤioշ{H2 iA* R'EV7yC`йi՜}0x0WAs%=+ I)9)$2dH|wY<aB.Z-${lϸJ�H APCt!NdP,׬Y3a7yH6bSf\M �dhp(teDt}G  �X*%eM uAEDTTaО0+JPP׀8ZhC}3Z "%H)UV<mP� � � � ŦdX?ǹN>kG9{]3f�ou$C6o=ְ *b#T Hjaq5EJV8ȍ6DjLV&H$%_gQ±42Dѐ8PXq5$HYRHgmO  ��PGA1z)iܚ\K=4rDjb ð[⛝�fi\ՕL~a<䣕9ȀxKV dw-'Z-f*R$ԨT=2J)9 Qg zTDŞI70I_B@p'ģ Q_`R$JJ^$Z jA+\gJ+<|RDrޞ+ϭVD edNg=3٬#!D6.(jE1&@{\(BHpyaX+q--Zb <X)H'[!u pT*�XS°V.rҹ`tKe!wSg89"BE3D SM � � � �}u~L6iv7j`6`PhH.J3&i 15@h~K߀@@iKc<FL&LI54;. Fi `C'0g,i3ɼfI_d J�A@f jޘz-hҼ42 uK.N#+"Dq]IȧqA41dȸaQJ)'ahFaPj4is,V bLv@ ʵB�aZgliQBDq4 (aDDcP)W1lPJyr.Iꂠrqq�FLg ("(u=뉱BG;VAP-WV*5$dj J$oG@,T*(WjZ.*M\i+Jú:::z9a|ϓa`|ߧ$%L&�kd&GQTR@B $#` rÐ1DpT)Qp9*EQݸ/VێY Ԙ(/pp\qFDZ BEI, cq*4@4@4@4@ `�HGB1�ԡ{rOA%fz60+{6bHp.1 �sJ1vf4k 5Z~Mu:H�ˌiJD x0ymdnZfgl i�F5jTdisRhZJ`f 4( BDi�->ǞqkQhiW-KZeQiozkg !) J)]+.<n;էNM'xVF4t)3D3LqeBo!�1d�1"U jZ5"B`IM`C9"%šC 1&8"JTs :3@BE )(J1,bIr6/ZmJw|8'B7ejD$s #vyRcLF/Zc+PX,V*q BZ{RC؏ )Kd2|VR'*$E�x,z1FQD0t!|IZ !ޠ*؛<vWMĺ dT'"`u7$0!B0f � � � 2)E1lD:0%:3LO#guhƟRQocjh11N4EƸUɪg0)My1<UΣ:{l݂"�bh6gLMhloa#@V~ .4\hL~u̥$6XgD#R1g�?mr TQ',�1N3G c�T҉C)c/#?P\ 5A*Κ;xr +m͐ɸ<@SI* fq40K4Z`q3hPLMPcR|A7ԤDTQFCk/' 8J3 "TRJFy@0bLF HKϭ+3t `8nǍl嚕D*9ldT]|MG~ kٌUjZT3B[ROuyJ1.�K<7y^wi缣cp__/sJ|6GD ʸBTRS0!i/1f54Je)52f9 ㄰@)B@8*TRLH ~&s4RDRjbL급x\RFQa- 褆�1{k@K0@4@4@4@ˤ`NDC&S=oSN6�F˒l$e`SYR# b3l; 9sǾռ]E)2Uc(9#}�V,t1̀7l++f-jMShܠQ'_0C M>K4c0!ƘÅidždxdk,o[1V- mղmA#M\I<X:ZҠ�PV#RiH9}H{0bo ��huy&,s.#im/a;B=jîTeHVklVEˎq0jy4lT$O78R""]CT5')ji)"\!rmyow! ن0NOJcLJh r\Ez޳٬B 09s\ !`�[LR4b֬Yvn7mwŔV<C ׬icL!E~FE!�AXT I1`OG`ʕyh9/:0Vy\I)#c9%"qCDZP -Q<R0e2I"Z@*NhݺuB!iWӾ~.ݭӅ%w*z]ttcL\9rR()Xmh9@4@4@4@?K[`" . d,zn5^bI<ms7Da � �IҝS74CR o�&.+٭5!p]}%"6 HLYMH�@( KI(VDlR_wV/K&ƢA9| 0U㈵�VY0o%cAi1fȜs67F=H;A3QU+U] R^'�D΂۫ 6$6n\.)Yո#jZVs8s]$By:1F1@f VԨR+I* GS0՚!w=)N^Q44}.kQ jR0ٶYA:Q$y\3 &"jLzt^(<-/;lyQdOCW0ԕ ąحEQDR !A_Oo>P@FQ$ɑ��y52L1#DE1xв%K;Rxa\$I8> '.?øh˷9m჻4N~hQ}+׬V?d蠐JW2^X-V|3 TGGGT2jƹRm9nP[68='j 2 肵�DQEmmmBAG(JL&STT2!u,#HF88٣qΥ "Hw)pXR,rZ+ʰatȃ0uwwLF'eRB+(x]MRWE⠆0 C!0T3θ296>.}09�}i@r迒?nBHJM:wb v6&F'pQGHڦukf7­َ mrYɀM�[R0dDjٔld&+XRSn /+`s 3P)Eq)^")R9\h IgjL]1ted>4.uGx1 bX.=6T.׌ȬsF#6:2 �m7f3^95|+5)Y3XHTOy;z7"7δ,q ֔͟ԌpΩ`6Ԩ2juXDCtb)dʕ g Z*9;x/+"%ZX=!ENJJ%J q ]L}`BG_$y7 ~&d/ do�fD$m$�xqWx~P)L6_)ZERZjERJO֮frDDڑGߏqNدDkڴ^@fT2$]U$FY?Wen|5 B�@lV;FUJe]g�(T*\�I72Hl]MPw2"L?%0�x Lz#>S>g�r٫Ӏ %g Vo7L{I;& LjSBS1M%:ZnxT@abB[(jg .f &!ȉ;y;whi )$O�*m)8g1̂F@%7~?m IǞqjƀ#@) 60KTʹfDc.(0m@ %V& -5fG{؝߯2H�H@@)BnR"%42DDV?+Uf`$)�Ȁ3휢-޶a{4KR[$[DV0OT vM{*{Fk;[=ب3iMFy "lLA ƄlСAf*\&*8IL&PB^IJ)rqN2@qzd֮]+ڸ <8 PH\S","'Z~ɰ q3Jļ0(s)`u<* zr+JV]Op!RA6ADOD2 'Юho?1Q�Z5Wf9$Kb�A2. texj @R |׭[fETҚA3hgZ/FwȀ1# 9@4@4@4@@ZG0$ȝ9f)+0IYbhy `SyKRnխlkڧnup}*Mx)`BqA݁6:6 6B޺@˞VJy7}׏pWVNc뺮+@‚|-�� �IDAT &)eV#"uɺl6+Jooo{{Ӎփ1H |eDH(2il5^P1b!dXHVLoQ{Q-E{%f"J|d-nCDH,%E&~�*{zzjaຑ83}bH*sGddL#/ ZP B$t"^ba ?Pl>s=l6 k8PXV)Aธ*a|d$dTj+F �cw!j\vGJQkfFQ�!ryA@>H)"Ƙ\DQH@'vh)D""u:Ja<PI)bjWҙwy;^$R,4RVL @̲ eUPh@l(B(B#d/c;q3$= t'>>Gґ^y#7lCTjeY%%%na,SFQJtLE51/<ˠt>>>>!s|Va Ad �G!e@^ƲEE/L΃d\#B0֑ �=mAJ:2^@bB d 0 u,Aw%|y!WFT?�!)#-/Hdn<0;)Bh625|O&8.fKSškHU8�xgi[$8:uՍd3NP(eXEF= S& DHd. ̎xn^ު[՟샐zE6UUwoV@ـ&:A9#SFc@q(ΙOMllllߴzZ:Dq2u]=]zBhDqoZpN;efۖDb8*11 Q]G,[:'`�QF 0CfkƱi:)&S)<!CݲBmsNEna#aRc # (=ϫ ÜX;͛y' �Db Ȑ) Xfݓ+0ZqA !U3GaX4|J㩸m"!Nwuu%H$")%}64 LP:PJ MWIGOOOO~MB<Ț,g?x2^h� 4 )8 bD y~_\5��dpqgcDl4  )iOUKZc^9�\BɕNq !n>jKWY/1!Dl-$唧i긾:(7ha"v@,S,YF�4]7 0M.Hq=L)85\RE9*1ƹƙj#@b }SgJV5S`$(V-T<ߛAm@$ù&-�STw.Aj=RV43` Y1tMY𴠄0QJGx/u}7 Lݲe c@t_~ #a1D310Hio>A=&!(:d2⎛N%lS'HP*8m^Kw#lH6nܨ.KvwBPsiT3 J\x* ΜMK&NumԈRJ)bi*y23.! D"a۶A0' C\.4Z%b  (ELƲęZh}B0B"΂OOOOQMPw�4@,sxUs %ʒA/@G!B =(Q$tֈlF9+?N( 8ϕH ��Z#oC<@8HSH#Pp@q@8sJ'W>9OPg+CT.u=3L} f1(◖B<OT!A0IFUԼBHIi8wPhNEe9 `qvdՁbp6_1yd0?d5V$Q !_{#(יB ThyJ>BhMPr'9(jQދ<*lC[g S0 upa$ȫ0 NY2%x',Q q)ea e! #ts<4l8N]ÀnÎ`]#Ri'lDuCw}3pf:Fh!ϳ4{<{z<ףkti0nP$D===dQ8@`4-1}0O% Bt:}*r( )ضm2uS0Lb_@ف<繮ёJ$5,3NK1M!Tگi˨R/Hp1"D<e.(3| OOOp,'AO� M 8"pfZ/T 6]!"�!.P`'o390q3;8Ml*(Z"WE =$ feQ{͓wm(9= <H(H*a,8{Hx\=0F,s,"BRNdJ0qŰ"}_'a& 7H,K I? F!�5˲ $MXB,L$_DxQUC`[&d3�_ ,UPaPF ŴA("G9HIº%>@09H# 1$PHV:y >SB6q/�C�0Y6�8ƈ3"H!W 8 @D48?R4r\YY^K'Sd}ӶtwgW( gҌ � 0mmhrꚷ{cb 4tT8A^ { OR9'///ٚ[:)Hӱ1Bݿd>g 8F as.1!N#Ks΅y6-MM'Pql-! EQq")1(!Ќt:M5bچiTʧi&)Jq$ 4&D^*)AHG(5M<A^JR qsL (f3/8eV 4p?iOOOO]@��y֟r]_8C2m>WFQ#B5g[?:>goL$PιS]ׁ`?}!.2E .B{J,"k/Jl=㍗x9ρ7sxFA2 $F hM:$$㙸Lp��1@ "49ʄU7G9/ v@1 0ƌ3�N'L4;tGʮaiƈR q(:L`Y 9Sast9p s1@X|2!e}LD~5&sĸap8Lt:-҈KB@t#ٜsX I4`a2FPwQZ &sh3Ƙ Y-cNX /C8Pg @bD1a!p$zcL�J�0g b()$sA%P-3%1�Kb& FZJ#1T&B#q`4 ]a@@``�3i)b#<q-]/ QD°-s94McUēT2V'�4b5A9a]׀`)`9$S) #h؎PqK+Fi53D鉙i(֍4TO�H  CR@![ OQ.+0MS1DO$N Sa8DjÖܼu�()ȩ�{a!w saBb =xw0nyXÎ�=ƀs^ZҐA 9BHP<2a_C"Y=RD.pΩ땕>m3p<Ӷ�D*ItsO&=a^Lg RSI[MLIa Cj@)4Q8L`‘IIII8DfYe.C^4𪻯(>�( [|A1dpt&/qr.�'WD6@m^^HQA佚=%#+#*+DNJ�Ey9c]yyo- $\w1"# -  ¦n()@؆i"BOD'vPI~:b!ǨfeǨ8{dXS sqZ<Qn"!"[@a h@C(̂Be�%� Qg D e8xUL |Wاll�sc-W愴2 وUc  /H35bh0|! LB0aEf<GCji>gTh4 8:iO5Mu0cy>b83P67C aXl霹sTwwwyy 4CB +ш&q�q)KՑc2]ƈ]#1}?JqΣh"2 <+Řtu$hDmEﺮ̵Qs1<ZZ8wq ݖ.N@0,g\}eH= TH aIXsc]Ks{{GKK˚ַtttR)Ƙ ;,,D:%$2BBt]4LC74`Zrv'zV/JO^9z[>y֧IegmimR鵫y}L~b^}ekYo{su}?$ٺaK?݊^%<+uy| UH痿 @[D %+|@e5 3[4F D ga&P󫓆bij @^tў\<ZfK@%|M z!G >�F`gT^QXn% l TຮGJ&|+*mV&{A=X$2-Oi beiH2cJ])b\CXDڞ a&?�eqmdYV4-))m[P]DJKKz8EUs.DB8*�zBP5Y:we 6D"&(az0Q �u~Q*qq17p -LE5%"WP)<!O>s"\!pÓc'.R)A !d�pat/͉\{W__<JMӬڼyeDEEEmmmKKK Я*:g6ҪK01�4#}!^PӴoݪ:y^ggڵk7o޼e˖t �ϙ(!㮮vD m}s\AF$\Eb+BD}1NSe Bm‘ʪh )I1sZ`uU ZR1Pr Mg*v$.|�7.kO 3OWp/lX˴.}CW ׷?YE#&?٦ckW]شgl?Еtou~=[{/[osu}?$Sy35<+O;_>s=(b՘}omo߹p{, X >Q1 *3"Q:*6\ ڪ[pӉ"�r&RNZ4 ~P]ٺPqBTHswyR8FM,--E #MsI.$s;4^G@1N;)y|}0B0 iQ.nP(y^"0M3{ ]jj#{37�  (cz(Oqy3;Dgp"qdSUGTSQWV:O@ Tm㜋PS+0@#KJ%mr *:\؀ĝp8,4�jjkZ!iD,BKDfԕy/ЭHq}x"ӲҞH$4MBڶlІ9Ptftl >P_np@ 0a 1:~"`BRZ/-)!J)Ǩ~h%%%cƘ?B IuA@)4MC�dR"f%q<ꛦEѨTLT,X!"9BD"H$y~AdVȂ%Q*R�w()ͱ W[~)6VIî6cKk5\z2;otaE;J浫]2#u?:8tY7ߡ*yص@>;i9袏 wXOm%է/;ܶJ5/ 3w(#:2Y8 Ow<D޻|o]"W'=`Hm5p 7}(+:E/'/>ily5Ce_y';'=-Q{uuJ#w=wa;GM3V?gOKP3<b>1"[o'5 9b@.(C*NJP sTAZb HUBBXY-VV yP C~;�iXi(c[yhBgaYV""rMC7 @ yEF bz1 9=9G@ca3gȶåh L REBa۴4LqL  S74LJ:�ttw\q ,--&IE8c|JZ5Mkoo]'!nQ E=Pa�'" "'qAfbh $Autq 4 k }S40?4F?H +Pu @?L9P@L\sYDI״Jkm g~<]#h8�@h{&`‚H1d"J9l޼Y o޼9JQJqش$3�D4&G8ߏ(e bT*ke"۶8FyG}4HDD7F0R0p"q@ٖFQ2o C0M,bPmT9 CqƘH=u8))BȲ,]5L狡Ċ6+Sy "GMN=�`QFB]hvw{o^pޛ4~u>r�.ziT]嶉xׂ箞ث>؜lMyO[W|bԓ.{-֧>,||g<6M~ۉW"-O~ >/x�_2_M<LSW}*[&9g7٥'>C֥ScC^wb]Ozd~~}s6nZŗwgS?-IgeKW=:9?s`VœM<f;㿧%|ϮI)t䯓Zݎ/|t5>Ͼ}q4}'j1FN7퀼ȼhuC\ 8."Su끲Zy7dΕQ?Ga*feP['39HP؆%~Zѹ+ɲ| @H ZvϒG0e1ΫK,Cu"bgœp8\VVI9V8d6uv%˗B{\aιie$HucBYQqg 'juÔ3rgd!VRY,\bY12l3J Ápz/j/q0Ǹ H ԥP?\C>J^xB^ղD,2m|S �b 4t:1޴,sl%�� �IDATSFR`?FxWG5ι#FbnqҞr5MáP|e˖ǻCŀY) JlK�+FL c41I۶-A}'0ƒt8m4MLt2 Җ ]]]B@!p>u }b9#TH\p]cT ɮp@Y"yt:uVqhXE\^΋)\TpQ &#1f@v LEPa?{65 &mvE'sfŷa]ydh˪~w|iيo,=a='O_=MY+Oox!{wY>yU/~i̽w+l;멧[]uMwpՇmyR�% Vt܁?9ᐁVM+77.?JJKKKKKwv枷5] <-PU13t͌֍|K \vm^^ܿW1(hκ'v}S3 O<ϧvߙM{mX;[߼ rZK'b��= �o}} =<>~~6�2'9 fٕ#]��U_jF\T~r`'Nf1�p܈J[7w>� GX᚝&\Zyx7}`cm?k^fNaCFxջ%%^tȎP2iiI|42dFF�޼;6,|ctC'=_4B.E7z~CnW>j.JPnxh'XtdNNWmwmZvIo�#+Bfv1o2f`m!罓ꥣ3nʈeC]xURKx+6;?oѻ U:ho}ϻ#e;וLlȏ~g>�7?qDlU_?/Nؽԭb3޵%K6*dEv?6e_;YnÁ[5G:,mcsztYʕGXBXF{ȟ{Q4YY7\ځ#Va3X› t(tvZ[4a5M5I<lt5_��l?g =sK-lȁj? R0zµe|l3<��sO}=�Uc ӺǓ =c%v3 wϪ;=;,>s=b'SRZ|6<.,! r7FAVq"UVT\V[gEzJRPʙϨ(L4Udyz A4dhnEv c9{},S @8f9¾<Be9>xO*^*SJu<gT(GuR4˶3lti "c, BPww7 |yZꪪ{VP``!yz)S 5FO""#Cr/4G LȴqN3<EN)H1GHx)/>+4^ڤdeE!Eg L ,Ti�a`C5b aapxM02L9!!,,'{nG5B뺉DP(ERd"زi3cK$:Q� �MDi�#iwwwq9E⌰iFm�€ ο2T*NSJ]WtWx Yg>x?(F> Æa` EZ`qzQ&<9ʥUX�H1+HuDB= �(=$=?%Ԓʚ1=fxrPϮG S2^sxt؟jGj��dؘ&s>�@ro~She6.\uc� 4fh|тu']q??D}{SYr߽Pro& �Nu6?zɶv7_ݖ To[!a}eP Aѧ�[|rIخ'>eݽzf7:_~{ZX�]ݲY5+`-wTKYI{QopCW,uC><gU3�<9Sx9.C{5t}\HRc�Х/lGc\|3%νoe�?N<18+|nƅ.3-y>+[8@z'?:k/~x՜5'?sg +Wϸaoi[qozgѪ<u�c~x7/){ _�hWֶ֯MM37Vs]|wъ>h/k}cLzfڅO鎓z'@=鸇ݓrʹo7y �PxK}bǮ崛>v�xOfӗ5_5t2YܼInyWX0m{j=-y۾ZfGSr1G~~.F1ǜ'K[7~ϵϻzzV}�}EɁT@販uc_Z}m7U[R樟UsV|1; X3geqƷW;~Էo5z x __w)~J&jم/_|K9ئEu-+vQ[Cg>ŷ޽~i WVޖIgPǾX{Oʢ!=Kfۿ/] Y΂̲^w=Y؝|J /dV[Ǘ/Y6:v_% OW43oK0.Rg ? qFhgvs-W޾ m˕\b AW2Q?X%Pn"gYPN A`MV֛{\Z-' n^( |{yq16UFA *"  )1±�ш.ceήn�6%s5]@iʞhEpe: Bd2 XN醂.NsuP�OIӄX,-sE4rX D֥zϨn[UP 8cLH.PI9W`J<Nay0^)O{o, % +<9"zw( _aRZAa.NC*$.EQS׋オ'J99Iĭ&qF)kG1SѰb$D"0p477/Ydu]i:4]Cs c湾b H$C~)uvv ]ݸqS[H$jY!B`�=dwWOw":\L2TzIcL97 qd2iY0@ mEӴX,&BޫwFdqu],RN%0XaT5HëzOȉ1eu.`g~'>RuC$=qDDH4 LrM7s o<19�-4M=o[ݓXp4]�}~y^?D$oeLM4lk[{4':KϾjiX]#gݻ'?FD~;#nsG E85"|Heo槮| ێ.=TZڿ(8jK1o/~C";<7-rM{|y-ɯ|eۯW<n14yλ뚟jIg@op͐GB^y5CX3,˲L� _|i~ o={<yA5VHS/І}ϙz~KxKG_ɷv4M흧^}-|}X[3`pMAlzƓl:[^o瓯<w>[/Iۨ~��nP;xydYs3{bAUhS=��dWTՎ:̣hu/?qw^u⒟{g}9^ql΃=Ψ4aٟHGj4TWot(��dTk{ fꆎ=sN 7^;njkw__r5;6+-߹Sr/]O}N8iom`&9s_6]+ƍ?>�x/ڒS0h'[7uGԿq@yy/ wSvW5|wgm(ΌǞh={oc]ð]'Xu]uϚ UvT7p_p _Y/EɌ<m ɿH?3<yACv?ꠑuk۳_-m _M8>zgc|,i:'72+~EjG �~]P@/ wGY. 3\L / طIL[ =*`W"Q,ب10 2D՛$)tʦ4(+.l~Y+^]Ɵ D Qf9q(>cTi@\rJ,yNyr ]u tSapH9i $NcF$kr{mQҮs1t=D"D#n$J4'.5M`#"s]W(0�*H"_[Ɉ)UVVEq dF|*t ەj\҄1>P0#�9P�`�bN3l8Pp�9 Ӈ(` \@/<*)< Tb!HreTt:-�YqD0ֈD!"؄2-hd}ЭO%MӴBvM]uDCE T*Ǔƍ][;Ν[VVzd2ՕH>cE Ko1W_]Jĩm!H B3�hIrR߰CG9P}ql9D4b`9F@42@]M iZ"LM甙ah < ND@2&M(0RU\/fSXA'guiٶ-SlF+4As�@5 c,ME{(ecC7&"B*m)_i/^0�Fp"D{Ez?|}Y+״y'*^v_Ν;wܯ=sp,NA_'iD"o9NP>7̝;wY,DOBmD4 9ړ5+v6uY/}Gh2껚p͸^[ЖpӝwHX6jض�yWéK 0wNz}?똙K=zy9Lz]IڃThԢzmu V"UNMͭ0q`ŭACjS--L民Xks1+?i8j֮fB[o /c*2moڿqP&*B86ow1Q=]]/TmYewyܺd]0d`o!K$ 75gc%L��P$ T ›w[eY;۷ܮڼ54ppv;ۦ2'靖J$9�h, d׎a>,q}9ޚWˠhE埸2�c)' io<x2B׶l^:T!$ ͭ^f:j9tCG5,˲}»wWm*/^Qno+hbz:Wt(NJAF~qB|3?!ӎlؘ.zw fZͲbڎGv耯A[woo𝯆!7{لzÈOٹa#bO=^"�6A `}K"._f\ɳ @.,†"ŭwZZ~H4IR Vl EU0RDՂEe9*9<|/cU#x ðm0XȗeA6Q˸Anw@00/LtͰp,ZZ^PJc%8FIA#D2Ju(sO%{ J*͐^gg֭[ /T5MБRj۶@a @.1DΑ@#*>pTD ˀ@&4oid+~LuX95@DCV+!;)ʢPo~fS4U>(X4et!M?D_y{,WM?YiX@Xi㹡HD"l|uNE" aX"hkk۴iӚ5k֮]^{lOxCmmYYYۦMeee[6nhiiI$GP{G{,si5{O=VVVr]-//CS#!!O%4G` �gA!spĈ/^P rC<qy_JI(x^K:gRD8aA؃[`EBfֈq7M0 H M䜋891U|nͽiqW5'M}eqH…l8w%dGt/GYWuޕ*?$)CSSSSȝFK|��ȝFi篿_B!oX3~Ajnjjj=|݂E"%8piuq40üߟ\5Wo8pOۿ5_UO;f"{ybBn.zßZ|I|% w}rCߡŵ"e-�^kg.ٸ[e_1ge혽ܴeW|Mt`/|Tc4� u |͊^r]Wi˺V@4+_imc'V��UU y5$W;VPW7ԑ+d85+עچ~$\Qa("ʓ_*ҎH^K*kUkAP QZ`E\=\]Sm~˂D&ܮ\]S\jSoX@pՀ` �w57+k_'g; }ә;F|7?qEUy5yK'h]]dՙX(vj^PUTU 7\]SCͩow/g(ڴjM\ޏW7 B d?tl:Y82dzuAѺHۊ4Rj)>ˊHok6 3_{7}踟loMG5&}~|3^V"O~eA~0l@$f1T!T湡H<+T78SF iՒ9ϤgK[xTL=Y86BԿԆ<9G!΀Q.ʩ2 OH8 \14hq5F�,8~\I0%Zgd?c5# !NH 1"#&." KE">]ѤN:i@q]9d"MѲH,FpQ'hٸaƍ={hT-fj@xu]B!X2r$̳ਠ](`?1nc`@ILCIvpJ*(b"H FuJ DU3rji)6tZ9E�& _d"&n�� �IDATi;O+QϘϘ806m< :sT x< ʕ+GF=|D2ޓH8WZZ>|p۶@Ye Six]m)Dzl ~ww("�lbXi뢝fE׉T*%)L.�^䭨rM fmb]4M5ϣ‚#BGMqDOf!LPh'J5z'g(WN@zK++v1t:v<58Gs9kV|xǍϹO?({\7jDxKXדܺ'4ʹn{{?]z{pMl$Ol :/j]QۓG>T~ʢuK^woxځ6AY<Dm_٦}̢kى^aNwۊY/xKlO"�Y?k/,{y-﫫w(9qzכ�{]>L-ݒ[ln_Fn؁{$~WǷ,Y}Bmƅ1N,`a{ '  :x!w ꖿ-m^;Kk3 N~7?YnoɎ&^poMu7֦/Lw¡P!7lÆo?dJ~t;} deY^tM(;g/zO?*dp7^¼kbBa[1w~ox\+6lڸfK9@^K*;/{fUn?M>7i<=NwoԲy}'/rw^:jNx|ߙy'.'졣P8D̛ `N:,'^zB >ԟyG>2C!h]MKyzGgo[?/muu{KrMxN86>ZֺdƬoLNɍz?Zڲm 70aL* xyKEsE+>:P$=n^^q_Cn]}=E~'_lhYYăX1kϹm["b,<fkc&Wkb9fY7\7*OU?Y;m<wwEAжSH޴KTP,?AU$=*W5\bX=(ߣHTl�r*R__V!V-bɢ*oPڸ!~d0v7X$ sd)9CוVkFj8`A%Uu5U%ekcem1�H$V^]UYӯ_mmeXIIiiiEeeu~uuuhT%C y 9` <p]*'Rply#Vr o%e+ʫj*ռG82PɯA Ƃ {L=yWx'(W�r$88 Py!o b#ٞFe tgwwOO1ƀqmmm8IRr1Q[(/cF44 ~b?ܸe5N2F0mL;ܼe,XO*J;@5V_i[d�pR �aY!<Ba8FDp83!. sq�@P9$ISPZZFB"FIM#)gAnm޼y֭T*L& ,>ĠHk]RϨ M,sq(RL}7<[>_7Eġv7s #.| o;rI/GF�J˪_cei׭<g q>wzḥg~|{~̦;^0�>>8ahYY&&=كKqWW~Y/gΜ ; v;;o\[�xb7rU>.ش ?xumyT{ o 3O޴ EUk:K|c/�ՐB:?l$YOdX__52p\Y֙{4wêDV'Dg⒗R/߾餟f O_V}Cw+}4tK/Ni+vyw2 n}cwts;l'y^ @5M'^7Ӌ|My`]_:[ҟ;o=h.G^6}w . zϏN?ga #|ݟnf] .Wil8ǧN<?Qۗb 4kzp{['?%:K^'oP-9G6d%^Q|){��lY\хOϹ`اĆw~7tO8rnz~7an;tԐ]L+xSg ƠճOLx{7-AW>>{ W=?r覻; m }uPӐ1o\o<pwΥXpé>pLvat4fPAhxzIK>"*>eUrڮxʩMI<v''ǜ:9?ͺFw?3Q$uC9tDhc{bV]gEgݧό}1;Pz2eIH2|(T.6Yi88AErp!d/B\ ظs Gc"c/9z9Uwm3I2 !! C¢�dy" ',O@QD} )YAVQ$Bdr:qd%{r?ԩ=^¼`U9_ٍ3o ȣ2�P /"ƙDdqI-B35ų<^piyy08p`8V.@SXx.˾zts(FF}~!ђM@Qh$-IC0\KKK~6#d54^BQD|Qsi'0Uy�J!"),UNCh,zO`!UFHʕ?:|I'fa^;ފ *^Kn.NEi_ʺA1Qka0XdRUU///ԮbW8tせ=啕dsI]Z {zvvI' wu{p՗~;N<zq%W\;aYڽƩa1w7|5ؿ<WFɨ( b/֠,D/A=:T(AfY]=ْADv7nKC"r "lKsG3ifd^i7<ι'ʲKH"6MSN5d2,k96!NXxA�TM"(^_'<絵:5 OO}Cη+^~c>w/g>{~}BzݟVxz9e;{곾Y'J];v }<'#P{[5sO>i+[\fCoo7gcoح^~e|Z;|_ξFH>pW7e|~Ž꒿z߷mX4>VHɧ:){"{ú!=_Da.1DqYRބOQ"dH""xWK'y<G)CD dkT0nчC̵3أ q991ӟ!̬Omfd.K2GWD4QwuJ5m�c < ;vuMƐ1kz8 BhBWٲd7�%Y#1-#f֜j8FC䖙t5/k¼tԑ\sIP97'fY` M6Hζ?eCقac�W@S Llu ys$VzrFU & LJ"j|HW)AcJGBs(jǸ*yB>մRMgϞnA{9ϲ(o]Gscoػosssmmkvڵ shT5u=X14n&h<e2_omw҉7mm$�ڥz駣ו45we׸Yv5U4Zkf|E,(][[۱c{z@TK]+8].0RvHB?ZѠI ;s}{ DVG" DTt`a8.hAG&=>/%F}o}HǟzƟ]|zs?oz�K3s/ftNRVQss?-}M#q>jGeW¿-Y,hAAij% 8\ID9Lzt/0#Zxo' a�D+xj o[5whkRlB}Dўbz4pCn ��(ِA3mJ X=3کo8k}Mz(4b HkE!v%fd,KKDXK'@D&ge,PZ+"�(& 0_{.2ѭzeee}}?[?ڪEm<TCD>5IIu$9 [PT*BQg$"�4M\.QRMdU:+2fI̷m#A]_ j-姘x,#+ =1Ug86Gl4MsWvӟsC^yڲnssl8ڵcuue߾{{(غi@ok�nXHdRWSЬ~RE5iVwؿ_>L^8ʃi-" Uu" (XQuաp8]#"ZĤ�)p+B-<V8^5tT$euJ2Gp.<U`7"IX#R| ��/~boɅwy7_pkk|W}W�|_Ulӓo;bVqo[u<[sڳgݺ{[oYЂ[ +v~fNzO'p޲5ˆj۱E@u��: [XN9;+)&`ZLWsD;Z#dNq̌YIS:~'ܒIs)!ǜyZo%:QQ&sm;q�Z2 j'(9DIޣ OI'N-~jTľ MxZM14M}9g/̵c k04;\7s| Ő2~Fkkkh:1qZ.6MAZ9p;&H&K>ʠ@$$u֠, u%r ])�. BCZ�@ RN�h�Ļ&e0uř @VJ Lɘ/4D55axkرvNbEU}4XX?@Cdf:sMӬVn +rUW]uU߸qܹI=t[ ߻H7S) \uWn񫫫+;|xA˭,WM`UU3vTWUU6}@v1 # .s�1 sp8ޏ5e$HD9lS9rf蕥>uYteYj,U?puD\]]VU%"ZgL\B2TEDcC):-hA ZЂIѕ�ChwkO} �1ٻ@D4�#O~c4<"#pjPO|DC~Yf,3V*2TTL"I u��2Xċ![^7"S:󂠎kAgPkb7ָPFT!"4T5$. #ht&tŝ B�Zc4W( "*@F ƨA@< hBz $@H C2(,Zqmee9ZI1ƢBfFB@D2脑MQLh4͠l!\"8`P�;jO, d4߿8E1Wֶ+z8̘HDLs�dU/:!D" /td*i2�E+# Q (ժCP1NZPtffo0PxZ2 hۘsհ$6J" tz _IʗL;"&땵^4,,{'NUQ j,:RquS48Dع7y�pz=�6M O :tyڵkڎjz3WU) N}堜4uRʭie{'jyu< +CACoơu"2q2&v 1(`hByt;hȚ,z}455"K;`t/@ -iBꭠ�FmZARn Q 0qhs4â(7L`?� 3ƀ{{EQy@Vż"h|S)XЂ-hA n%M,l&LQcGJGj)<Z�2bn9L vE�gcF2+r(іȈNU 'rFPsJ!sP|ѐ:mΈfG$6!4z(R5u2btIvȘ%N*3cA56IBA@kGʐ`3ATa@%wMgpr|34.--F^mmmA۫.%^%!d!dH djpnؔV8* E݃�3s�ɡ@rLF穇+r>< 3/@P&RzPpYa伣tg:dyy�F}ͫW5pOjCǜaDC 4Tuiڋ.hΝeYՕrThZ4,emjiWMY cWoU$~TJ0x4&UiKShnĒP1AF9,�t,̚CLB^7LDdeeeeeiƘBlUA0Id1Sr&ZA5G-F�D6xJMEXY EgdҊ� vp;ti9}# ZЂ-hA Rdž9.j4Pg1|r@4z~Y^s/`r.dh^L�jEOږ4#G?- ") }}ZB�~ �>6GD&y$]�`۩f/%gf- {i~HSeGu:@4Kf~2�ZZZNUUi6�yf90LM㘮2_q_ 申`f>pAB8"d&s*$8EaO(V�� �IDATnju3.NάZ >ܔcx%'1ὟN1u%e[O^[[N8SF�"I._Wf}}]k1^Y?n*4DTW;r<nw}#y1u]:zT%yԖL|So{眈hAp^@P\>8G{^5@33H>ݫMj&gNiA%a[3UU\Ђ-hA ZPNmn<]%?'Ƅ�,"Mrg.}."Maz8D;|?;;C+㇘ƩWObݼaDs@ꢈx a%=mƸ_7;&6CONb`] ,B2@ MDHȖA4fX2}LaQK̬A 3'M7NY:ǖsQdƜuyʅ61! ' ���ΦB`$h+ʬ+gj[8 5J6ZCAĺnZ:a {L&,~˳,KW7B¶:PK2Xkoᆕ=iW]Og++X]ӠJj܎5w#Lv qĻg} }1; ҉cafTdiܛT*3·.mPIVXY3 j0m%.:YϽT)WєL )"p\\کB$v-hA ZЂ*:I(,\9W;ZgY3w M*>`)|zfB@{<eADW8QH3e`Qr cuq %CÌзym !0GV�)Ň#^C= "Kk"' 0#hv%ŋ!z4 !J`iJ#$e!"Imj�@60kkkW_{t:]^ZVjs)L畞ґ7uN·&iUY6֐Ah% t` [g8&u{%/ !ҠIԠ.=Mmi"2 �:lj00)l&D|I't`jSR"|BؚAa1z2ʺc`ڽ{`g?}qFc$vJVzfv�pC#V@8 WWzt:uMźnHV`鐌HBr̅ɑj:u~qMĉMeZʑIX&$G5� ‰`A ZЂ-krٞ<d'o7:i3oP�ThPAA ʝs(HD 8@};Ia4,jjGp,>̰`;Lƙ-i7rkv~g&u3^��~9sQ*%<\ ppwWj,IwQaFhh!LV#$HgmgD$:cQGeFICa\h��i]eu1 FH Ih$LDAbA'4@sn7,(}jt;ϫ#l 2&k)KuWaC "ksP}siteY70 8 k-%3o޽c^ڲi[*fW?04M' Uh{PHYzjqX7X[Y>ve./ lA"E㽢|P)7xf1>^݂PU�h VH f)LkQFoA:I•"s4ZBCvL^FG;QE3hB0O{YXق-hA Z7EV2O ]~ 1>i8K1Swt Tp+c��K5a[aXA} QxH3;j`@DH�d@� ͪW|n `OLp([,H ڦ TF~t &E$ r3`MuҢE6CtΩ#;� fW+ITh0i5vڪ&Sgs�HfDzn9@LoNJbIH:3۟bt|��ڰ. OA<zbaJwM|(Ԧ0UR\AZKe(xkD$=`~"H)vx0z–шVVV.╵Օi[ k'P'[[^WWN8bܪ2v50! J9ܼˌ4wPc/Hn$֊ ?feeMJ˅Eւd 80by|06!u}2JJ,BD �c_:}c6�]tm]E@juko ^-O Yw$ݱo#Yk@n?�~�Z΃ t<ɣo0@8@hefs@vl2ZNb0a6xaMŖ>ԪIgaѶp6s%`;"ijxNGo6[TF$JYZ83b4;+[1"P!?&evj&"vãc2"bD`B-!"kq2رhΝSϦ(K4:8g-ۥ@ ,ke:!Ђ� @N[aAChɄYn',U[jZ qEQXkeSw-)J`'3OatYѪnlY*EQ[kUՂ,NXz2v@ĦivԮn`y5u?`TUa`d\Ty֪z]Kމ $B J�Ri3x53Q=HMrfSea @DNDLkm ѼEQ0jPsVK4^rMkp^޶ w-蛤O}C>{?-$gAߑt;VoL R^<dU$fnS<i3*Ϥyj0��m:N S<=._C 2h0Z 4 6�@ʂ�ŒD"I,v0ebz&Nc-ujN8ݫireć�00xyI}ɚd!D;xod1xV_b�`$ӧ¥j�oL_A"Ұ�HHbPA]eYH@2wDG0HD[CNČnVW꺮,KZk /gsDD|]89iUzǼmuL5"V2p2cfՈªDFVK}N:0.DS,> C7+YɤLsFZ*q�TUA֖f'^`^&-+ @F!$kv:pE YSJi~]T EI`vwMC={vte^VLQhkR%{?{D֖B웦 !d z)U vic-$⥝`�ZjkUvvGvU7p6"�jĪrY鈩?:giI'-{̪ Ę~Y /,R.hA ZЂ;,mmm]~g,# ?#v2: EYO5uh6:3o'$16ʊ{外]SiM|ҪDk^(̰f0E;#.9 P^  `Pf=MDCgH�B �ؒ4,dO&:QЌ5"ꕜ-E踼|Ν;뺶e)RSIJbΡ m| B.Z.sΩg=PtprmIyP%E7?)O^=5 bR=B~{>N6CD$˜@ -(CY݆u DAB1ă'FX4EZ,{kG;BjfUa_F3;Q<ϳYmOMb%cV<S=-߽3A,8308{&taCJ,GHuyb?WqA ZЂ-G?A, ZZ�%^xGh'i a@ܦ bQ:j}8On9͟: pi^(M3ΠY8-?-"s%H( �"4�9883G`B@9"20| 3{L8GTHB�av#(\B"PHd{vfEQ۱!)hJ Lp�Gb�y̥Z)3CclVY0K}XZZmlF];v$$\rהz0TQnrx̳4Xv©ׇ8L! ʑH4r@In#'3ʩ<$( A0:U<"hY[��LQ�02AD|"޹j*M]Ok)kd}\arye19 i4읫YC`�$iB+f]]#(1ibU�o3b~ bOQ%48=$@ݴ rB¼p? ZЂ-hAw,Y[T2;$[Bt'6-}Μ8 n52\HNkr~5o( cdj^5`dI2y*߇c B`ŘHѓ=,$ Ya>˖<&9 I_"|+2$> 3m@kVm/CV@|E`g֬u]knPVX1g.hYIӳ0 tLO!PD4j'-R𡾃CD8I|F:,'OJ'Y 65@D-={D֦je &Wƴ{a, �9ރsFPP�-R3mI]k,L5kٻghS@F'UUyn%Ɠ�H(e q]9'Q _"I'Yz27͋RQF| j)i&EpL;t%_;̂-hA Zw9a4'GoxXBYpv-Cj 0 ޜ6tO2 ckɷ$kp[TIȀw*Z֎ ގw TZ`�12/t%vij8y4Q=#b cK~˳0 UR@DBB l:;jʀ5 A-U�0"es"r¨`;trxgꓼED a-z�H]f6ٶChHI3�WbfLZa#:w;˜u#6|(΋! $">&@Ģ8r-?{pқ88mgDa#ijn'D~iPlMoܰrR׶i*ϓcx睓<GPj7;щ2"Ę>$,�!!*vEi6g-}~cZie*$0r[m*?ݠ AwXЂ-hA jlKyppm| ͧs�fg�m?m6]]&�)8Kch7kZo gJ'%*7S !j8 �87ez 5&mJB"3^iH9'i} -?[!A! kUIDZrڧ̕= EOĺ\f>I)# G L *bSf};Zg $M̺D',K 2MWD͢('BeD4ѧIÞn;taQ&)3i9O` &Ζd,˪`6TJ_& 2bEșKF5A % BH *΁1dk Ari `?tѴX`r#f#1C['v@`/ s.'B1KeڵfA "B*a!DEH;3r>%�,IaI$̳f*̆$߹#,-hA ZЂ;-o>iw>npp VdE�BU=fxaXk�3r/۩ rTz}rYgʎ-dz�5�֣>u4. s."`lԆ :A/E,LҡJn{ jQ!` qh q&  ZUVLgM](̇lD"):DL h$9DaL.(IgT"D<v{ �@�+++kky;;Sa.LfRIWLzNQ?A5>"Hs^Q1vF*g-̝f1C~ADFc˲[,ιAj_ЃxbNSv0~녉�&$/<0#A7ؚwXIIQE)ˤ+E9TA4$EZ<ȶ fuj1ꌷ]EDEzݾہeQ&/II���UU[gnwlf[b-hA ZЂ; =Ͼ믺ɉwӟT>bgoe&tp"1 5U<gu`DJG8HH=.9Sc:b6Mubl58cGkm >j2 <^.&iwNx`dϥƘ3?'ԍY  �ķ|Z@?֖etbnfӔozhB-ι9cSŠj%E~OJ Rr;Ȁeb(6dL7j|ޱzӦnfǮw=M}.$va,b3SrKdjH8�6KOjtu0AbfA|BD4HLRu%mv8ƥB�oWr9si5*Gv|gmۓ{m'�0mawNDKtco\Gk9pqE.Y(&c7ڪ!dI9nX,χOqh"-}vos)Le$nL$Y}D?}߸k �hK֧݀]{݇3- _w[TS_ yݵ 谬o܁>kr+o\__6-;:pMow/Kx;=7_յ5}/[PLx:DF߰x7q56dckkcޛ6'7wWahscq0DB=7ya d;bL O2ƐE I͟,Y1x !dH<xƅă4TY* 8qMin9X_,(-�۸Z%AK"~yC}P"k4$`5<|Fg5~!gQ hG�� �IDAT@�4 8V}h&(=z9}A �i|9v A90ph =ZSz)b΢h TiL( ْdBGh ƃfm¢W 2w8nc8"Ym'[2l7$( Ykɴp8m&#[Neb0x2h.n|1pRTBq% B�ܸQWQ%>�,%"2�� H�=u D/PXc�{` Hj �AQYBҾ (@ l{(`$]Sg�I4g avM]]Pzj CѦ!E}SPL/ F1F1nkZ >z5d kĀ@^a x?mN^o,<k< W^ӛnjӗfXW4u5 YyM֨su*  Mm c`umAL�qZ6�u1Tɘ�jm$j8`� iA큐%�)D*�AZ70�R֊V4 !C(ZFm N D݃ïW-]޿{�o֥]?yf筬!ʹC_}:7GͿxſ,?lO?yݟk谬O~{t"_'?ߡ r>Y9/~܂zSuOznZM|-7 4,puݻoܿwh<'tsf9\X c{-v=DԺ֊U10%دSB1�ho@k"3ialwL�[󜁷S0ٜerfhE!cGoH3Fj.<Owc= yFBìͭ3Ҥ쁚G] Z#lP;6j[#qtUps5̌!+C!:ifs8ihdU/hオVbA:Aբ(ʲ<D\P{m?4MiRRZIہ q#3D(Pg:Ps'#u|P5yO!@c A( ]u]b) 3NjC6:)|rA< bQMz}ovp8,BߵkW3&I,N� 67eIpix7x`MFz;W±? :p =*-Eu*LCc,P ;A}al]c沴(u+JtMv+;ۊ "peee0X2ι(XkuAV$vQI/$04I `x(];&D\ܵz?^uMa͕Ňu`{?ā/O͕ǜq̰||x.z=G?^7{ػ >>š\~I?(}{iS^m&y?g{Co`wmwo<x%c=A v嗷5ߥ$yWdE_>d׼<M3(_w]o~KӓnJods<QJz<οavNkWg>1Xֺ8 ox^%$"V'RWWKJYk{ް,]wuS1^oy0zDQEQhBA!]_q�H ’hz 6,4 70g!C9$ tC~M(p_�@yC9ԝZH&FO0:�{Pv|W!0gۋaNgΠ�j@Q= 3 k;c�B7 YLێgGm1 {O,-/x<."Gn[WIy &�{ދ1')sS- 5g*DSYoW7{[}M&;o~4 "&"*@쪺s8uB*q5tglš"ꉪ8˲ddR{`߾}HR7S�(n*1zYgy7pdk6:}0Z(/ u-QZEFko @8wu]x/hAgSUnODޗ=oOꗓWa<F(2YQW$Abx0I2&t$>lϜ/ӏ |ɩͧc�K_?ޣ_|'s ͯ=k11 O{O\󓛯{w<orxE_ϖgUĎ?ʅMMϧz9?{_?9/:/W>o.z%OO]'ײַy!{O놃JO8ΏCVk>u}gf^?^o{џ?>9 +|?ys޷޿F>Ï᭻+r?JDŽo=~zmd#iO\y_w~ΫoޏfA Ϧ{ӊow?B=SQjC[] yСh5'h4N9W׊(j:Ɖ%̿jѪZ 9$K`&b3A@&UVGotq<,b 6,1Λ;ބWctGw9'm)CNDd+uxDpt3apA>e;|83sO$è=IPީDgMB>ijr'w.\޴c)Нgc OҒ. k()@.ꭍ1:u#Y4qCD1/ g.ayNM`TҗROMzMy9 v1j4RZif2<+IVUToι^>LT>ꨣ{qƘht>޳gϮ];~QHoڻ~g~h}USOnzEGDMӸ޳J%.Q ⭵:wiy *lIk:9U|S9`PU5J亮(yW(Cb]޽K ׺ƽ"0oWȄG=~șkp_}^ɧ=W_7 |qmO.d'<{ǝug}؟})g=w}<.|s>^Zk3O^ko`tEWПx++'qޯ_1cǽ_=/رcǎ? ?:xٟ='I�}꣟uGe_yǾqƓ~'a7mdﻟtK~=fhՇig>꼿CGHW!sSW*C/?���{ǣׇoG>p� 7]'`w~e� g~\:x/J_^xA9Zw_؝ �AQ.uҽO7��xُҞ{=?pb?{΃O58gߢH}߿:n,;O8/Gwp?w^;3=ٺm?л>A�<`)/gؕ^9<߶}/|'3˿ w>O;{?<K{Mn;3?9=a?XٯL�r}oz#sҮA[=y89 !^yׇ?/>Sv ;N~ DF6r_xݏ}),'_yus/;S~guY'鴥ZmY=ş%zyW׎0^Z__ӏWN'c{uwC}3x~[9x⛻@\<'Oy7;2V}ܳ:qoA?^;~Qg7_9~x%ᶎ}W|؏|]۹ܷ+Nѻ~|北�oA+?+@6Ϟu;<_4K{cWOz �@pS?�h.SzyN\KO}CO>_߂ #z<d"(,W~n`y4V)7" zX\U:4T{@Fҋ`mKEݡ1՟A#չ]_^X_y7&?eHH�[ ssÏmޅ,1xh�ivx 0 98":"5~S0gqf[+w}<:v-xLB=IUcSQM S S!� |;kWHlUOWz14z"ᐈ666TM@Dι, =hh#TQ^*rrksBMVt18kMF6j,)]du Ό^ Ŝ9ϏW �T)"cQTt<lJ1,)_輪&E32Ee(MرCDF3}){?=fϙcre</_>ϝx;먍CWas}T)z>~tLfxP�yHƠyiq z=u{)˲/u+REWbR;p;PEu~Ovg82id82;kIg]_eY}Dlf2 bgk¨=TfNCEHYӧ �i*s�71^OxgF\r;<S_u~%ǜ?Gw󞗿v_OwL+_NX��s}};�_{ܽoҗx3V�`u}}޷+O?r-opkvN|_מA�Փ{Y:~`|k^iw|SW~7~'g}]V �_zW'ެc=mOyGO_?qxxk|#yθ/3گ.^ՃǜaںE~q޿|_<^}˾򡗞џy܋/}~+~<{_@:d29O�^z9>pWtS_ymIQ'kTFj@dx ڈ6)h; آ�C"*TEA["] CQV5Rý {\Cf#ʝ{s`;nv\9E" >o#7~OSm@v}}a/O{~+{~ @S{Λ>vѿ~rwIϹ~7/~oߠRK<oħkO!�|+ygm[w_ͷ^WM oï7>e/{'nЫ_yͯy7boէ&C o~u?|Sh�_7Ow}/}Uw<-O 1P^S~\m׽['_?ȍS/c~?)|׵^oǝ'?W>Q=}g/n/xˮү?�y~Cշ?oy+k>}?572۟yz7~] }|Q/[?{ ̲~SxC?ʯO|/ J}|c9Ew}?v7/>\s'/xYN-߾;o /ʋ C'=w\fgw[#8~CϾ>~]xu=e~T“so~ᏼѳ;?/z쒦^{ q~RV/6Mi%(�t<JZ2Y}%Z~8hdWqhT!+0ekl1 %,nCU]u-VX�f),nM]JWj[2eYń!CLEg]㩌Y6}.#k<EaS]S=(B+7Ƥi:&bT @ '0*k* #,�Ħ"ǵݳĄof7X`njWpe5Mܰĵ:aDQ:t; @`-af/W2V6 D@~yUU^/\s?Aaηm}ߞhwf&hT&muYv`8OFUq$ Xw;}QxJhOţ-G?\)+VN�fHkXK.reR1ǥD'V|XQ}<liR3?|{2O?(>LFG7Ew]=ko\?OacثǓ]=z3y}Qfwx R#i/+}xʟguw)'O_G(>+7|~l>'-oOR?7y.~<g0??x蕃S_~ܾF"هn;0Э>:vo?Rdmo|֓+Rw^Kek7ߠ+s_.g !4<K�]'oW}N#[_O}/>'5?|G'^ܯ%W=ٯxW]{@)<%/^vك^=?_]y-y+UW]x+N-7'}O>;jKox|ï?��m]]r|H{O_v^uI< ��/:}<OK.~ c׎?K?>_]t_P_/WxCP}\z|E<?ݤH^yɩS^u`�l\g}ۗ&Ee>"إW^z{Ͼqk` \\u_W?^Ͼ۾Ի3=ǹtW??_|+/ ��9bWw>E=_~%_pwOnlnyس~KO_|e`lok/·\p~|o?.t(կ>뙏KK|IzvEN]g}WgpzEy3ڏSիl�� �IDATseg'[‚k<I7y ?>|vw[C~}cnۯ~Aշ};ߘ-lƏܰà pS#,j8`kmD4Ȍ|( \Diz^b>j^Zk~81 E $",N@Q? �BB"rQ-E$1!x+ / 9Z7z5.a<ܕszw~,&b˜_1�QR؁"z{g9_b@g~ l| ł?DKh}YU^vv"D`B�H 3c0ޭtG Wۦ( :TRv 6Mswommz= k.z,&k\0 clk3B�ťd���Â?GD, [tz(i|GsD؈Hr,Qa@80 ]u`RӔ'Z]^5�4E&,3It@J<2HHAD$fx<>d^;wn::u"2fUg@Cv`6NL'ܾKz~gK,/z7}:e ~xssinPx<"U[]j�EDdqyiVhWg罯 ?晇Ea.|"]bȐ""KS MGb3x[ozNqg777btQ׵~1(CYk5o>rS.-�D"3 YBRJl@y IO~2ܣrx=PǎOɵe-_rqn-kYZV M&b�@U׏ycV"[(u=Y"`0pn(uU@97ͪl $)>{,Ih`P׮KBf v$b50z?@!DA4�H,tnczXlyab3,0A鮀dzP��Jp+EwIr9̐_AqW>""@1躶ԒPVn{i@:D$yG)'9^ *9qK1Pw`PU`0N6YjA;g<`X|JbX "vqi(r�P*C6~:% KT}69[zpim ,K凡=pACʟW]f c ٤[ -BXZݜ=iF`ڲ c~00eY'ZH0s$RLg"/kFK`q'O=Ե �t$7UID8X4yEAD{;;|cs,˭-f<{,kcs8hT+ӷrPE .Q�cz^.RpYACsnsᜫ*vHE΄/!,VWA^c{ﻯʯBkYZoWxЃwb-kY˿\ʯSw)k>pGa; Vow.ACe ak�@Q@ z=uu”u|^F14l @@cL:皪 u 5QjoC,e7hOA<.G!Dw!B=4bn kVb#"y ރGD8Xc>rZ#ڋ,XQ:}0E: $"]VBD:ՆN%"j^;E=""wiBDkNx`A(H{A2hWP(ϟ:0 sfYGt(I) .馛&~Q4܅o +/K@.bu-WsB"S^C(cAhӵJB$4 y6v oΪXKJDdMaYbR۪!^JhJ_y]4Ik ,U#H92�o%Y9v" p8O'EQxZF<ݹ^yj;cl6 $|:?~vp<{7^:ߕWͧo(˙MWijs$"~>A$(g"ͪtuc� `2,OR*Fla&D(IbD*sBODcaA=Th@�M0Yd6;yıwYvcc̓(.ppZ)j4殮UE_[6!Zc QsŴ[,.v%ײe-kYZ?]#ԝW"Ll}VHm@L 1Y*Hcq>9t7HJ7 "$AYDhS;#D]hM�h^?s ,"xBl1 {ZΩjZPʺE ;z. 4NtyC1!X?/V@GC/\"eοDef5`&P"({p~ �J[G1=Ȗ }8 52B+5-|X5bu1jD&k8po<GX*?"Yl0Yhi`M 8ǡ(�~k%eh?B-DkCz+Z k8~ŤAs<[=%kVP u]MP"6";vl2F,tdE^ P nnnMz=m!dA4EWɓE[UnE^~ç/]ztvE66٨g(^sR :1$piX3Y5MSUEQ0 ,K:``j<ס@0z_p_ollh MĄ9+zH8LFӑ!ʲ �,Y | Ɔ}իYϊQ%ײ�)lZֲ/5yӟ~XybW �u�BX4ƀzc?ֱ"glubB!.�`ld3y9Giq^-c��Y=_%•� 4zV`sސQz&]`K8R$x"IX�A<0Zk-Rkc"W,[A4p[!rBW _�uuDFnDEpGD�6Ԁt S3VnPYM E%@U3|R/? t @0rhI`Փbcccggp8L("@V hc+i V ';5a ,em]E�dU⥍>{Hqj�@_-XbPg@]I"HuӠgk2qZBcАq3A!`%l+m@P^I$}Xeٙ3gD}O76J,If-#iƹWKNBpyJAIv'Nx>ΏrU5YB4H(`YռDD4duKRz$In<_|TUU1I5z�-�$ X� 6]^ BV@ǎۍEFln㺮p0cvwwY7xê7fX x_D4=%,5^#t@@_[nY|Q%X-e-kYZ]vm??ZG%\= !"fEI]SbO^pJϦ4} k�ctm Me>ll 3gDRB4$Ј[x{ &E9Wb+~zg@\J3VS-?.z_XC@ XF1<aXlQ. C(n("E��v!\]xn%53?�AThCC�$5[$|jLNuЈ"_z$Qh x@<2 Kܵ{@>+!Ǖ~DY4`L{O "b8u[ Ӥ5j)r={'];U~񂈾qة9>zey�!eEZBA7p8Tym�PV`0@hd,O|>O}u)<J a盆rk|MU&\ B$EƘR EQlook|~|thĺ5n0+ODVb\�X:�÷$b&N}((qJ"Ш70X4Sɳ꺩gAc;)r�Y^ %ײ%Ƚu^Zֲ|{ޏ^\'pi}PF$DC6I$ Mbχ}"ҌeY7&!T#D@6or{߇8o]iL&^=t! FQ6CDX(ҬR:ެh"�H4IE ι�[t]Ӣt @^! m-.:}VQЅx>BDD Y' vz_df-2k{bqgu!YZ ,^gv^h#ZjӠ ^=>`Tv(>~ٳgp8z(kze@:oē(8i".J �m";%l6#"F%"""$ ̾/d!@o�,W֧ڍ5~@?-MwP+� �Gi/$At� mgl kB"bfgA �h@U] -$I,a:u޹ʲ"y>L1N6q 1i]W̋AUM XCt:];gs֘,@ Bbm5IH_+gF_%ι[[p6VMSUm$cB9V-�!'B Ł1f5=mloqiy%4M4|I"CZc)>)15{_N"f ]t>_<s--Pl$_Zֲe-k'ˡwg?9w~石% z@5 "PbgYrGD$~S^ZjAS5~:}Oh{k#{Ҕh�dX/ `TSr• ^J'&HӴE ML&o +ρ"!Nj%} .d饲h$BxrxF0wm xu D$~fF2 jTs<`w_t: E݋`p~>bXo}QԿ;ȦBHl}b?hQxKhtD!e˸Ӏ>y hDƖ@AËbdA8j`0ƻyoX\fp:U׃f�IUiڪtɘ"1XMfkkco'ED(~n:MR㜯FdI2HDTUU߯9wm]y-&`檪}DtMj.b9횔KHKN%[4i2w܁c My%2TE(Q@͍~_UUݸb6�,:lgWrԂ,\ZֲeY ֲܛ,%+ǁ[m8AVnګE2ڪc&`d/" xE},d|\q~'jskpe7~ea'M]A{JuXL.%(Xkk ^uî��@�K"4M�6u]WU=k6�yl4 �HKm(,!d+l@!I;gXDDxE(  VUڍY� IlÞ!.a/`BIK#Zt�rh7;;;eY&i*&Ü,%pI#�v�X{ߍcCk:^NBǻp?UĮf>DվȘꢓEDPXg@#,6M],"€�^qi�c[E̬z9"~#/4Ix<~%Ie&M؍,ld8Hmj̆gmRONxȲ㓽s"غ/HMU<z6f3cL[[[;grVhie<'#cC&IVEhv(f;>Jե{p%uN*A4ߊEVWs�PʀiDD q:(B%TyPY%n8�n\(<ͬn�lo6ޥyl4nߋb-kYZֲe.!��/_g`FT G˯f"q[%v�4vZA0ŽPnKN>dR`u9EmLdN.U<pԅaCDf 1$j`\o[Pma)ABDDY<+"glXHQ+4 �TimH4!��! !J8ЏjÎ7 3!𞕸ODˡ DŽ^di]"B"Aό "Z_)#M`XЙ| 0`j$"$(F"$CDdYw|YKdo)" DЅ$q+1jXpIg3_saf4jUae�Tʿ޷ICcZʺ;KT|i aFNAa$e.6GQt!/]BϭO{x Fas@D-^ w4$5蚖3�u9�F(y{IR](+9Ycj99:ӛGho,l1,MD46<Wh4J</rZghTsGԧ$b3_@wi,.,P�]<lFd4 4M5HR].amX$ƺ�MlrM0-O kos^&͘sP%VK_˷zk$ՒkYZֲ{/ [%K/`dVeQ=v̼A#ޣAP� ,Hkt8Eޣ,M泩Ieds~vq~ۆ}i*+" xD k>Q幋qemڂk�cZzߩNP\{=^1swkZ߂zCDPGwuEx4JMZԜ �Ȑ�3{�BB�4/FP 5-+: �K:Zcn-H 1Itl@-&$T`VNM<Ga@ 2Kd@Dۦi�jW`{( E,9̠=~rƬ{D @B@�Bu~hܸn(D HIycϞ=;^e>*Z~bQSVDzaXR.:|΋= f!!D44on�"^Ĉ$  (,̞$i~ "@1HIbQЋ 5(Vd.^f2a*�%!0$�PWc wR CtMk=MXkխ $"8W#,k@1SKt6&E@nA4MQMYY<Uelssls7;g\|i3tP�� �IDATKIY~ʢ]9 $)�y>v}.6iלDD9"nlmZkkm9[itMbM2�FИ9ǭ[�)DW2NDT �`5SFBԡmCad-JȚ󂀑\嚲Im/I2t>iR5FlAȫBC^XC"bk лM YU*E�iZXw}vG@;w׸^^ gB{~Zr-kYZZ9hYZZֲ%?{stYR3JX>!m`mc5E23H`O\cDtlkԱ-f7a[1e"�ts~ź+]�1g͸.b SDm `m3^D@P)"r˲lڑCUi`H wt׷̫٦dDnLDBNXT#1θ d 3{a&9H],+{afn_˝.4!PDk^L%�@dՇ8` bHDƋ?se\4M]>ms6Ǜ%6&Mqnߋ2�C!Xb|Ui(:!BmX:t&o]xOAD�t:̒yܑvHhǼ>DO u]kCc Y%3$[@kL]վn�7NԹ-;�ԖeyYMND�5Y5cǎx?(qIALj,oژ>K4/R!I\�84 $IMsSf^h50L"𦉗..e@{D<B6MSU&"VU5E9}tӭTuSHv! ,3c�@FOGWx.}+.<qN~y>}Î{fe-k9\Ze-.Kww~<'jaF^ Tppd 29<&"q!bZߙfaF j*Qx9J h(`A\6n*�g"Cp]aB⑑C%'KGϞzYwiï~yQ(a1'@eDVǞ�b�5 2xWՆC{1�udb%!! rޙA$ VoZ80op@1$gA^v:C%=^wٓ'O&Ilj Q+$]+q s8T9ZgPZ�@=m4P"8$� v.)<K� HC ȚuB^AhѱwdT5 ai]f- Ǝ\:fJ]i9"gΜ9ydɲ"˲4dskdɳl{{ixeYS78"Q�dI^Vye>O9.lnxu3<~C;cp8\�Ƣ46@�/ V-n!G<A=؅(;fYP`<zƍ7޸F7?-YKD꺖.{G۞6MQͫLXzz['eLOgO'3qOγZֲ/Lѿ%gYZֲK|.VM@8{q*Ծ0-cd1Ag$˲"@ BRHianrTtU= Y!b@+-@옡b:c!`^dV'm}<},om: fճ\hSWf}G�rj+0t)b$7 :$hp@ofXQbBmaUGca]aὡwu ;~x]ת08G"nKZpbɝКW "w5]| rBZd PֱXatW,)B{-QfxgN�:tcPyΪD,%d@9W)DDyn�ԓakk+MS<N;ɴ,+k-!un|b{ޙwڶ ˲)�k3 3ceYF}<(zh 4M41fk{CDf`cXl:^<tьyA@<ijxHGa.)k˴s ��9&zx<o%;s]yW2PA&AʦAUYG ql|pnLZhyO`QCe-kB4kYZr]s|d|$$uuk$.5wlxq"Kkpoo?sߢ.^9s mjgE<_im�sSwJ`4�@҅9ue@ qګ00j3w�YXCx":Ak,@S =JvaHLĈF# P$( ;e @dys[[B8 p?ommyI4MG@;2Voc@|%bBvRWN:x8D ԙYK� xT|< @Dum @ưʯ14!r1l1z/#Y^x(p s9o|$SDFQgybMdzZ;N fYF"}4( 'CuUu-IcIrι^�vռ%ϳzy>lΫ�,ld4yJMӠ&s:tȉ|ȋ4�� #h~k(꒣Q]t6;v9WUv//\ݴ 31] �i9Z:^ŗ}࿽=c0HSO^yżnȚCx=\Z)}_]| tq;WSY<odlђκh^h,!/n[?,/"%X/ģ^mǑcxoqA2Y@J "CzBK#}]p�H; x}䥑X̂6__C.'YY6mP:ٓC @!jlJu_;VH�vJ5o"0 GI~NN0t�Tį@WW Rv ڶ}8tV9v VZDp8eeq1bZjS:GU-Èζe^4ȳ~GH왈fYeUU";sĩYM3Nk y>N)-o81 8D$01ռ4`􎙝07n8NS$iJ<Ẹ}W՛/,gݽŧUie'iƵFD+:wrPA2ǓHD l4ܹsu]k[w}'tyުoE$MS̓d7m&IFM_xIp5T^6m5lnql<19bkYZֲe-\+ WC-x(ֲWB`&QJUI/,cnz2c(��E؎IdT^qs6�HZ�DjnTo%u{!Iw}I :fN��"P4I; 'N4P@CŪI Is6*W(NdH ZsrqyLo{N]'fZ)Y!La`#J,m�YAsj .cN8q,aa."@18zt(C"/ LhHh3(XP#" Y<Y1u[Gk?5Ԍh$BujGCD꺶u]{M0!eT/gsB$Ơ,zv2i:6om9wSy""QػBM%`=qxxv&/EQ H eݔd{)Enҽt:,'|:a_xo �xV╔FEKar)ӑ4ࡴ8^UkۄI蒘Í^^y&"Y~vo6"Vrc0̒,K^@b%Ȫ[*t)NdKWc@CwZֲe-kY `3${4+ttα1k�;Bt^!:�Xk�92$Ij}pdž+ ? $n*w-QLd U�J#kʛ_9* tu ^j;M'ڪ6{]7M`uS ?: #ρ)GU,Eq"yQ&:nkI`66Lӡ"K6Mst4& [Y`+ZrqEVjWuA1a\.F(?BI]]jdp8H 5[ GM]БJTe Z!t]5MNu󺬼< L&c6MS~ss3I|>L&Qe5k~$~a ϧ sUɮ:{_R:QN~QIL&mxԥ#y@(^5-c5'+,@hWu+$,ŕz4eeff<y( "*rkk <˲HSC::6I1ݑb448̦S�%5x<zx-kYZֲe-��`ZA)GvU?`,nm3z^\4wNPjIb+ӒEK/Ɏ D@� sS(Y0 KG_;�5aEc`a"AV$ ~YkDEzY2ЋhFӁ!jF/"4^Mv5i1ItI[{8{gS۔@1B .pBH4, ([D<{kmS7y׮gK&!ӱ0D~!9K`+9K�`QAKFeneIȑAN<y̙-\s9+z[WI뇢 B):1I !bbŽrμ�UN @ySaHDcfϋ"0pe+$" yI jnb_4 �XkFаHD觮n^j-"4ݝf'OF`>1ZMʲz5l6 ܝ94E8.8u,SiJ^J@@^d�PV|e|>-lk1'pfwEkp89{'&s-+n hAfhZ+2vq9ot6 ,~wXrY3 RߟitZ9MS�ǧO󲷵UUռ^XDuMkHӪ!0c-gp-kYZֲ q@@DűDV JeHG,„ *Ct[`{9~ADnJYIKҸGF?nFۖme!�5ohwNؙQCD0@C.<D4@WCh@lՈh#ΤokABk%c@3 PU� 1S,x(DCHǣ2ZÍ謵I;wnssS-hq%ge?PNAv&RsH""RDh?XÕ_V UЁK͎BVt,|p>e'XXsf #"%H,N=EeYyg4m%z3WQgv{iD4Y{,mnh}T`2}eդ9wlc8U]}cLUE4~]~"*assiHkBq()xttn/t( b4d�mT4|2$cϜ9#m^uY9Z á15Dkm8t-Ѻm�0�1,[69Zֲe-kYZ�ye:8 [o@T'0Vivd�O·Lg.^ArM3{ ס-/p[xӺ*|鮡L Uyu|I@u}6@NDikT?�D, %E@m-#Aq"y+4V-]x@HdB Ѵ "&D+`F`f@BDCHCDDYbH@<4խ$IRŹs y[&{� `Fh ӧ_igpMRD��tK]uۆ_@Pڍ}8Dc%AB@$@ŌFj: I!% ;hL٠! glg3W$|>ǡ$Y?/f|4i%)"|>?,Mr6􊢀d<X|5lPsc=ZcclBD΋gDi'NXkE=jF|/F"'p:5[[X$a<7.*>Zc"�cxI hY�BdG_t"c꺞f^OĊrijx̜֠eY?/qe1.xvCD$CUW�E=HOZZֲe-kB5H͎ǁ/.o+O 6VЎ~@Bދ"[i3,-kwƃ%WV�/ ǁ+,ثN}NzbG}�!jYl`齆6Y%F+Q[zԻ Wǃ)"*,s=+�}��$j# ucw!t6Ny$TBi`3Ԏ;DH&m W(GM!."'pqhZ[|%pR>+ېÞA (F! $@A"\Emfwْ%fqߙT}Ϝ>EbjQRR޼ 3JծO_*܌F ^ă0)չիZ|D,篟?#q" �/޼W޾p\y߿z 3o~{DTy-"r:0 9t88{�8qZ�lU�@Fc##p_ָ@9D5mxv8wwwOGy>OjhW^aH)].|j90 `GBJb @ ¡=7}o}o61_/d/j+!>M¦~=xPJB�H YeIxWMpiA~lе~z|ZShw| |F"$"ƞ_|S Uz쩅ߖ{NHmD$r"$78w $0K Ywb;ntz׵u0 POt2:JKU VrC3 8vC?u%}\vǏ� _\}\vn18l.~䆹{JjIs_qUt`ĈBE<$^1;TB000TG@D, �(@1TRVa4ۓ0 P�8"r<jÞ+f(LB-yxxKY.)!0$9nSy/?d4}Z"ș˲̗LiGEV) s1g�� �IDATͫy+�3v;fF_~pe98"FM"��ee#㖖1TaJNoq`ʌ@+5 as͸CeY.G "jC^eY<p˥?�UA(I��W�Vc}o}om- E}( yNzmCЈbU!J@ 탞V6#|8Ow=kMos2I+RuUTUg�\. �kT)ZkaZ> Y }0D$Lf "\;3#�!j8@BO(("RYL͓P+!"Rll1 _x[p`}l ߼yO?"0]`nYO}1ЛEbf9^Vh̗&qȯib_ۿUGI3* ZE@zثDy�ʉr_xR1< 2<MAӣRK)*8Z 8c.axs>?o^~ǻWiY.?~g(eG-EO"} :ϧiNSFy|>,|ލ4M �yI)@E��278E7ºHb\I<{a/>^Z'}|yw�,˂9~rER"Zjt(J"I"�{-Y}o}o6lݣ'&w M3ԕ{z )c]ſ.mGFVAРf Z+[֓MްvngݢDBH~a7R x^7$-�P+�Xw x7*M5KޭX5W֊ܛ�E OVv JF+Lm/vi>{?]9HXʢٛ7:{xaU [6[K椿݄p28!2"B6;�h1 9"qL$j1hɏO~iRv:4s:<sΔS23D?rH{aAt<Ҹ, "@B<r9/\Iq)g"BZ^c3_., n |é qo˭{ *XU\.i47Ux̃ kR Q}h4oH<DT(]Em H" HLBvXFumw }o}oƊ(Z @F;QF9WnHQ�]8xSWx.%tZF#^7o-&lE"WJTZ+&g�<oUکrN[UDYJqb'g e5wD'kb)(Rj U&tbfF *K)ZMOzrRKwwEEOLc=$+iJ( ˶)GR4M?7o]@!zƴ!6v軠2U*G�XJ*W`4^n6W_!o@5ha'B"bmʄq9G�*W( µ`&LR5C j)ׯ_ ø;f \.9,h˧_~t뷧eJߕ<?A-C>|@0Zn/˼,KۡuA]x.eYiO|XuCR^eyg KSJy\.0d]C__]HuCZR!RqA!"RNX)!b7o|O;PN4]}<_42_z#O>}Az:\aD\5k:p[ ߂iPof"lY.9Ire>Р3F@ wI(˗/߼~\w8\ͥ(Y%儈W^i$\(Pu�ٟ߯&?Qk3_ݛƆ|٘Nl\+ ^Z{~.}`;בSٟnBRR#& bm~inh2l@mrD&bwYN|{CM�ɁuPq"2tRWRyݻKYWL$'w/[b�J�53V7&YW>bR[f"ě ;A)+hk.w>hҊj)ރvLx~h#K1?~|\@t�yuƛ~�� *̵ּ*JDAKYM_,)0D4zQ0M*Aw­5\$@Ü[mDz"?._KmOPmU%Q<OI} /+L?h== rP@ 7Ɣ"dP"s5Ү4hþ/ ϥJa&wU56u D(" K~]\ H/ *y�3o Л?">$K j!AJXP pUv5 w)eTaF56URs UZ*sB}!W�~$;)Rc4piŽEfj�AHܛ'HLD^hH�+0'"[x=R@y �*HFj#AO"y<vKշ}D$H0A \�k DiIXFY BBODTD$%JU$, Rh ")傈MEP!N#qc0تk˔RRJAR>2s?jYfqigTk+B7be!3:�@e�@Ә2fTFXcXM(�RJ*-'BLs;vhP3'uh(*{P�D�N:1Y)ZmHGq[; "cih :TJs`K=$@)td@\kBH)Icy )À9 aR3Rke~~t9w\|t:uH)aN$_??w TrtzN)n< ␧׌PJd"Z3秧8ViKOЄ]h]HG4AT"Rv"Ƶ2 sʙ0USS?ի?<~)P04dqw?/xzt~qwӑnϵJ *`  HXѓ1"!;rVMTM^楤d>!!x@<dD"ǧc)E5</\ʲ5.Y&nQD$@I�RN�PK ZْqO5O &Mr4!*)6rk>t0ǭio"\[%%XQN4�"Ux`4!yf*F  Ub&l}uJ u 5/Fj @oB6 ;+RK猴H͔4N"KJ Tc$ [@ʊ/^K Ik)Ê~%JbAUcv%F0wDeDDM.㷩A\o*�zd\ 4D^\6 CEqL)*\ʥRWh>md�kjچ# Kf!ePu8i$-6$&^8F[Vv=D+{VqUYe~Ze-FCtH " 6UsBD}lmrzZ�,dtd�@޿aD# ³7 "mf.v ATUBjD#7 q4[H`;ld2팭=.ÊؒUUh Xm[ؔf`n]@[%gq_+ )gP\Ì2�r0V@mCJjjP:JG24۞Qo/&Fm}@qs4N ٫ȡjӯNC=$,oP}[of%{=5Q!x��@<i/UV1!rgf1꽙LQ'Sԏ׍DzL_ \,uv\Ŕ@THs`DDj<THj@qT6xt2ę6fi9�5pTGh" M/Wk5I뺁esy=` $Vppץ J+i�'Tq) PEhP[+(mFvЎJ]qi8Jb'gE.SF�*+\[IF4y>�|}z$"@y$'t:gDqTk=Li8L0u/HSJ sviY>?}}t<~sϗ�/OOO3ϟo>4-�@e>� '@J n7r,e,|>su7M0<gP&Iu&t c[i.8 `ad`湖~'"˲,??XZ ðq,\ksYN33SX{F Ln!0VP 2e A^BAQ"VK(TDdƁ9#0 \,c~HpZ9yΊ9j*V#*Rkrf(�g V<lA뉅Ȅ>q֑k^3 aCTdkT!S%3@t%ٴD�XZYVXk�5:'W@F(֗7]fX [=G+5c:[F9$ D(ի1 TtcDM ۂnhns.]7>D7\?h}q--ߧCJx׭6J\4+ .]Ej~U1;#&B 6K29sJj-*V*EթFZM9NHSʠʋ`CBMQ{h6krcp{ml}sR|ַ۠пcS|RBu i@"Rt>Y٬q[r3BȎ.[[`Nԍt)O<X7o I#u8bMjz`)Y3#  SDͭET/(̀XYaAD�!Z=pskB@P?2#lnؼkiY~O`6CNq}ûIN[,0N<Oq"(M ,U$QJDj#>HEYQu"" $jYX=`”%J`KY ]k\ctLuLKl<|Td8^.Rov_y;07rǖ-V,Jҧ&a7{aE3b>v|Q k+fnkTKm)f7a#+~b3Js&Ҽ&=p5?o.0 "EۻaZ͵.g""u|:_.ʜLaǜ]N<> ~Ky(<?~,q"K) |9|> \. b(4bZeOcy7 _sAiUP#>Tm X"µ{�kWlx_q\ >0aU列sΧiFieP.KJIˎjj9f$BXv$2RV$U}{(ϷiI<w gUWjd��.H!yD %1S3 p]x> )Sbn|'ڀe4փ;p=B�@ )%�I/ /Տpx RF؇h� hӜ(Zϸ6el,kqKo[A-J\mK}qT\7^v)b'D7U͹[RP`;HC`Y\!v(@8tOk`~n7T q`q-tpdmsY-JYp5]c`Ln<j}tcĵVJ$ jxSZefȀz#BNG `iv Nx_X6l\m@H:"7.ؔ.Z lDU[5nvF[=~ uinOIDaBu 0F3OFX]: AyΜ]\`p#3NUiWs*=|⺗Gn9 ]hw!V۴@�Eܢ5| reB�XJHG^ 'Y;Re h1?7hWP6ZUG`qTuu$ŷ y[F>o^[q!QP7]`t3N*܁STnQ:KY< rJV0z0s~],UnG R@H͍:$Jm6 U~Q_�Bp{6ȉY,֟gxG>\bʵ>nh]м4=-ELQ#D``¦ Ttrsm`sCׯe`J4dx~hA q7I3~QHa|ݻwo޼U̵ǔ:#Ta7 0S)rsΟ|>,/_ջ|Lnte؝Norww'DL>swwy^ZZ*Jp>S94M㐞k</Y:1T \Gx + &;tڠS7K FvZ�o6ITJA3ێ3MFL)iV0ґ+ED~&YvӀ5rm Ch A(DTʒsVc0 3e)� 0 B(˲x _wm]l} D �hf7|_|v\>6o[KÈ_z}Gl՚S G*wA nD# R�j*Gś�Mk"H~Ե "+YɏT&L͏2IHCܶs >ݎ\z:UpUV:}pP\ɭ+ ﷇ M-S_ya˖HgDt׎@jcn)ԟRR0< fS0^ȧ\ ZIbcm_D)pFuFMx]l�ҠJxjS!ӕsTuM|m(`�ߧWƣ)i]K!`CuobN/,Va�`VAUK9IԟT7{f1nY8aw˯>saoLW6g8SDigvnOà":ތRJ9Ӑ΍kI4\0< yqdtZ3 &(T-C5L_¶ Z?s y43Ϋ*%/ ι`_o5Ѭ4)Sjc|< ")KM2L҉Tj˜)eU`jȸ\"׺ hR:ysFK%h *bg# | z^Fhj?W 1/:k*",r>NÏ?O?p:t2NHyC7cB5EF9m>I: zJ&o vkZݘ` 4(Vp'J g c*W1N\=D9fgDOrR #y�<)\ΧsJirΕ0 3ra0АeiZ˗f�xzzz}<-"懯yr�� �IDATc-ע9a,̵ 0 I,<Oǧ.0<>=jYJ)K e!ݦZ-(ȍmd h ԋ戤RJ)D?ôCj�,2AH"!ĵlDxY$DBvBBC@u]֐|="6xݎk) qCK/u)h`a-qR,\ed&MhfJ^Uթ<̨ j곑H DHP�` /sWw�2KJГp@DZ}SPB(άWI/:*r^+.Xg�@"aթZ;xVJLyo"J4QZe|%"nD}>zW _i+JۖcW֤_׮&aُt%`Wt:dڌP؃8C;*m)%$TLDP#RcFΪLn5)4+d'@acDxzlPX\ EzFWufH| I;^ 6 . F:øL_o#.65]L/ %}m;A vq5@n#hؠt� )ŵmOq‡[6vT,TCUEev49ϳ�@0q6XoDq߿w*/?K6(\K?H/nh]Teƃ "Mن:fFr ke,DzMP`wFeC >~Vn@LJq_DEkڳi+"&$q) l a`Σ /RBPY2m@fn(1kr޽ӏ?pɢHRD iZ v"",BBz/We{hqGh~UWJ{H"w6el+>bݻZ2/45߁Z K)ewطT9DD.3SDD9~7M!\n/ϟ ќvJ9>>ե È~җϗ~w\Lrߏ"P7r@f<˲=3SnͶXmN˲䜧FÔ7 �4-<",r5u=Ԣ>ƴHG;6BN46X 棆F-z}9k#LRJ �8N'x 37yY*#rƊ̕Rʴ(5�`ԴIm+DBcb%'�@Keң[:+vuOlۆ1Ӹލ:WϮxDí7p"@&MI^ Qce2|6̠C.;nQ[|b])Z~aơrmr{agd.}-moCow }#^x*=o/W[{6(YOE[n5<l.ȶX4?;qTk?K)yiRN, yrejZA87.:ﳼ )5^k~ֺ ؼ*7t7xfЯom<iqLYz;/w#MsD6D<|1u Czkj[BWQ|Ԙ]/45%9lڄ"KmLuD�]V[kķxt"v]hJ"s7�lnZ!Qr= D5f&􃒗 w?n&uYxpV׺""JXTI o/XƭQTnkKiqY5#)�&%OD'%Ck`Z`;*H5G=V@�}SBk ðQ�t[AD, &dTLDJ)@SnLhNFPkJ;aXnRqV\npJ\Pc \e-ϟ?"R u.RfdBkAF_8kv蕈-sRgQU{7毭-0(f,Ď;HEљ0 ) : # c<e!rQ0UA40<cNu^v~Woq>i'>|92>~/__~xww?N㳔e4 a'")1edr˒s>O_~ڜp@q7톁 C*s %W T*ҟlԈ6ђq8M˙r\zDrJRjyRJ54V#z6;d-Kg~n@ԗZc ΔH(q<a$Bf�r<}yn)h_ `lcXy,kfj< `"P9% <ffIG>B�=UP1DZgF "`s5DX妶$N,d5lL ,a57( �@u2 Psp'No%mp6DjB��=B}t  k&Hyج>o&_x~%{U?SFЯiksc:xf+>/m?y[=Y9RPrHZAi?(' +/RޣbՈ %>F9/oZLxQ X߭%"z+%f*FZ'"/ZYߠs6E"u}ɰ*"l+W$nPq5f"Wо0>S{]>j m)V/ju[)l[{90A LDgYB<Z@tqB7*VaEi_�ZY'8VլrLa8+3V +!ix`""-*]fEc8XYJDF^0=[0ܲ@]JМ7}ʺC5ލ2V,i\gԡ B ,"d tb'k>+3~U\O,`-'+!~; ڭ \ai0R"X|dvtnsWiqTm_Vъ=~`x("RrPȟ] ^G ".ˢfsnG  R[\O[#"Zm@ FD7 PJ9ovO>}q.eYRJ`̵4MZOy1ӛ߿wr|[fqHK9?7o7HT�0DDׯCnGD4v9z)DRZN X풸@ÆlV =8˲TwHDA+� .UM�ywwNy:I{!s&_ ˰1`jA\ ٨`i=r\+-p`.8M"23<aǧpid%˲8,|KA)5VID˲P"d(0|ˀAyk%s+lTנjA bc9FdBVtQ>hgME'{>|]_$"*AtXlGjiCAl-K)H\A jBLԜ. 6VNyf#0CcNy[p'D7 l]q^>i.X|m=Jw&˦/u@HDQHDM6#"JɕV!aa^kn]NrhELY(6 /f,vY$ZSߎN\< V&+l/zm7vpm}Z=@�H:Y9iXW_6R $moksÊ"W1v%lDG>0�пv{bĮWFn/jyԌaO77Y%u:@t:d3k"B"@MWID82<L#3WG n8/_kY4-9JQx &m Ʒ+VoT3tqm9 Iΰyz[^Nn-TY9>՞~R�,So`kҘ[[b|}`_skAkRA4=cs&�h#RVyzz9xw7EIj,,Ze#[>^ǺyBrWvpOu'Z"r<,W/:/W{ׯ͵qtB\� &<5BJ.F-Vw6 (:nf1GEnp͈r.jѠUw[ɭW0!DHH)an"~Y@rB�MayZ<a�LZH�HR#"F0Ӵ#Qqc>RJIXN~7ϟ~{J鰿(i�o_y7Fz}7?}4 |9yx@OyM׃%(͜ayo޼9u].P#7H@;L>Ėꇯ2 *8NeYv(&/pЭ+YECfׯ)</JX!jq +H("v@U&jP8JaәE!N<sY즉K-o޾Eq 3&TBHD|~:  ޶>MD6JrG !QJ�(0*|nIMhz+PVW@ pm4QT!) T5{ O 6IZ!!h?C�̰pugm$b)ص)yq[*_{;`{i+=b)S�qfKIcΟ^zEtx̕봛R-DTJQO5m0iAځ'?wnB3Ԩ~F2Ѵc�O6Zofhߨw Eo!Fw+4^L] 7FP+JGѾAvl­{[:G� i2bcgTa+%?3#ikNk/:�ubVEc3lj=Gl!nA狫YuZf!I{{A`s~KGzUrE/MFf0Yl)xN*N)$cb!樊ʢ<trrZ66D 41Aa[`&Fh4 u87J  ټ7г6@ ,_筊f1:Wkfq6 EjfCa0`4hjE\A5. tkNT.LY* 2?S3\Vqf*۵cOn6v#*~㊄MAo7vݸ_Aڈ/G>\)BnBS5LK$*i;zZJMW5qn>BB<{ V-fC KS/v .DkDob!"o߾_~᠇�@y(*G vm3 ڍo)onw/qDqm3ZE�5r(Zz r'$3$j'ނ  ef¬!DXEϺkRj�@}de(8 |ÇR*dq/"_O�Ǐ?jJͦ)- N'-%DWΛO433~\.|8zz<sK<@eYwbbW6K js[pͩ?|��(CM[6!1"nԭhK" v;#Ŕ!Hrqq4I[2`%aa^PEdG ԍKDZK-^&+3 ` 9T42�P$mؚHkی"s !)M2*6~2r6F +ԻIw.8h}J�Pխ~::Sk»q1^ C1[\} Ar̓zn@PPS4)X h="ڑj%" ի?l2ڤHWtb~�Y 뾤m@9�`fkaM-n0UM7Z׫yr%W,Uw6)YGgVN"a0/\˲@5s&;2|YUZ"RJH �Z)fuO` %Dsþo+xFoS|AJ"DXAOe9p$ ʻk=B! ;ŏ^-߸NF?pdVq/ڿ#;VXVx 1𝶳4W%:-z_#KrMǯ0ϊيC;Iy3-֌b.L-K)GҌ*̵_>&H/b-D54�rH^O" ؒDc$RͮU-63o'ƍM[ƒb5Yo !F^ d>Rogn( "՟UmS""&h˪7/˒tI@jR<u~cs:[u 4߃D"2x�4~S0EÇOt Dɜ# !x 4[ v8/*v5<Ye0>-o9JD�==�MDґ76ǖqdڵ I2 zJЕTK(iy\pF@,4M�/R)%b܍04Ιg"i�_?}x|~zxxGY.Cr9i 503y>_i倫Ç?OO8{L',zΡjTncX8ɫ<)6Wcf g2:'JsEZQ@RTPT_; q@J;'F5#(4T%`%Rk^G� !RQr\~G-\/̔rN)10$Bd}.,zB�L-Ptl,C>"*6!@Sm@6(T YQS%Fj΋N/.bEӿȀܨs^Vg hDhw#%RD)3.ZcuWAgQ f2_R-6.~Bscb#@`OgIZ%+6 )*,/ThoC96�h$t*EM*A%ʶj0WtUGp4`R27QΉ`03W"0P"LI!AH;,2ͽ 4Eo#|>|*1x``LtE|1nR6Zڭ J̢ꈊA]ϺӞ(wkVzv@ Q/ImqѪo.*GFG/4ߠڤb:vXBS3 78`&\߇"Nmj@HdFv)6BwWYFQW j9 .DZ, @mX,� $Y[t[3_?yoP{- ><D|SJXa#n>ǁ9 WToN$:G 34!)Q Cr=@3ÃB qG GoW ;741t~j#4Mxwws92$0?wn0ܗ7Snbf$iapi}zyAQc��IDAT LGD\'zC)*V0DD%XlaF5}h)*Aˏ3%VC*9gdi!R ".R۟N?ܿr/rŠȵv;su`DRJ+f)HY¬t5e|~~�Ŵ?ϧpbK 3QZnESK8h (ywN&7�%="jy=f|nؘ %L[UgV0-%V<D 0%BqvLɧΑHX "0W>܂\eݏ5ml5 >X!CT󃃺g&sXF$��R&I @>5 4Nx$@$JUKlF|5? h+!qȩC&#$xk^K f$MD:f6@444g_E5qhˈESaW*l"-myF^wex<I]BnRp=okG5j d# JfM.RNjWDH0 Z˲�s1eG\µhZK-��Oe}+%J/XXmCJP!^s{ܔ֕jD4S{8}@{+\ͱL0Y[ܸ`9Wj#n4a|鄮nOqbyDT0*~Ҳث~^Bou򯪰*Eqg~gJTod=lUk E@ FB HU2v($ ~YmKp$(0P*3 &@Rl.�iJ �%j2k BbP_z u]o ~ϾVDTSS=^YAֆ(l6NxE 4:fFAsKB!y1/*U<DZZJn%m@_5x;`7x]h|!Щh\x`l<Ͽow.K\>=%@Vr!R5cIX GR[wώ]xlDBb6d珷ۚ`P(R+z6:RLf\ RQ%5E-iDRn? |:w)Q&i3/|>ː򧏿 N!2zq݉H)e uCZI�HTEd'& iQO`x~z>iw?Liy~~>zVTdAaalMS]v&{s;"2Ȁa p8xT|BrLՀSY,4:c#RZK)뱡bWt; RU_. e�X*"Zea ӌ0`z*XܣbqBE]j> aW@ku % +n[3@B:h5$�9Q;v-i˳A aXlJRگۜi+:,YS܎ʑ nifAc r- F pm+Ȭz0JYiDvўz-J/'+gvNl9Y_+]VB 38B o|KDr0آbWU@z1J L`s]Fll3 aBa̚i7*\+,K)Ka8àTRJLrcM7EَjJ_ɶSo]̰ms3&+={uxU޷6x04ܦz `tۇ\y٭{\oԢ R*~sKк v$yV71Q@u;lrVQW.3Jq\~��^]WoH����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0014457�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/.gitignore��������������������������������������������������������������������0000664�0000000�0000000�00000000050�14401326716�0016442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.pyc build dist *.egg-info __pycache__ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/LICENSE�����������������������������������������������������������������������0000664�0000000�0000000�00000002132�14401326716�0015462�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.5.8.1/ui/MANIFEST.in�������������������������������������������������������������������0000664�0000000�0000000�00000000132�14401326716�0016211�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������recursive-include opensnitch/res * recursive-include opensnitch/i18n *.qm include LICENSE ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/Makefile����������������������������������������������������������������������0000664�0000000�0000000�00000000551�14401326716�0016120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������all: opensnitch/resources_rc.py install: opensnitch/resources_rc.py: translations # deps @pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc sed -i 's/^import ui_pb2/from . import ui_pb2/' opensnitch/ui_pb2* translations: @$(MAKE) -C i18n deps: @pip3 install -r requirements.txt clean: @rm -rf *.pyc @rm -rf opensnitch/resources_rc.py �������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/bin/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015227�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/bin/opensnitch-ui�������������������������������������������������������������0000775�0000000�0000000�00000006750�14401326716�0017752�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 from PyQt5 import QtWidgets, QtGui, QtCore import sys import os import time import signal import argparse import logging from threading import Timer logging.getLogger().disabled = True 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 import opensnitch.version import opensnitch.ui_pb2 from opensnitch.ui_pb2_grpc import add_UIServicer_to_server def on_exit(): server.stop(0) app.quit() sys.exit(0) 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 def load_translations(): locale = QtCore.QLocale.system() i18n_path = os.path.dirname(os.path.realpath(opensnitch.__file__)) + "/i18n" print("Loading translations:", i18n_path, "locale:", locale.name()) translator = QtCore.QTranslator() translator.load(i18n_path + "/" + locale.name() + "/opensnitch-" + locale.name() + ".qm") return translator if __name__ == '__main__': parser = argparse.ArgumentParser(description='OpenSnitch UI service.') parser.add_argument("--socket", dest="socket", default="unix:///tmp/osui.sock", help="Path of the unix socket for the gRPC service (https://github.com/grpc/grpc/blob/master/doc/naming.md).", metavar="FILE") parser.add_argument("--max-clients", dest="serverWorkers", default=10, help="Max number of allowed clients (incoming connections).") args = parser.parse_args() os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" 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 translator = load_translations() app = QtWidgets.QApplication(sys.argv) app.installTranslator(translator) thm = Themes.instance() thm.load_theme(app) service = UIService(app, on_exit) # @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), )) add_UIServicer_to_server(service, server) if args.socket.startswith("unix://"): socket = args.socket[7:] socket = os.path.abspath(socket) server.add_insecure_port("unix:%s" % socket) else: server.add_insecure_port(args.socket) # https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt signal.signal(signal.SIGINT, signal.SIG_DFL) try: # print "OpenSnitch UI service running on %s ..." % socket server.start() app.exec_() except KeyboardInterrupt: on_exit() ������������������������opensnitch-1.5.8.1/ui/i18n/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015236�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/Makefile�����������������������������������������������������������������0000664�0000000�0000000�00000002633�14401326716�0016702�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.5.8.1/ui/i18n/README.md����������������������������������������������������������������0000664�0000000�0000000�00000001750�14401326716�0016520�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.5.8.1/ui/i18n/generate_i18n.sh���������������������������������������������������������0000775�0000000�0000000�00000000475�14401326716�0020234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh app_name="opensnitch" langs_dir="./locales" lrelease="lrelease" if ! command -v $lrelease; then # 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.5.8.1/ui/i18n/locales/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016660�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/de_DE/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017620�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/de_DE/opensnitch-de_DE.ts����������������������������������������0000664�0000000�0000000�00000334136�14401326716�0023312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.0" language="de_DE" sourcelanguage=""> <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="299"/> <source>User ID</source> <translation>User ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>Quell-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>Prozess ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <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="679"/> <source>from this executable</source> <translation>von dieser ausführbaren Datei</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>von dieser Kommandozeile</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>dieser Zielport</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>dieser Benutzer</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>diese Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>einmal</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <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="763"/> <source>forever</source> <translation>für immer</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> </context> <context> <name>New node connected</name> </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="472"/> <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="454"/> <source>Default timeout</source> <translation>Standardzeitlimit</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation>Popup-Standarddauer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="754"/> <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="314"/> <source>Default target</source> <translation>Standardfilterung</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>mittig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>oben rechts</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>unten rechts</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>oben links</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>nach ausführbarer Datei</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>nach Befehl</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>nach Zielport</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>nach Ziel-IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>nach UID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source>once</source> <translation>einmal</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <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="240"/> <source>forever</source> <translation>für immer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>deny</source> <translation>verweigern</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <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="711"/> <source>Nodes</source> <translation>Knoten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation>Prozessüberwachungsmethode</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="881"/> <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="884"/> <source>Address</source> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <source>Default log level</source> <translation>Standardprotokollstufe</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="734"/> <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="737"/> <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="809"/> <source>HostName</source> <translation>HostName</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>unix:///tmp/osui.sock</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="868"/> <source>until restart</source> <translation>bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation>immer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <source>Apply configuration to all nodes</source> <translation>Konfiguration auf alle Knoten anwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <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="1074"/> <source>In memory</source> <translation>Im Speicher</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <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="1345"/> <source>Close</source> <translation>Schließen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation>Anwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <source>Save</source> <translation>Speichern</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="347"/> <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="350"/> <source>Show advanced view by default</source> <translation>Standardmäßig erweiterte Ansicht anzeigen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="665"/> <source>Action</source> <translation>Aktion</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Dauer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="1093"/> <source>Database type</source> <translation>Datenbanktyp</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <source>Select</source> <translation>Auswählen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="150"/> <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="793"/> <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="894"/> <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="321"/> <source>Default position on screen</source> <translation>Standardposition auf dem Bildschirm</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation>jede temporäre Regel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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><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="495"/> <source>Don't save rules of duration</source> <translation>Speichern Sie keine Regeln der Dauer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="601"/> <source>Time</source> <translation>Zeit</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation>Ziel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation>Prozess</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <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="589"/> <source>Events tab columns</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation>Desktop-Benachrichtigungen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation>Systembenachrichtigungen verwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation>Qt-Benachrichtigungen verwenden</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="1178"/> <source>minutes</source> <translation>Minuten</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1227"/> <source>days</source> <translation>Tage</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</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="14"/> <source>Rule</source> <translation>Regel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation>Knoten</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <source>Apply rule to all nodes</source> <translation>Regel auf alle Knoten anwenden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>From this command line</source> <translation>Von dieser Kommandozeile</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation>Von dieser ausführbaren Datei</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/> <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="383"/> <source>To this IP / Network</source> <translation>Zu dieser IP / Netzwerk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/> <source>once</source> <translation>einmal</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation>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="827"/> <source>always</source> <translation>immer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation>Zu diesem Port</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <source>From this user ID</source> <translation>Von dieser Benutzer-ID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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="333"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation>Dauer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>To this host</source> <translation>Zu diesem Host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/> <source>Name</source> <translation>Name</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation>Aktivieren</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation>Lassen Sie das Feld leer, um den Namen automatisch zuzuweisen</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation>Prioritätsregel</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation>Groß- und Kleinschreibung beachten</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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><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="822"/> <source>until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <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="163"/> <source>Applications</source> <translation>Anwendungen</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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <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="246"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation>Netzwerk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/> <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="857"/> <source>Reject</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="290"/> <source>Save to CSV.</source> <translation>Als CSV exportieren.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation>Strg+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation>Erstellen Sie eine neue Regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1665"/> <source>-</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation>Abfangen starten oder stoppen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="748"/> <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="1569"/> <source>Rules</source> <translation>Regeln</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="857"/> <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="704"/> <source>Application rules</source> <translation>Anwendungsregeln</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="806"/> <source>Permanent</source> <translation>Dauerhaft</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="815"/> <source>Temporary</source> <translation>Temporär</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="933"/> <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="1020"/> <source>Applications</source> <translation>Anwendungen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1127"/> <source>Addresses</source> <translation>Adressen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1214"/> <source>Ports</source> <translation>Ports</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1298"/> <source>Users</source> <translation>Benutzer</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1404"/> <source>Connections</source> <translation>Verbindungen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1459"/> <source>Dropped</source> <translation>Abgelehnt</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1514"/> <source>Uptime</source> <translation>Betriebszeit</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1639"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation>Löschen Sie alle abgefangenen Ereignisse</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="864"/> <source>Edit rule</source> <translation>Regel bearbeiten</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="878"/> <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>Verbindungen löschen, die dieser Regel entsprechen</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="797"/> <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="177"/> <source>0</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Statistiken</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Hilfe</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Schließen</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Aktivieren</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Deaktivieren</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>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="91"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>für immer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation>Ausgehende Verbindung</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Prozess ausgeführt von:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>von dieser Kommandozeile</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <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="52"/> <source>until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <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="390"/> <source>to {0}</source> <translation>zu {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>UID {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>zu {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="105"/> <source>New outgoing connection</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="208"/> <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="448"/> <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="299"/> <source>Exception saving config: {0}</source> <translation>Fehler beim Speichern der Konfiguration: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/> <source>Applying configuration on {0} ...</source> <translation>Konfiguration in {0} anwenden ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/> <source>Error loading {0} configuration</source> <translation>Fehler beim Laden der Konfiguration {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <source>Error applying configuration: {0}</source> <translation>Fehler beim Anwenden der Konfiguration: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <source>Warning</source> <translation>Warnung</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation>DB-Typ geändert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <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="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>Wird geladen...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation>Es sind keine Knoten verbunden.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <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="527"/> <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="541"/> <source>Protocol regexp error</source> <translation>Protokoll-Regexp-Fehler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <source>process path can not be empty</source> <translation>Prozesspfad darf nicht leer sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/> <source>Process path regexp error</source> <translation>Prozesspfad-Regexp-Fehler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/> <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="577"/> <source>Command line regexp error</source> <translation>Befehlszeilen-Regexp-Fehler</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/> <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="595"/> <source>Dst port regexp error</source> <translation>Fehler im regulären Ausdruck des Zielports</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/> <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="613"/> <source>Dst host regexp error</source> <translation>Fehler beim regulären Ausdruck des Zielhosts</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/> <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="639"/> <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="651"/> <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="665"/> <source>User ID regexp error</source> <translation>Regexp-Fehler der Benutzer-ID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <source>Error applying rule: {0}</source> <translation>Fehler beim Anwenden der Regel: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/> <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="739"/> <source>Lists field cannot be empty</source> <translation>Listenfeld darf nicht leer sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/> <source>Lists field must be a directory</source> <translation>Listenfeld muss ein Verzeichnis sein</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/> <source><b>Rule not supported</b></source> <translation><b>Regel nicht unterstützt</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation>Gestoppt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>Deaktiviert</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <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="761"/> <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="1244"/> <source> Are you sure?</source> <translation> Bist du sicher?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="568"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch-Netzwerkstatistiken {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="570"/> <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="180"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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/dialogs/stats.py" line="253"/> <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="1837"/> <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="738"/> <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="921"/> <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="928"/> <source>Warning:</source> <translation>Warnung:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="717"/> <source>Allow</source> <translation>Erlauben</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="718"/> <source>Deny</source> <translation>Verweigern</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="722"/> <source>Always</source> <translation>Immer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Until reboot</source> <translation>Bis zum Neustart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="731"/> <source>Disable</source> <translation>Deaktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Enable</source> <translation>Aktivieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Duplicate</source> <translation>Duplizieren</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Edit</source> <translation>Bearbeiten</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="891"/> <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="1244"/> <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="392"/> <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="379"/> <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="380"/> <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="386"/> <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="383"/> <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="390"/> <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="395"/> <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="396"/> <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="391"/> <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="393"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="377"/> <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="301"/> <source>Args</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>DstIP</source> <comment>This is a word, without spaces and symbols.</comment> <translation>ZielIP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> <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="305"/> <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/dialogs/stats.py" line="252"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="709"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="719"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation type="unfinished"></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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="382"/> <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="384"/> <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="385"/> <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="404"/> <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="394"/> <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="644"/> <source>New node connected</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.5.8.1/ui/i18n/locales/es_ES/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017656�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/es_ES/opensnitch-es_ES.ts����������������������������������������0000664�0000000�0000000�00000342276�14401326716�0023412�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="299"/> <source>User ID</source> <translation>UID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>Etiqueta de texto</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>IP origen</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>PID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>IP destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation>Puerto destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation>de este ejecutable</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>de este comando</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>este puerto destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>este usuario</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>esta IP destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>una vez</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30 segundos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <source>1h</source> <translation>1 hora</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="763"/> <source>forever</source> <translation>para siempre</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation>a partir de este PID</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="472"/> <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="454"/> <source>Default timeout</source> <translation>Timeout por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <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="754"/> <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="314"/> <source>Default target</source> <translation>Filtrado por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>centro</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>Arriba a la derecha</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>Abajo a la derecha</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>Arriba a la izquierda</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>por ejecutable</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>por comando</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>por puerto destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>por IP destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>por UID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source>once</source> <translation>una vez</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30 segundos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30 minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <source>1h</source> <translation>1 hora</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="240"/> <source>forever</source> <translation>para siempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <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="711"/> <source>Nodes</source> <translation>Nodos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation>Método parar monitorizar procesos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="881"/> <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="884"/> <source>Address</source> <translation>Dirección</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <source>Default log level</source> <translation>Nivel de log por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <source>Version</source> <translation>Versión</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="734"/> <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="737"/> <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="809"/> <source>HostName</source> <translation>Nombre del host</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="868"/> <source>until restart</source> <translation>hasta reiniciar (el servicio)</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation>siempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <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="1039"/> <source>Database</source> <translation>Datos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1074"/> <source>In memory</source> <translation>En memoria</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <source>File</source> <translation>Fichero</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1345"/> <source>Close</source> <translation>Cerrar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation>Aplicar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <source>Save</source> <translation>Guardar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1093"/> <source>Database type</source> <translation>Tipo de base de datos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <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="350"/> <source>Show advanced view by default</source> <translation>Mostrar vista avanzada por defecto</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="665"/> <source>Action</source> <translation>Acción</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Duración</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="347"/> <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="150"/> <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="793"/> <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="894"/> <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="321"/> <source>Default position on screen</source> <translation>Posición por defecto en la pantalla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation>cualquier regla temporal</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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><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="495"/> <source>Don't save rules of duration</source> <translation>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="601"/> <source>Time</source> <translation>Hora</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation>Destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation>Aplicación</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation>Regla</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <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="589"/> <source>Events tab columns</source> <translation>Columnas de la pestaña Eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation>por PID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation>Deshabilitar ventanas emergentes, sólo mostrar notificaciones</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation>Notificaciones de escritorio</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation>Usar notificaciones del sistema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation>Usar notificaciones de Qt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation>Probar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="1178"/> <source>minutes</source> <translation>minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <source>Minutes between events purges</source> <translation>Minutos entre borrado de eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1227"/> <source>days</source> <translation>días</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</source> <translation>Máximo de días a guardar</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="14"/> <source>Rule</source> <translation>Regla</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation>Nodo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <source>Apply rule to all nodes</source> <translation>Aplicar regla a todos los nodos</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>From this command line</source> <translation>De este comando</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation>De este ejecutable</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/> <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="383"/> <source>To this IP / Network</source> <translation>A esta IP/Red</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/> <source>once</source> <translation>una vez</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation>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="827"/> <source>always</source> <translation>siempre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation>A este puerto</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <source>From this user ID</source> <translation>De este UID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.dominio.org, .*\.dominio.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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="333"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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="459"/> <source>LAN</source> <translation>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation>Duración</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>To this host</source> <translation>A este host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/> <source>Name</source> <translation>Nombre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation>dejar en blanco para autoasignar nombre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation>Prioritaria</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation>Distinguir mayúsculas/minúsculas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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>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="822"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <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="163"/> <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><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="229"/> <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="246"/> <source>From this PID</source> <translation>De este PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation>Red</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation>A esta lista de dominios/IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <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="549"/> <source>To this list of IPs</source> <translation>A esta lista de IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <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="677"/> <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="857"/> <source>Reject</source> <translation>Rechazar</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="290"/> <source>Save to CSV.</source> <translation>Exportar a CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation>Crear una nueva regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1665"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation>Parar o iniciar la interceptación</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="748"/> <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="1569"/> <source>Rules</source> <translation>Reglas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="857"/> <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="704"/> <source>Application rules</source> <translation>Reglas de aplicación</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="806"/> <source>Permanent</source> <translation>Permanentes</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="815"/> <source>Temporary</source> <translation>Temporales</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="933"/> <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="1020"/> <source>Applications</source> <translation>Aplicaciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1127"/> <source>Addresses</source> <translation>Direcciones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1214"/> <source>Ports</source> <translation>Puertos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1298"/> <source>Users</source> <translation>Usuarios</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1404"/> <source>Connections</source> <translation>Conexiones</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1459"/> <source>Dropped</source> <translation>Rechazadas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1514"/> <source>Uptime</source> <translation>Tiempo de ejecución</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1639"/> <source>Version</source> <translation>Versión</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation>Borrar todos los eventos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="864"/> <source>Edit rule</source> <translation>Editar regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="878"/> <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>Borrar conexiones que coinciden con esta regla</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="797"/> <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="177"/> <source>0</source> <translation>0</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Eventos</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Ayuda</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Cerrar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Deshabilitar</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>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="91"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>para siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation>Conexión saliente</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Proceso ejecutado desde:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>este comando</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <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="52"/> <source>until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <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="390"/> <source>to {0}</source> <translation>a {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>UID {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>a {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation>de este PID</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="105"/> <source>New outgoing connection</source> <translation>Nueva conexión saliente</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="208"/> <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="448"/> <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="299"/> <source>Exception saving config: {0}</source> <translation>Error al guardar la configuración: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/> <source>Applying configuration on {0} ...</source> <translation>Aplicando configuración en {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/> <source>Error loading {0} configuration</source> <translation>Error al cargar la configuración {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <source>Error applying configuration: {0}</source> <translation>Error al aplicar la configuración: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <source>Warning</source> <translation>Aviso</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation>El tipo de BBDD ha cambiado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <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="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>cargando...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation>No hay nodos conectados.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <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="527"/> <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="541"/> <source>Protocol regexp error</source> <translation>Error en la expresión regular del Protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <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="559"/> <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="563"/> <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="577"/> <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="581"/> <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="595"/> <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="599"/> <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="613"/> <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="617"/> <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="639"/> <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="651"/> <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="665"/> <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="205"/> <source>Error applying rule: {0}</source> <translation>Error al aplicar la regla: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/> <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="741"/> <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="777"/> <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="429"/> <source><b>Error loading rule</b></source> <translation><b>Error cargando la regla</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/> <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="669"/> <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="683"/> <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="764"/> <source>Select at least one field.</source> <translation>Selecciona al menos un campo.</translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation>Parado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>Deshabilitado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <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="761"/> <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="1244"/> <source> Are you sure?</source> <translation> ¿Estás seguro?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="568"/> <source>OpenSnitch Network Statistics {0}</source> <translation>Eventos de red OpenSnitch {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="570"/> <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="180"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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/dialogs/stats.py" line="253"/> <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="1837"/> <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="738"/> <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="731"/> <source>Disable</source> <translation>Deshabilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Duplicate</source> <translation>Duplicar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Edit</source> <translation>Editar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="891"/> <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="921"/> <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="928"/> <source>Warning:</source> <translation>Aviso:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="717"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="718"/> <source>Deny</source> <translation>Denegar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="722"/> <source>Always</source> <translation>Siempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Until reboot</source> <translation>Hasta reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <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="392"/> <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="379"/> <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="380"/> <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="386"/> <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="383"/> <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="390"/> <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="395"/> <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="396"/> <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="391"/> <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="393"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="377"/> <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>Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <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/dialogs/stats.py" line="252"/> <source>What</source> <translation>Qué</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="709"/> <source>Apply to</source> <translation>Aplicar a</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="719"/> <source>Reject</source> <translation>Rechazar</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <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>Dirección</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="382"/> <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="384"/> <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="385"/> <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="404"/> <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="394"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Prioritaria</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="644"/> <source>New node connected</source> <translation>Nuevo nodo conectado</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.5.8.1/ui/i18n/locales/eu_ES/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017660�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/eu_ES/opensnitch-eu_ES.ts����������������������������������������0000664�0000000�0000000�00000241252�14401326716�0023406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS><TS version="2.0"> <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="299"/> <source>User ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <source>1h</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="763"/> <source>forever</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> </context> <context> <name>New node connected</name> </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="472"/> <source>UI</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="454"/> <source>Default timeout</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="754"/> <source>Default duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="314"/> <source>Default target</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <source>bottom left</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="274"/> <source>by executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source>once</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <source>1h</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>forever</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <source>allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="711"/> <source>Nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="881"/> <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="884"/> <source>Address</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <source>Default log level</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <source>Version</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="734"/> <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="737"/> <source>Log file</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="809"/> <source>HostName</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>unix:///tmp/osui.sock</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="868"/> <source>until restart</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <source>Apply configuration to all nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Database</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1074"/> <source>In memory</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <source>File</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1345"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <source>Save</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="350"/> <source>Show advanced view by default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="665"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="1093"/> <source>Database type</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <source>Select</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="347"/> <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="150"/> <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="793"/> <source>Default action when the GUI is disconnected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="894"/> <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="321"/> <source>Default position on screen</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="495"/> <source>Don't save rules of duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="601"/> <source>Time</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Events tab columns</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="1178"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1227"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</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="14"/> <source>Rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <source>Apply rule to all nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>From this command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/> <source>Action</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/> <source>To this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/> <source>once</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/> <source>always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <source>From this user ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <source>www.domain.org, .*\.domain.org</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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="333"/> <source>TCP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>To this host</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/> <source>Name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <source>To this list of domains</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <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="246"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/> <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="857"/> <source>Reject</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="290"/> <source>Save to CSV.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1665"/> <source>-</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="748"/> <source>Nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1569"/> <source>Rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="857"/> <source>enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="704"/> <source>Application rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="806"/> <source>Permanent</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="815"/> <source>Temporary</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="933"/> <source>Hosts</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1020"/> <source>Applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1127"/> <source>Addresses</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1214"/> <source>Ports</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1298"/> <source>Users</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1404"/> <source>Connections</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1459"/> <source>Dropped</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1514"/> <source>Uptime</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1639"/> <source>Version</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="864"/> <source>Edit rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="878"/> <source>Delete rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="912"/> <source>Delete connections that matched this rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="797"/> <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="177"/> <source>0</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation type="unfinished"></translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="52"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <source>from this executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <source>to port {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/> <source>to {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <source>to *.{0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="105"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/> <source>Server address can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/> <source>Configuration applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/> <source>Exception saving config: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/> <source>Applying configuration on {0} ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/> <source>Error loading {0} configuration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <source>Error applying configuration: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <source>Restart the GUI in order effects to take effect</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <source><b>Error loading process information:</b> <br><br> </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/> <source>loading...</source> <translation type="unfinished"></translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <source>Rule applied.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/> <source>protocol can not be empty, or uncheck it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/> <source>Protocol regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <source>process path can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/> <source>Process path regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/> <source>command line can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/> <source>Command line regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/> <source>Dest port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/> <source>Dst port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/> <source>Dest host can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/> <source>Dst host regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/> <source>Dest IP/Network can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/> <source>Dst IP regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/> <source>User ID can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/> <source>User ID regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <source>Error applying rule: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/> <source><b>Error loading rule</b></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/> <source>Lists field cannot be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/> <source>Lists field must be a directory</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/> <source><b>Rule not supported</b></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <source>Running</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="761"/> <source> Your are about to delete this rule. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <source> Are you sure?</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="568"/> <source>OpenSnitch Network Statistics {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="570"/> <source>OpenSnitch Network Statistics for {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/> <source>Save as CSV</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="738"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="921"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="928"/> <source>Warning:</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="717"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="718"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="722"/> <source>Always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="731"/> <source>Disable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Duplicate</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Edit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="891"/> <source>Rule not found by that name and node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <source> You are about to delete this rule. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="392"/> <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="379"/> <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="380"/> <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="386"/> <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="383"/> <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="390"/> <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="395"/> <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="396"/> <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="391"/> <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="393"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="377"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="252"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="253"/> <source>Hits</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="709"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="719"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation type="unfinished"></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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="382"/> <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="384"/> <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="385"/> <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="404"/> <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="394"/> <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="644"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/fr_FR/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017656�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/fr_FR/opensnitch-fr_FR.ts����������������������������������������0000664�0000000�0000000�00000340373�14401326716�0023406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS><TS version="2.0" language="fr" sourcelanguage=""> <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="299"/> <source>User ID</source> <translation>ID utilisateur</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>IP source</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>ID processus</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>IP destination</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation>interface destination</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation>depuis cet exécutable</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>depuis cette ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>vers cette interface</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>cet utilisateur</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>vers cette IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>une seule fois</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5mn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15mn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30mn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <source>1h</source> <translation>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="763"/> <source>forever</source> <translation>définitivement</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Permettre</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> </context> <context> <name>New node connected</name> </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="472"/> <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="454"/> <source>Default timeout</source> <translation>attente par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation>attente dialogue par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="754"/> <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="314"/> <source>Default target</source> <translation>cible par défaut</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>centré</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>en haut à droite</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>en bas à gauche</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>en haut à gauche</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>par l'exécutable</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>par la ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>par l'interface de destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>par l'IP de destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>par cet utilisateur</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source>once</source> <translation>une seule fois</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <source>1h</source> <translation>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="240"/> <source>forever</source> <translation>définitivement</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <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="711"/> <source>Nodes</source> <translation>noeuds</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation>méthode de surveillance des processus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="881"/> <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="884"/> <source>Address</source> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <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="932"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="734"/> <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="737"/> <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="809"/> <source>HostName</source> <translation>nom d'hôte (host)</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="868"/> <source>until restart</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation>toujours</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <source>Apply configuration to all nodes</source> <translation>appliquer la configuration à tous les noeuds</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Database</source> <translation>Base de données</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1074"/> <source>In memory</source> <translation>En mémoire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <source>File</source> <translation>Fichier</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1345"/> <source>Close</source> <translation>Fermer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation>Appliquer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <source>Save</source> <translation>Enregistrer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1093"/> <source>Database type</source> <translation>type de base de données</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <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="350"/> <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="665"/> <source>Action</source> <translation>Action</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Durée</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="347"/> <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="150"/> <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="793"/> <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="894"/> <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="321"/> <source>Default position on screen</source> <translation>Position par défaut sur l'écran</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation>toute règle temporaire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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><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="495"/> <source>Don't save rules of duration</source> <translation>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="601"/> <source>Time</source> <translation>Temps</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation>Destination</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation>Protocole</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation>Processus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation>Règle</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <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="589"/> <source>Events tab columns</source> <translation>colonnes évènements</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="1178"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1227"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</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="14"/> <source>Rule</source> <translation>Règle</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation>Noeud</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <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="239"/> <source>From this command line</source> <translation>Depuis cette ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation>Depuis cet exécutable</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/> <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="383"/> <source>To this IP / Network</source> <translation>vers cette IP / ce réseau</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/> <source>once</source> <translation>une seule fois</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation>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="827"/> <source>always</source> <translation>toujours</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation>vers cette interface (port)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <source>From this user ID</source> <translation>depuis cet utilisateur (ID)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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="333"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation>Durée</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation>Protocole</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>To this host</source> <translation>Vers cet hôte</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/> <source>Name</source> <translation>Nom</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation>Activer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation>ne pas remplir va créer automatiquement</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation>Règle prioritaire</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation>sensible à la casse</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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><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="822"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <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="163"/> <source>Applications</source> <translation type="unfinished">Applications</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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <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="246"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/> <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="857"/> <source>Reject</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="290"/> <source>Save to CSV.</source> <translation>Enregistrer au format CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation>contrôle-S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation>Créer une nouvelle règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation>Etat</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1665"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation>Démarre ou stoppe l'interception</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="748"/> <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="1569"/> <source>Rules</source> <translation>Règles</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="857"/> <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="704"/> <source>Application rules</source> <translation>Règle d'applications</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="806"/> <source>Permanent</source> <translation>Permanent</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="815"/> <source>Temporary</source> <translation>Temporaire</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="933"/> <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="1020"/> <source>Applications</source> <translation>Applications</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1127"/> <source>Addresses</source> <translation>Adresses</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1214"/> <source>Ports</source> <translation>Ports (interfaces)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1298"/> <source>Users</source> <translation>Utilisateurs</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1404"/> <source>Connections</source> <translation>Connexions</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1459"/> <source>Dropped</source> <translation>Abandonnée</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1514"/> <source>Uptime</source> <translation>durée active (uptime)</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1639"/> <source>Version</source> <translation>Version</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation>Effacer tous les évènments interceptés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="864"/> <source>Edit rule</source> <translation>Editer règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="878"/> <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>Effacer les connexions qui déclenchaient cette règle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="797"/> <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="177"/> <source>0</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Statistiques</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Aide</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Fermer</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Activer</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Désactiver</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>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="91"/> <source>Allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>indéfiniment</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation>Connexion sortante</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Processus lancé par :</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>depuis cette ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <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="52"/> <source>until reboot</source> <translation>jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <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="390"/> <source>to {0}</source> <translation>vers {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>de l'utilisateur {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>vers {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="105"/> <source>New outgoing connection</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="208"/> <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="448"/> <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="299"/> <source>Exception saving config: {0}</source> <translation>Config enregistrant les exceptions : {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/> <source>Applying configuration on {0} ...</source> <translation>Applique la configuration à {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/> <source>Error loading {0} configuration</source> <translation>Erreur au chargement configuration {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <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="323"/> <source>Warning</source> <translation>Alerte</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation>type de base de données changé</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <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="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>chargement...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation>Aucun noeud n'est connecté.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <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="527"/> <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="541"/> <source>Protocol regexp error</source> <translation>Erreur à l'interprétation du RegExp protocole</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <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="559"/> <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="563"/> <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="577"/> <source>Command line regexp error</source> <translation>Erreur RegExp dans la ligne de commande</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/> <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="595"/> <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="599"/> <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="613"/> <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="617"/> <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="639"/> <source>Dst IP regexp error</source> <translation>erreur RegExp sur l'IP destination</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/> <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="665"/> <source>User ID regexp error</source> <translation>erreur RegExp sur l'ID utilisateur</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <source>Error applying rule: {0}</source> <translation>Erreur en appliquant la règle : {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/> <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="741"/> <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="777"/> <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="429"/> <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="179"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation>Inactif</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>désactivé</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <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="761"/> <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="1244"/> <source> Are you sure?</source> <translation> Êtes-vous sûr?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="568"/> <source>OpenSnitch Network Statistics {0}</source> <translation>Statistiques réseau OpenSnitch {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="570"/> <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="180"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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/dialogs/stats.py" line="253"/> <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="1837"/> <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="738"/> <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="731"/> <source>Disable</source> <translation>Désactiver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Enable</source> <translation>Activer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Duplicate</source> <translation>Dupliquer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Edit</source> <translation>Editer</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="891"/> <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="921"/> <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="928"/> <source>Warning:</source> <translation>Alerte :</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="717"/> <source>Allow</source> <translation>Autoriser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="718"/> <source>Deny</source> <translation>Refuser</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="722"/> <source>Always</source> <translation>Toujours</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Until reboot</source> <translation>Jusqu'au redémarrage</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <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="392"/> <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="379"/> <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="380"/> <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="386"/> <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="383"/> <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="390"/> <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="395"/> <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="396"/> <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="391"/> <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="393"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="377"/> <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>Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <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/dialogs/stats.py" line="252"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="709"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="719"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation type="unfinished"></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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="382"/> <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="384"/> <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="385"/> <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="404"/> <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="394"/> <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="644"/> <source>New node connected</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.5.8.1/ui/i18n/locales/hu_HU/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017670�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/hu_HU/opensnitch-hu_HU.ts����������������������������������������0000664�0000000�0000000�00000332244�14401326716�0023430�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.0" language="hu_HU" sourcelanguage=""> <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="679"/> <source>from this executable</source> <translation>ettől a futtatható fájltól</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>erről a parancssorról</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>ezt a célkikötőt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>ezt a felhasználót</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>ezt a cél IP címet</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>egyszer</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30 másodperc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5 perc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15 perc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation type="unfinished">30 perc</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <source>1h</source> <translation>1 óra</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="763"/> <source>forever</source> <translation>örökre</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="299"/> <source>User ID</source> <translation>Felhasználói azonosító</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>Szövegcímke</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>Forrás IP-cím</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>Folyamatazonosító</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>Cél IP-címe</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <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-kikötőn. És talán a www.goodsocket.net webhelyhez a 344-es TCP-kikötőn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation>ebből a folyamatazonosítóból</translation> </message> </context> <context> <name>New node connected</name> </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="472"/> <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="350"/> <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="863"/> <source>once</source> <translation>egyszer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30 másodperc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5 perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15 perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30 perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <source>1h</source> <translation>1 óra</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <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="665"/> <source>Action</source> <translation>Művelet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="314"/> <source>Default target</source> <translation>Alapértelmezett cél</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="905"/> <source>deny</source> <translation>megtagadás</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <source>allow</source> <translation>engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="274"/> <source>by executable</source> <translation>futtatható fájl szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>parancssor szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>rendeltetési kikötő szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>rendeltetési hely IP címe szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>felhasználói azonosító szerint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>középre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>jobb felső</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>jobb alsó</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>bal felső</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <source>bottom left</source> <translation>bal alsó</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation>Előugró ablak alapértelmezett időtartama</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="334"/> <source>Duration</source> <translation>Időtartam</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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 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="257"/> <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="451"/> <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="454"/> <source>Default timeout</source> <translation>Alapértelmezett időtúllépés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="711"/> <source>Nodes</source> <translation>Csomópontok</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation>Folyamatfigyelés módszer</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="734"/> <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="737"/> <source>Log file</source> <translation>Naplófájl</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="754"/> <source>Default duration</source> <translation>Alapértelmezett időtartam</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <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="790"/> <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="809"/> <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="868"/> <source>until restart</source> <translation>újraindításáig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation>mindig</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="881"/> <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="884"/> <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="932"/> <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="983"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <source>Default log level</source> <translation>Alapértelmezett naplószint</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Database</source> <translation>Adatbázis</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1093"/> <source>Database type</source> <translation>Adatbázistípus</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <source>Select</source> <translation>Kijelölés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1074"/> <source>In memory</source> <translation>Memóriabeli</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <source>File</source> <translation>Fájl</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1345"/> <source>Close</source> <translation>Bezárás</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation>Alkalmazás</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <source>Save</source> <translation>Mentés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="347"/> <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="150"/> <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="793"/> <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="894"/> <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="321"/> <source>Default position on screen</source> <translation>Alapértelmezett hely a képernyőn</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation>bármilyen ideiglenes szabályt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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><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="495"/> <source>Don't save rules of duration</source> <translation>Ne mentse az időtartam szabályait</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="601"/> <source>Time</source> <translation>Idő</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation>Cél</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation>Folyamat</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation>Szabály</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <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="589"/> <source>Events tab columns</source> <translation>Események lap oszlopai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation>folyamatazonosító által</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation>Előugró ablakok letiltása, csak értesítések megjelenítése</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation>Asztali értesítések</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation>Rendszerértesítések használata</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation>Qt-értesítések használata</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation>Tesztelés</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="1178"/> <source>minutes</source> <translation>perc</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <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="1227"/> <source>days</source> <translation>nap</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</source> <translation>Események megtartására nyitva álló napok száma</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 elindítsa vagy 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="14"/> <source>Rule</source> <translation>Szabály</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation>Csomópont</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <source>Apply rule to all nodes</source> <translation>Alkalmazzon szabályt minden csomópontra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/> <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="751"/> <source>Action</source> <translation>Művelet</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation>Erre a kikötőre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <source>To this list of domains</source> <translation>Ehhez a tartománylistához</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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>Helyi hálózat</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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><html><head/><body><p>Több kikötők megadhatók 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="792"/> <source>once</source> <translation>egyszer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation>30 másodperc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation>5 perc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation>15 perc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation>30 perc</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation>1 óra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/> <source>always</source> <translation>mindig</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <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="300"/> <source>To this host</source> <translation>Erre az állomásra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation>Időtartam</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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 reguláris kifejezést, azaz: ^(TCP|UDP)$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation>Protokoll</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation>Ebből a futtatható fájlból</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>From this command line</source> <translation>Ebből a parancssorból</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <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="899"/> <source>Name</source> <translation>Név</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation>hagyja üresen az önműködő létrehozáshoz</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation>Elsőbbségi szabály</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation>Kis- és nagybetűk felismerése</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/> <source>Applications</source> <translation>Alkalmazások</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><html><head/><body><p>Ez a mező csak a végrehajtható elérési úttal fog megegyezni. A felhasználó nem módosíthatja.<br/></p><p>Szabványos kifejezésekkel tilthatja meg a /tmp fájl végrehajtását, például:<br/></p><p>^/tmp/.*$</p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <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="246"/> <source>From this PID</source> <translation>Ebből a folyamatazonosítóból</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation>Hálózat</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation>Tartományok/IP-címek listája</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <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="549"/> <source>To this list of IPs</source> <translation>Ehhez az IP-címlistához</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <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="677"/> <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="857"/> <source>Reject</source> <translation>Elutasítás</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="290"/> <source>Save to CSV.</source> <translation>Mentés CSV-fájlként…</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation>Új szabály létrehozása</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation>Állapot</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1665"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation>Adatelérés indítása vagy leállítása</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <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="748"/> <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="1569"/> <source>Rules</source> <translation>Szabályok</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="857"/> <source>enable</source> <translation>engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="864"/> <source>Edit rule</source> <translation>Szabály szerkesztése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="878"/> <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="704"/> <source>Application rules</source> <translation>Alkalmazási szabályok</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="806"/> <source>Permanent</source> <translation>Állandó</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="815"/> <source>Temporary</source> <translation>Ideiglenes</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="933"/> <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="1020"/> <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="1127"/> <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="1214"/> <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 kikötő törlése</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1298"/> <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="1404"/> <source>Connections</source> <translation>Kapcsolatok</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1459"/> <source>Dropped</source> <translation>Elvetve</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1514"/> <source>Uptime</source> <translation>Hasznos üzemidő</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1639"/> <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>Kapcsolat törlése amelyek megfelelnek ennek a szabálynak</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="797"/> <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="177"/> <source>0</source> <translation>0</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Statisztika</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Letiltás</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Súgó</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Bezárás</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="52"/> <source>until reboot</source> <translation>újraindításig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>örökre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation>Kimenő kapcsolat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Folyamat innen indult:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <source>from this executable</source> <translation>ettől a végrehajtható fájlból</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>erről a parancssorról</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <source>to port {0}</source> <translation>kikötőig: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/> <source>to {0}</source> <translation>eddig: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>a(z) {0} felhasználótól</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>eddig: {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation>ebből a folyamatazonosítóból</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="105"/> <source>New outgoing connection</source> <translation>Új kimenő kapcsolat</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/> <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="323"/> <source>Warning</source> <translation>Figyelmeztetés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation>DB típusa megváltozott</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <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="389"/> <source>Applying configuration on {0} ...</source> <translation>Beállítások alkalmazása: {0}…</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/> <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="238"/> <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="448"/> <source>Configuration applied.</source> <translation>Beállítás alkalmazva.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <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="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>betöltés folyamatban…</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation>Nincsenek csomópontok csatlakoztatva.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <source>Rule applied.</source> <translation>A szabály alkalmazva.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <source>Error applying rule: {0}</source> <translation>Hiba a szabály alkalmazásakor: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/> <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="527"/> <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="541"/> <source>Protocol regexp error</source> <translation>Protokoll reguláris kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <source>process path can not be empty</source> <translation>folyamat útvonal nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/> <source>Process path regexp error</source> <translation>Folyamat útvonala reguláris kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/> <source>command line can not be empty</source> <translation>parancssor nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/> <source>Command line regexp error</source> <translation>Parancssor reguláris kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/> <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="595"/> <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="599"/> <source>Dest host can not be empty</source> <translation>Célkikötő nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/> <source>Dst host regexp error</source> <translation>Célkikötő reguláris kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/> <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="639"/> <source>Dst IP regexp error</source> <translation>Cél IP-cím reguláris kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/> <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="665"/> <source>User ID regexp error</source> <translation>Felhasználói azonosító reguláris kifejezéshibája</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/> <source>Lists field cannot be empty</source> <translation>Listamező nem lehet üres</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/> <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="777"/> <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="179"/> <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="669"/> <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="683"/> <source>PID field regexp error</source> <translation>Folyamatazonosító mező szabványos kifejezési hiba</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/> <source>Select at least one field.</source> <translation>Jelöljön ki legalább egy mezőt.</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="180"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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/dialogs/stats.py" line="253"/> <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="310"/> <source>Not running</source> <translation>Nem fut</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>Letiltva</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <source>Running</source> <translation>Futtatás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="568"/> <source>OpenSnitch Network Statistics {0}</source> <translation>{0} OpenSnitch hálózati statisztika</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="570"/> <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="921"/> <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="928"/> <source>Warning:</source> <translation>Figyelmeztetés:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="717"/> <source>Allow</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="718"/> <source>Deny</source> <translation>Megtagadás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="722"/> <source>Always</source> <translation>Mindig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Until reboot</source> <translation>Újraindításig</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="731"/> <source>Disable</source> <translation>Letiltás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Enable</source> <translation>Engedélyezés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Duplicate</source> <translation>Másolás</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Edit</source> <translation>Szerkesztés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="738"/> <source>Delete</source> <translation>Törlés</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="761"/> <source> Your are about to delete this rule. </source> <translation>Törölni készül ezt a szabályt.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <source> Are you sure?</source> <translation>Biztos benne?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="891"/> <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="1244"/> <source> You are about to delete this rule. </source> <translation>Törölni készül ezt a szabályt.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/> <source>Save as CSV</source> <translation>Mentés CSV formátumban…</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="392"/> <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="379"/> <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="380"/> <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="386"/> <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="383"/> <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="390"/> <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="395"/> <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="396"/> <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="391"/> <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="393"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="377"/> <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>Argumentumok</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <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/dialogs/stats.py" line="252"/> <source>What</source> <translation>Mi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="709"/> <source>Apply to</source> <translation>Alkalmazás a következőre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="719"/> <source>Reject</source> <translation>Elutasítás</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation>Hálózatnév</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>Cím</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="382"/> <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="384"/> <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="385"/> <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="404"/> <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="394"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Sorrend</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="644"/> <source>New node connected</source> <translation>Új csomópont csatlakoztatva</translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/ja_JP/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017643�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/ja_JP/opensnitch-ja_JP.ts����������������������������������������0000664�0000000�0000000�00000316140�14401326716�0023353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS><TS version="2.0" language="ja_JP" sourcelanguage=""> <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="299"/> <source>User ID</source> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation type="unfinished">送信元のIP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation type="unfinished">プロセスID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation type="unfinished">宛先IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation type="unfinished">宛先ポート</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation type="unfinished">この実行ファイルを</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation type="unfinished">このコマンドラインを</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation type="unfinished">この宛先ポートに対して</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation type="unfinished">このユーザーを</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation type="unfinished">この宛先IPに対して</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation type="unfinished">一度のみ</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation type="unfinished">30秒間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation type="unfinished">5分間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation type="unfinished">15分間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation type="unfinished">30分間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <source>1h</source> <translation type="unfinished">1時間</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation type="unfinished">再起動するまで</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="763"/> <source>forever</source> <translation type="unfinished">永久に</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation type="unfinished">拒否する</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation type="unfinished">許可する</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> </context> <context> <name>New node connected</name> </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="472"/> <source>UI</source> <translation type="unfinished">UI</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="454"/> <source>Default timeout</source> <translation type="unfinished">ダイアログの表示時間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation type="unfinished">ポップアップ時に選択される規定のルールの有効期間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="754"/> <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="314"/> <source>Default target</source> <translation type="unfinished">既定のターゲット</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation type="unfinished">中央</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation type="unfinished">右上部</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation type="unfinished">右下部</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation type="unfinished">左上部</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation type="unfinished">実行ファイル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation type="unfinished">コマンドライン</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation type="unfinished">宛先ポート</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation type="unfinished">宛先IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source>once</source> <translation type="unfinished">一度のみ</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation type="unfinished">30秒間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation type="unfinished">5分間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation type="unfinished">15分間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation type="unfinished">30分間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <source>1h</source> <translation type="unfinished">1時間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation type="unfinished">再起動するまで</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="240"/> <source>forever</source> <translation type="unfinished">永久に</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>deny</source> <translation type="unfinished">拒否</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <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="711"/> <source>Nodes</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation type="unfinished">プロセス監視方式</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="881"/> <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="884"/> <source>Address</source> <translation type="unfinished">アドレス</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <source>Default log level</source> <translation type="unfinished">既定のログレベル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <source>Version</source> <translation type="unfinished">バージョン</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="734"/> <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="737"/> <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="809"/> <source>HostName</source> <translation type="unfinished">ホスト名</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>unix:///tmp/osui.sock</source> <translation type="unfinished">unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="868"/> <source>until restart</source> <translation type="unfinished">再起動するまで</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation type="unfinished">常に</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation type="unfinished">/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation type="unfinished">/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <source>Apply configuration to all nodes</source> <translation type="unfinished">全てのノードに設定を反映</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Database</source> <translation type="unfinished">データベース</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1093"/> <source>Database type</source> <translation type="unfinished">データベース方式</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <source>Select</source> <translation type="unfinished">参照</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1074"/> <source>In memory</source> <translation type="unfinished">メモリ内</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <source>File</source> <translation type="unfinished">ファイル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1345"/> <source>Close</source> <translation type="unfinished">閉じる</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation type="unfinished">適用</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <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="347"/> <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="350"/> <source>Show advanced view by default</source> <translation type="unfinished">標準で詳細表示を有効にする</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="665"/> <source>Action</source> <translation type="unfinished">アクション</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation type="unfinished">ルールの有効期間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="150"/> <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="793"/> <source>Default action when the GUI is disconnected</source> <translation type="unfinished">GUI未接続時の規定のアクション</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="894"/> <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="321"/> <source>Default position on screen</source> <translation type="unfinished">規定の表示位置</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation type="unfinished">全ての一時ルール</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="495"/> <source>Don't save rules of duration</source> <translation type="unfinished">ルールの有効期間を保持しない</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="601"/> <source>Time</source> <translation type="unfinished">時間</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation type="unfinished">宛先</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation type="unfinished">プロトコル</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation type="unfinished">プロセス</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <source>Node</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="589"/> <source>Events tab columns</source> <translation type="unfinished">イベントタブの項目</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="299"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1178"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1227"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</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="14"/> <source>Rule</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation type="unfinished">ノード</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <source>Apply rule to all nodes</source> <translation type="unfinished">全てのノードにルールを反映</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/> <source>To this IP / Network</source> <translation type="unfinished">IP/ネットワーク</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/> <source>Action</source> <translation type="unfinished">アクション</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation type="unfinished">ポート</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <source>To this list of domains</source> <translation type="unfinished">ドメインリスト</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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="unfinished">LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/> <source>once</source> <translation type="unfinished">一度のみ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation type="unfinished">30秒間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation type="unfinished">5分間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation type="unfinished">15分間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation type="unfinished">30分間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation type="unfinished">1時間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/> <source>until reboot</source> <translation type="unfinished">再起動するまで</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/> <source>always</source> <translation type="unfinished">常に</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <source>www.domain.org, .*\.domain.org</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>To this host</source> <translation type="unfinished">ホスト</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation type="unfinished">有効期間</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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="333"/> <source>TCP</source> <translation type="unfinished">TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation type="unfinished">UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation type="unfinished">UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation type="unfinished">TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation type="unfinished">UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation type="unfinished">UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation type="unfinished">プロトコル</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation type="unfinished">実行ファイル</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation type="unfinished">拒否</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation type="unfinished">許可</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>From this command line</source> <translation type="unfinished">コマンドライン</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <source>From this user ID</source> <translation type="unfinished">ユーザーID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/> <source>Name</source> <translation type="unfinished">名前</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation type="unfinished">有効</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation type="unfinished">空にすると自動生成されます</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation type="unfinished">優先ルール</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation type="unfinished">大文字/小文字を区別</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <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="246"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/> <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="857"/> <source>Reject</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="290"/> <source>Save to CSV.</source> <translation>CSVファイルに保存</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation type="unfinished">Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation type="unfinished">ルールを新規作成</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation type="unfinished">状態</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1665"/> <source>-</source> <translation type="unfinished">-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation type="unfinished">サービスを開始/停止</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation type="unfinished">50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation type="unfinished">100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation type="unfinished">200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation type="unfinished">300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation type="unfinished">記録した全てのイベント履歴を消去</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="748"/> <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="1569"/> <source>Rules</source> <translation type="unfinished">ルール</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="857"/> <source>enable</source> <translation type="unfinished">有効</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="864"/> <source>Edit rule</source> <translation type="unfinished">ルールを編集</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="878"/> <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="704"/> <source>Application rules</source> <translation type="unfinished">アプリケーションのルール</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="806"/> <source>Permanent</source> <translation type="unfinished">永久ルール</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="815"/> <source>Temporary</source> <translation type="unfinished">一時ルール</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="933"/> <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="1020"/> <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="1127"/> <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="1214"/> <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="1298"/> <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="1404"/> <source>Connections</source> <translation type="unfinished">通過</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1459"/> <source>Dropped</source> <translation type="unfinished">ブロック</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1514"/> <source>Uptime</source> <translation type="unfinished">実行時間</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1639"/> <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="unfinished">このルールにマッチした接続を削除します</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="797"/> <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="177"/> <source>0</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation type="unfinished">ダッシュボードを開く</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation type="unfinished">ヘルプ</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation type="unfinished">終了</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation type="unfinished">有効化</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation type="unfinished">無効化</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="52"/> <source>until reboot</source> <translation type="unfinished">再起動するまで</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation type="unfinished">永久に</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/> <source>Allow</source> <translation type="unfinished">許可</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation type="unfinished">拒否</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation type="unfinished">外部への接続</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation type="unfinished">プロセスの実行元:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <source>from this executable</source> <translation type="unfinished">次の実行ファイルを</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation type="unfinished">次のコマンドラインを</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <source>to port {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/> <source>to {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <source>to *.{0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="105"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/> <source>Exception saving config: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <source>Warning</source> <translation type="unfinished">警告</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation type="unfinished">データベース方式が変更されました</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <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="389"/> <source>Applying configuration on {0} ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/> <source>Server address can not be empty</source> <translation type="unfinished">サーバーアドレスは空白にすることはできません</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/> <source>Error loading {0} configuration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/> <source>Configuration applied.</source> <translation type="unfinished">構成は反映されました。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <source>Error applying configuration: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation type="unfinished">読み込み中...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation type="unfinished">接続しているノードがありません。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <source>Rule applied.</source> <translation type="unfinished">ルールが反映されました。</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <source>Error applying rule: {0}</source> <translation type="unfinished">ルールの反映に失敗しました: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/> <source>protocol can not be empty, or uncheck it</source> <translation type="unfinished">プロトコルを指定するかチェックを外してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/> <source>Protocol regexp error</source> <translation type="unfinished">プロトコルの正規表現記法が誤っています</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <source>process path can not be empty</source> <translation type="unfinished">プロセスのパスを指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/> <source>Process path regexp error</source> <translation type="unfinished">プロセスパスの正規表現記法が誤っています</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/> <source>command line can not be empty</source> <translation type="unfinished">コマンドラインを指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/> <source>Command line regexp error</source> <translation type="unfinished">コマンドラインの正規表現記法が誤っています</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/> <source>Dest port can not be empty</source> <translation type="unfinished">宛先ポートを指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/> <source>Dst port regexp error</source> <translation type="unfinished">宛先ポートの正規表現記法が誤っています</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/> <source>Dest host can not be empty</source> <translation type="unfinished">宛先ホストを指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/> <source>Dst host regexp error</source> <translation type="unfinished">宛先ホストの正規表現記法が誤っています</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/> <source>Dest IP/Network can not be empty</source> <translation type="unfinished">宛先IP/ネットワークを指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/> <source>Dst IP regexp error</source> <translation type="unfinished">宛先IPの正規表現記法が誤っています</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/> <source>User ID can not be empty</source> <translation type="unfinished">ユーザーIDを指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/> <source>User ID regexp error</source> <translation type="unfinished">ユーザーIDの正規表現記法が誤っています</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/> <source>Lists field cannot be empty</source> <translation type="unfinished">リスト項目を指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/> <source>Lists field must be a directory</source> <translation type="unfinished">リスト項目には必ずディレクトリを指定してください</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/> <source><b>Rule not supported</b></source> <translation type="unfinished"><b>ルールをサポートしていません</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/> <source><b>Error loading rule</b></source> <translation type="unfinished"><b>ルールの読み込みに失敗しました</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/> <source>Select at least one field.</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="180"/> <source>Rules</source> <translation type="obsolete">ルール</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="300"/> <source>Action</source> <translation type="obsolete">アクション</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/dialogs/stats.py" line="253"/> <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="310"/> <source>Not running</source> <translation type="unfinished">停止中</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation type="unfinished">無効</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <source>Running</source> <translation type="unfinished">実行中</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="568"/> <source>OpenSnitch Network Statistics {0}</source> <translation type="unfinished">OpenSnitch ネットワークモニター {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="570"/> <source>OpenSnitch Network Statistics for {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="731"/> <source>Disable</source> <translation type="unfinished">無効化</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Enable</source> <translation type="unfinished">有効化</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Duplicate</source> <translation type="unfinished">複製</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Edit</source> <translation type="unfinished">編集</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="738"/> <source>Delete</source> <translation type="unfinished">削除</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="761"/> <source> Your are about to delete this rule. </source> <translation type="unfinished"> このルールを消去しようとしています。 </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <source> Are you sure?</source> <translation type="unfinished"> 宜しいですか?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="891"/> <source>Rule not found by that name and node</source> <translation type="unfinished">該当するルールが見つかりませんでした</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/> <source>Save as CSV</source> <translation type="unfinished">CSVファイルに保存</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="921"/> <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="928"/> <source>Warning:</source> <translation type="unfinished">警告:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="717"/> <source>Allow</source> <translation type="unfinished">許可</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="718"/> <source>Deny</source> <translation type="unfinished">拒否</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="722"/> <source>Always</source> <translation type="unfinished">常に</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Until reboot</source> <translation type="unfinished">再起動するまで</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <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="392"/> <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="379"/> <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="380"/> <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="386"/> <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="383"/> <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="390"/> <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="395"/> <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="396"/> <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="391"/> <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="393"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="377"/> <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="unfinished">引数</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished">宛先ポート</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="252"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="709"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="719"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation type="unfinished"></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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="382"/> <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="384"/> <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="385"/> <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="404"/> <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="394"/> <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="644"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> </context> </TS> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/lt_LT/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017676�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/lt_LT/opensnitch-lt_LT.ts����������������������������������������0000664�0000000�0000000�00000333647�14401326716�0023454�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="299"/> <source>User ID</source> <translation>Vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>TekstoEtiketė</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>Šaltinio IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>Proceso ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>Paskirties IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation>Paskirties prievadas</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation>iš šio vykdomojo failo</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>iš šios komandinės eilutės</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>šis paskirties prievadas</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>šis vartotojas</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>šis paskirties ip</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>kartą</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30 sek.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <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="763"/> <source>forever</source> <translation>amžinai</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Drausti</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</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="472"/> <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="454"/> <source>Default timeout</source> <translation>Numatytasis laiko limitas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation>Iššokančiojo lango numatytoji trukmė</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="777"/> <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="314"/> <source>Default target</source> <translation>Numatytasis tikslas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>centre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>viršuje dešinėje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>apačioje dešinėje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>viršuje kairėje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>pagal vykdomąjį failą</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>pagal komandinę eilutę</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>pagal paskirties prievadą</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>pagal paskirties ip</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>pagal vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="881"/> <source>once</source> <translation>kartą</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30 sek.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <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="240"/> <source>forever</source> <translation>amžinai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="923"/> <source>deny</source> <translation>drausti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <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="734"/> <source>Nodes</source> <translation>Mazgai</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="740"/> <source>Process monitor method</source> <translation>Proceso stebėjimo metodas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="774"/> <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="899"/> <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="902"/> <source>Address</source> <translation>Adresas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1042"/> <source>Default log level</source> <translation>Numatytasis registravimo žurnale lygis</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="950"/> <source>Version</source> <translation>Versija</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="813"/> <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="757"/> <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="760"/> <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="832"/> <source>HostName</source> <translation>Kompiuterio vardas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="886"/> <source>until restart</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <source>always</source> <translation>visada</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1013"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1018"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <source>Apply configuration to all nodes</source> <translation>Taikyti konfigūraciją visiems mazgams</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1057"/> <source>Database</source> <translation>Duomenų bazė</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1092"/> <source>In memory</source> <translation>Atmintyje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1097"/> <source>File</source> <translation>Failas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1363"/> <source>Close</source> <translation>Uždaryti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1374"/> <source>Apply</source> <translation>Taikyti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1385"/> <source>Save</source> <translation>Išsaugoti</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1111"/> <source>Database type</source> <translation>Duomenų bazės tipas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1118"/> <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="350"/> <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="660"/> <source>Action</source> <translation>Veiksmas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Trukmė</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="347"/> <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="150"/> <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="816"/> <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="912"/> <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="321"/> <source>Default position on screen</source> <translation>Numatytoji padėtis ekrane</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="713"/> <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><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>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="596"/> <source>Time</source> <translation>Laikas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="676"/> <source>Destination</source> <translation>Paskirties vieta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="644"/> <source>Protocol</source> <translation>Protokolas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="692"/> <source>Process</source> <translation>Procesas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="612"/> <source>Rule</source> <translation>Taisyklė</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="628"/> <source>Node</source> <translation>Mazgas</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="584"/> <source>Events tab columns</source> <translation>Events tab columns</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="494"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="512"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="528"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="557"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="570"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="705"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="909"/> <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="1196"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1222"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1245"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1255"/> <source>Maximum days of events to keep</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="852"/> <source>Node</source> <translation>Mazgas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="875"/> <source>Apply rule to all nodes</source> <translation>Taikyti taisyklę visiems mazgams</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/> <source>From this command line</source> <translation>Iš šios komandinės eilutės</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="341"/> <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="456"/> <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>30 sek.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> <source>5m</source> <translation>5 min.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> <source>15m</source> <translation>15 min.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> <source>30m</source> <translation>30 min.</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> <source>1h</source> <translation>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="366"/> <source>To this port</source> <translation>Į šį prievadą</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="247"/> <source>From this user ID</source> <translation>Iš šio vartotojo ID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="492"/> <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="503"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domenas.org, .*\.domenas.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/> <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="406"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="513"/> <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>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>fd00::/8</source> <translation>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="449"/> <source>Protocol</source> <translation>Protokolas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="373"/> <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="912"/> <source>Name</source> <translation>Pavadinimas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/> <source>Enable</source> <translation>Įjungti</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="928"/> <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>palikite tuščią, kad sukurtumėte automatiškai</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="891"/> <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="899"/> <source>Priority rule</source> <translation>Prioritetinė taisyklė</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="770"/> <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="773"/> <source>Case-sensitive</source> <translation>Case-sensitive</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="442"/> <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><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="658"/> <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="221"/> <source>Applications</source> <translation type="unfinished">Programos</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <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="240"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/> <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="274"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="360"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="616"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="623"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="649"/> <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="684"/> <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="712"/> <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="727"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> <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="764"/> <source>More</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>Įrašyti į CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="294"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="330"/> <source>Create a new rule</source> <translation>Sukurti naują taisyklę</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="360"/> <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="399"/> <source>Status</source> <translation>Statusas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1627"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="437"/> <source>Start or Stop interception</source> <translation>Pradėti arba sustabdyti perėmimą</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="482"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="724"/> <source>Nodes</source> <translation>Mazgai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1531"/> <source>Rules</source> <translation>Taisyklės</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="833"/> <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="680"/> <source>Application rules</source> <translation>Application rules</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Permanent</source> <translation>Nuolatinė</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="791"/> <source>Temporary</source> <translation>Laikina</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="895"/> <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="982"/> <source>Applications</source> <translation>Programos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1089"/> <source>Addresses</source> <translation>Adresai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1176"/> <source>Ports</source> <translation>Prievadai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1260"/> <source>Users</source> <translation>Vartotojai</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1366"/> <source>Connections</source> <translation>Connections</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1421"/> <source>Dropped</source> <translation>Dropped</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1476"/> <source>Uptime</source> <translation>Veikimo laikas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1601"/> <source>Version</source> <translation>Versija</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation type="unfinished">Ištrinti visus perimtus įvykius</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="840"/> <source>Edit rule</source> <translation>Redaguoti taisyklę</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="854"/> <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="773"/> <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="177"/> <source>0</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Statistika</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Pagalba</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Uždaryti</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Įjungti</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Išjungti</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>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="547"/> <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="91"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation>Drausti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>amžinai</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation>Outgoing connection</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Procesas paleistas iš:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>iš šios komandinės eilutės</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <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="52"/> <source>until reboot</source> <translation>iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <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="390"/> <source>to {0}</source> <translation>į {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>iš vartotojo {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>į {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="108"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/> <source>from this PID</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="230"/> <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="477"/> <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="321"/> <source>Exception saving config: {0}</source> <translation>Išimtis išsaugant konfigūraciją: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="418"/> <source>Applying configuration on {0} ...</source> <translation>Taikoma konfigūracija {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/> <source>Error loading {0} configuration</source> <translation>Įkeliant {0} konfigūraciją įvyko klaida</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="479"/> <source>Error applying configuration: {0}</source> <translation>Taikant konfigūraciją įvyko klaida: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/> <source>Warning</source> <translation>Įspėjimas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/> <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="351"/> <source>DB type changed</source> <translation>DB tipas pakeistas</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/> <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="509"/> <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="127"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="135"/> <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="387"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>įkeliama…</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="164"/> <source>There're no nodes connected.</source> <translation>Nėra prijungtų mazgų.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <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="537"/> <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="551"/> <source>Protocol regexp error</source> <translation type="unfinished">Protokolo regexp klaida</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="555"/> <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="569"/> <source>Process path regexp error</source> <translation>Process path regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="573"/> <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="587"/> <source>Command line regexp error</source> <translation>Command line regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="591"/> <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="605"/> <source>Dst port regexp error</source> <translation>Dst port regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="609"/> <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="623"/> <source>Dst host regexp error</source> <translation>Dst host regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="627"/> <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="649"/> <source>Dst IP regexp error</source> <translation>Dst IP regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="661"/> <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="675"/> <source>User ID regexp error</source> <translation>User ID regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="207"/> <source>Error applying rule: {0}</source> <translation>Klaida taikant taisyklę: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <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="751"/> <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="794"/> <source><b>Rule not supported</b></source> <translation><b>Taisyklė nepalaikoma</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="439"/> <source><b>Error loading rule</b></source> <translation><b>Klaida įkeliant taisyklę</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="181"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="679"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="693"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="781"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation>Neveikia</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>Išjungta</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <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="899"/> <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="1286"/> <source> Are you sure?</source> <translation> Ar esate tuo tikras?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="583"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch tinklo statistika {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="585"/> <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="298"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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="24"/> <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="1901"/> <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="765"/> <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="758"/> <source>Disable</source> <translation>Išjungti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="760"/> <source>Enable</source> <translation>Įjungti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="763"/> <source>Duplicate</source> <translation>Duplicate</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="764"/> <source>Edit</source> <translation>Redaguoti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="918"/> <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="948"/> <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="955"/> <source>Warning:</source> <translation>Įspėjimas:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="744"/> <source>Allow</source> <translation>Leisti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="745"/> <source>Deny</source> <translation>Drausti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="749"/> <source>Always</source> <translation>Visada</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="750"/> <source>Until reboot</source> <translation>Iki perkrovimo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/> <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="285"/> <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="286"/> <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="287"/> <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="288"/> <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="386"/> <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="383"/> <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="292"/> <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="293"/> <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="294"/> <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="295"/> <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="296"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="308"/> <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>Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>PaskPrievadas</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="652"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="23"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <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="297"/> <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="384"/> <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="385"/> <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="404"/> <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="736"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="746"/> <source>Reject</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.5.8.1/ui/i18n/locales/nb_NO/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017653�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/nb_NO/opensnitch-nb_NO.ts����������������������������������������0000664�0000000�0000000�00000324520�14401326716�0023374�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="299"/> <source>User ID</source> <translation>Brukerid</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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;">Utført fra</span></p></body></html></translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="630"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>Kilde-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>Prosess-ID</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>Mål-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation>Målport</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation>fra denne kjørbare fil</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>fra denne kommandolinje</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>denne målport</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>denne bruker</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>denne mål-IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>en gang</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <source>1h</source> <translation>1t</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="763"/> <source>forever</source> <translation>til evig tid</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Nekt</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Tillat</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>til omstart</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation>fra denne PID</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="472"/> <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="454"/> <source>Default timeout</source> <translation>Forvalgt utløpstid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation>Forvalgt varighet for sprettoppvindu</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="754"/> <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="314"/> <source>Default target</source> <translation>Forvalgt mål</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>midtstilt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>øvre høyre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>nedre høyre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>øvre venstre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>av kjørbar fil</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>av kommandolinje</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>av målport</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>av mål-IP</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>av brukerid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source>once</source> <translation>en gang</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <source>1h</source> <translation>1t</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="240"/> <source>forever</source> <translation>til evig tid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>deny</source> <translation>nekt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <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="711"/> <source>Nodes</source> <translation>Noder</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation>Prosessmonitoreringsmetode</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="881"/> <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="884"/> <source>Address</source> <translation>Adresse</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <source>Default log level</source> <translation>Forvalgt loggnivå</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <source>Version</source> <translation>Versjon</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="734"/> <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="737"/> <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="809"/> <source>HostName</source> <translation>HostName</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="868"/> <source>until restart</source> <translation>frem til omstart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation>alltid</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <source>Apply configuration to all nodes</source> <translation>Anvend oppsett for alle noder</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> <source>Database</source> <translation>Database</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1074"/> <source>In memory</source> <translation>I minnet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <source>File</source> <translation>Fil</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1345"/> <source>Close</source> <translation>Lukk</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation>Anvend</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <source>Save</source> <translation>Lagre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>frem til omstart</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1093"/> <source>Database type</source> <translation>Databasetype</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <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="350"/> <source>Show advanced view by default</source> <translation>Vis avansert fremvisning som forvalg</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="665"/> <source>Action</source> <translation>Handling</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Varighet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <source>Filter connections also by:</source> <translation type="unfinished"></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="451"/> <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="347"/> <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="150"/> <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="793"/> <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="894"/> <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="321"/> <source>Default position on screen</source> <translation>Forvalgt posisjon på skjermen</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation>alle midertidige regler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="495"/> <source>Don't save rules of duration</source> <translation type="unfinished"></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="601"/> <source>Time</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <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="589"/> <source>Events tab columns</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="1178"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1227"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</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="14"/> <source>Rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <source>Apply rule to all nodes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>From this command line</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/> <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="383"/> <source>To this IP / Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/> <source>once</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation type="unfinished"></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="827"/> <source>always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <source>From this user ID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <source>www.domain.org, .*\.domain.org</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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="333"/> <source>TCP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>To this host</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/> <source>Name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/> <source>until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <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="163"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <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="246"/> <source>From this PID</source> <translation>Fra denne PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation>Nettverk</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation>Liste med domener/IP-nummer</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <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="549"/> <source>To this list of IPs</source> <translation>Til denne listen med IP-adresser</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <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="677"/> <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="857"/> <source>Reject</source> <translation>Avvis</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="290"/> <source>Save to CSV.</source> <translation>Lagre til CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation>Lag ny regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation>Status</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1665"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="748"/> <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="1569"/> <source>Rules</source> <translation>Regler</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="857"/> <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="704"/> <source>Application rules</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="806"/> <source>Permanent</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="815"/> <source>Temporary</source> <translation>Midlertidig</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="933"/> <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="1020"/> <source>Applications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1127"/> <source>Addresses</source> <translation>Adresser</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1214"/> <source>Ports</source> <translation>Porter</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1298"/> <source>Users</source> <translation>Brukere</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1404"/> <source>Connections</source> <translation>Forbindelser</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1459"/> <source>Dropped</source> <translation>Droppet</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1514"/> <source>Uptime</source> <translation>Oppetid</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1639"/> <source>Version</source> <translation>Versjon</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="864"/> <source>Edit rule</source> <translation>Endre regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="878"/> <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>Slett forbindelser som passer til denne regel</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="797"/> <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="177"/> <source>0</source> <translation>0</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Statistikk</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Hjelp</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Lukk</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Aktiver</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Deaktiver</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>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="91"/> <source>Allow</source> <translation>Tillat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation>Nekt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>for alltid</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation>Utgående forbindelse</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Prosess startet fra:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>fra denne kommandolinjen</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <source>from this executable</source> <translation type="unfinished"></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="52"/> <source>until reboot</source> <translation>frem til omstart</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <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="390"/> <source>to {0}</source> <translation>til {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>fra bruker {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>til {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="105"/> <source>New outgoing connection</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="208"/> <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="448"/> <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="299"/> <source>Exception saving config: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/> <source>Applying configuration on {0} ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/> <source>Error loading {0} configuration</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <source>Error applying configuration: {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <source>Restart the GUI in order effects to take effect</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <source><b>Error loading process information:</b> <br><br> </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/> <source><b>Error stopping monitoring process:</b><br><br></source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/> <source>loading...</source> <translation type="unfinished"></translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <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="527"/> <source>protocol can not be empty, or uncheck it</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/> <source>Protocol regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <source>process path can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/> <source>Process path regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/> <source>command line can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/> <source>Command line regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/> <source>Dest port can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/> <source>Dst port regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/> <source>Dest host can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/> <source>Dst host regexp error</source> <translation>Feil med målvert-regulæruttrykk</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/> <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="639"/> <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="651"/> <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="665"/> <source>User ID regexp error</source> <translation>Feil med regulæruttrykk for bruker-ID</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <source>Error applying rule: {0}</source> <translation>Feil ved anvending av regel: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/> <source>Lists field cannot be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/> <source>Lists field must be a directory</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/> <source><b>Rule not supported</b></source> <translation><b>Regelen støttes ikke</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/> <source><b>Error loading rule</b></source> <translation><b>Feil ved regellasting</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/> <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="669"/> <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="683"/> <source>PID field regexp error</source> <translation>Feil med regulæruttrykk for PID-felt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/> <source>Select at least one field.</source> <translation>Velg minst ett felt.</translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>Utkoblet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <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="761"/> <source> Your are about to delete this rule. </source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <source> Are you sure?</source> <translation> Er du sikker?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="568"/> <source>OpenSnitch Network Statistics {0}</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="570"/> <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="180"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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/dialogs/stats.py" line="253"/> <source>Hits</source> <translation type="unfinished"></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="1837"/> <source>Save as CSV</source> <translation type="unfinished"></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="738"/> <source>Delete</source> <translation type="unfinished"></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="731"/> <source>Disable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Enable</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Duplicate</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Edit</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="891"/> <source>Rule not found by that name and node</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="921"/> <source><b>Error:</b><br><br></source> <comment>{0}</comment> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="928"/> <source>Warning:</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="717"/> <source>Allow</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="718"/> <source>Deny</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="722"/> <source>Always</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Until reboot</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/> <source> You are about to delete this rule. </source> <translation type="unfinished"></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="392"/> <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="379"/> <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="380"/> <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="386"/> <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="383"/> <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="390"/> <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="395"/> <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="396"/> <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="391"/> <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="393"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="377"/> <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="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation type="unfinished"></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/dialogs/stats.py" line="252"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="709"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="719"/> <source>Reject</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation type="unfinished"></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>Adr.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="382"/> <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="384"/> <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="385"/> <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="404"/> <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="394"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Prosedyre</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="644"/> <source>New node connected</source> <translation>Koblet opp ny node</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.5.8.1/ui/i18n/locales/pt_BR/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017666�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/pt_BR/opensnitch-pt_BR.ts����������������������������������������0000664�0000000�0000000�00000327560�14401326716�0023431�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="299"/> <source>User ID</source> <translation>ID do usuário</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>IP de origem</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>ID de processo</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>IP de destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <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="679"/> <source>from this executable</source> <translation>a partir deste executável</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>a partir desta linha de comando</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>esta porta de destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>este usuário</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>este ip de destino</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>uma vez</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <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="763"/> <source>forever</source> <translation>para sempre</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation>a partir desse PID</translation> </message> </context> <context> <name>New node connected</name> </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="472"/> <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="454"/> <source>Default timeout</source> <translation>Tempo limite padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation>Duração padrão do pop-up</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="754"/> <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="314"/> <source>Default target</source> <translation>Alvo padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>centro</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>superior direito</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>inferior direito</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>superior esquerdo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>por executável</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>por linha de comando</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>por porta de destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>por ip de destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>por id de usuário</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="863"/> <source>once</source> <translation>uma vez</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <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="240"/> <source>forever</source> <translation>para sempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="905"/> <source>deny</source> <translation>negar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="914"/> <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="711"/> <source>Nodes</source> <translation>Nodes</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="717"/> <source>Process monitor method</source> <translation>Método de monitoramento de processo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="751"/> <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="881"/> <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="884"/> <source>Address</source> <translation>Endereço</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1024"/> <source>Default log level</source> <translation>Nível de registro padrão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <source>Version</source> <translation>Versão</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="734"/> <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="737"/> <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="809"/> <source>HostName</source> <translation>HostName</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="983"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="868"/> <source>until restart</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="873"/> <source>always</source> <translation>sempre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="995"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1000"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="767"/> <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="1039"/> <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="1074"/> <source>In memory</source> <translation>Na memória</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1079"/> <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="1345"/> <source>Close</source> <translation>Fechar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1356"/> <source>Apply</source> <translation>Aplicar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1367"/> <source>Save</source> <translation>Salvar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1093"/> <source>Database type</source> <translation>Tipo de banco de dados</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1100"/> <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="350"/> <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="665"/> <source>Action</source> <translation>Ação</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Duração</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="347"/> <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="150"/> <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="793"/> <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="894"/> <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="321"/> <source>Default position on screen</source> <translation>Posição padrão na tela</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="479"/> <source>any temporary rules</source> <translation>quaisquer regras temporárias</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="492"/> <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><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="495"/> <source>Don't save rules of duration</source> <translation>Não salve regras de duração</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="601"/> <source>Time</source> <translation>Tempo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="681"/> <source>Destination</source> <translation>Destino</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="649"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="697"/> <source>Process</source> <translation>Processo</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="617"/> <source>Rule</source> <translation>Regra</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="633"/> <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="589"/> <source>Events tab columns</source> <translation>Colunas da guia de eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation>por PID</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <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="461"/> <source>Disable pop-ups, only display an notification</source> <translation>Desativar pop-ups, exibir apenas uma notificação</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="508"/> <source>Desktop notifications</source> <translation>Notificações da área de trabalho</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Use system notifications</source> <translation>Usar notificações do sistema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="542"/> <source>Use Qt notifications</source> <translation>Usar notificações do Qt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="571"/> <source>Test</source> <translation>Testar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1178"/> <source>minutes</source> <translation>minutos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1204"/> <source>Minutes between events purges</source> <translation>Minutos entre expurgos de eventos</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1227"/> <source>days</source> <translation>dias</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> <source>Maximum days of events to keep</source> <translation>Máximo de dias de eventos para manter</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="14"/> <source>Rule</source> <translation>Regra</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/> <source>Node</source> <translation>Node</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/> <source>Apply rule to all nodes</source> <translation>Aplicar regra a todos os nodes</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>From this command line</source> <translation>Para esta linha de comando</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/> <source>From this executable</source> <translation>Para este executável</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/> <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="383"/> <source>To this IP / Network</source> <translation>Para este IP / Rede</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/> <source>once</source> <translation>uma vez</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> <source>1h</source> <translation>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="827"/> <source>always</source> <translation>sempre</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/> <source>To this port</source> <translation>Para esta porta</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/> <source>From this user ID</source> <translation>Para este ID de usuário</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/> <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="430"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.dominio.org, .*\.dominio.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <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="333"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/> <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="459"/> <source>LAN</source> <translation>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/> <source>Duration</source> <translation>Duração</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/> <source>Protocol</source> <translation>Protocolo</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> <source>To this host</source> <translation>Para este host</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/> <source>Name</source> <translation>Nome</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/> <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="713"/> <source>leave blank to autocreate</source> <translation>deixe em branco para criar automaticamente</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/> <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="54"/> <source>Priority rule</source> <translation>Regra de prioridade</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/> <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="77"/> <source>Case-sensitive</source> <translation>Sensível a maiúsculas e minúsculas</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/> <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><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="822"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/> <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="163"/> <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><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="229"/> <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="246"/> <source>From this PID</source> <translation>A partir deste PID</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/> <source>Network</source> <translation>Rede</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/> <source>List of domains/IPs</source> <translation>Lista de domínios/IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <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="549"/> <source>To this list of IPs</source> <translation>Para esta lista de IPs</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/> <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="608"/> <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="636"/> <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="651"/> <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="677"/> <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="857"/> <source>Reject</source> <translation>Rejeitar</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="290"/> <source>Save to CSV.</source> <translation>Salvar em CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="300"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="351"/> <source>Create a new rule</source> <translation>Crie uma nova regra</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="381"/> <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="420"/> <source>Status</source> <translation>Estado</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1668"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="464"/> <source>Start or Stop interception</source> <translation>Iniciar ou parar a interceptação</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="509"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="751"/> <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="1572"/> <source>Rules</source> <translation>Regras</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="860"/> <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="707"/> <source>Application rules</source> <translation>Regras de aplicação</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="809"/> <source>Permanent</source> <translation>Permanente</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="818"/> <source>Temporary</source> <translation>Temporário</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="936"/> <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="1023"/> <source>Applications</source> <translation>Aplicativos</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1130"/> <source>Addresses</source> <translation>Endereços</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1217"/> <source>Ports</source> <translation>Portas</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1301"/> <source>Users</source> <translation>Usuários</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1407"/> <source>Connections</source> <translation>Conexões</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1462"/> <source>Dropped</source> <translation>Dropado</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1517"/> <source>Uptime</source> <translation>Tempo de atividade</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1642"/> <source>Version</source> <translation>Versão</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation>Excluir todos os eventos interceptados</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="867"/> <source>Edit rule</source> <translation>Editar regra</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="881"/> <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>Exclua conexões que correspondam a esta regra</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="800"/> <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="177"/> <source>0</source> <translation>0</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Estatísticas</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Ajuda</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Fechar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Desabilitar</translation> </message> </context> <context> <name>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/> <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="52"/> <source>until reboot</source> <translation>até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>para sempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <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="285"/> <source>Outgoing connection</source> <translation>Conexão de saída</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Processo lançado de:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <source>from this executable</source> <translation>a partir deste executável</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>a partir desta linha de comando</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <source>to port {0}</source> <translation>para a porta {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/> <source>to {0}</source> <translation>para {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>do usuário {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>para {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="334"/> <source>from this PID</source> <translation>a partir desse PID</translation> </message> <message> <location filename="../../../opensnitch/notifications.py" line="108"/> <source>New outgoing connection</source> <translation>Nova conexão de saída</translation> </message> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/> <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="448"/> <source>Configuration applied.</source> <translation>Configuração aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/> <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="389"/> <source>Applying configuration on {0} ...</source> <translation>Aplicando configuração em {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/> <source>Error loading {0} configuration</source> <translation>Erro ao carregar configuração de {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/> <source>Error applying configuration: {0}</source> <translation>Erro ao aplicar configuração: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <source>Warning</source> <translation>Aviso</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/> <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="329"/> <source>DB type changed</source> <translation>Tipo de banco de dados alterado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/> <source>Restart the GUI in order effects to take effect</source> <translation>Reinicie a GUI para que os efeitos tenham efeito</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>carregando...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/> <source>There're no nodes connected.</source> <translation>Não há nodes conectados.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/> <source>Rule applied.</source> <translation>Regra aplicada.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/> <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="541"/> <source>Protocol regexp error</source> <translation>Erro de expressão de protocolo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/> <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="559"/> <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="563"/> <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="577"/> <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="581"/> <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="595"/> <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="599"/> <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="613"/> <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="617"/> <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="639"/> <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="651"/> <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="665"/> <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="205"/> <source>Error applying rule: {0}</source> <translation>Erro ao aplicar regra: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/> <source><b>Rule not supported</b></source> <translation><b>Regra não suportada</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/> <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="741"/> <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="429"/> <source><b>Error loading rule</b></source> <translation><b>Erro ao carregar regra</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/> <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="669"/> <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="683"/> <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="764"/> <source>Select at least one field.</source> <translation>Selecione pelo menos um campo.</translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Not running</source> <translation>Desativado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <source>Disabled</source> <translation>Desativado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> <source>Running</source> <translation>Ativo</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="886"/> <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="1258"/> <source> Are you sure?</source> <translation> Você tem certeza?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="572"/> <source>OpenSnitch Network Statistics {0}</source> <translation>Estatísticas da rede OpenSnitch {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="574"/> <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="180"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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="24"/> <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="1850"/> <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="752"/> <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="745"/> <source>Disable</source> <translation>Desabilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="747"/> <source>Enable</source> <translation>Habilitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="750"/> <source>Duplicate</source> <translation>Duplicado</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="751"/> <source>Edit</source> <translation>Editar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="905"/> <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="935"/> <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="942"/> <source>Warning:</source> <translation>Atenção:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="731"/> <source>Allow</source> <translation>Permitir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="732"/> <source>Deny</source> <translation>Negar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="736"/> <source>Always</source> <translation>Sempre</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="737"/> <source>Until reboot</source> <translation>Até reiniciar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1258"/> <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="281"/> <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="282"/> <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="283"/> <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="284"/> <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="382"/> <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="379"/> <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="288"/> <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="289"/> <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="290"/> <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="291"/> <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="292"/> <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="401"/> <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="295"/> <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="296"/> <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="298"/> <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="302"/> <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="303"/> <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="304"/> <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>Args</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <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="300"/> <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="301"/> <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="23"/> <source>What</source> <translation>Qual</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="733"/> <source>Reject</source> <translation>Rejeitar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="723"/> <source>Apply to</source> <translation>Aplicar para</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation>Nome da rede</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="285"/> <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="380"/> <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="381"/> <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="400"/> <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="293"/> <source>Precedence</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Precedencia</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="644"/> <source>New node connected</source> <translation>Novo node conectado</translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/ro_RO/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017700�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/ro_RO/opensnitch-ro_RO.ts����������������������������������������0000664�0000000�0000000�00000234775�14401326716�0023462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.0" 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="679"/> <source>from this executable</source> <translation>din acest executabil</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>din această linie de comandă</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>acest port de destinație</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>acest utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>această adresă IP de destinație</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="842"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="723"/> <source>once</source> <translation>o dată</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>1h</source> <translation>1o</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>forever</source> <translation>mereu</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="784"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="813"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="299"/> <source>User ID</source> <translation>ID utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>EtichetăText</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>Adresă IP sursă</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>ID proces</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>Adresă IP destinație</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation>Port destinație</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="417"/> <source>UI</source> <translation>Interfață utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="361"/> <source>Show advanced view by default</source> <translation>Arată implicit vizualizarea avansată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="695"/> <source>once</source> <translation>o dată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="226"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="231"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="236"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="241"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="246"/> <source>1h</source> <translation>1o</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="251"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="256"/> <source>forever</source> <translation>mereu</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="526"/> <source>Action</source> <translation>Acțiune</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="325"/> <source>Default target</source> <translation>Țintă implicită</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="377"/> <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="737"/> <source>deny</source> <translation>refuză</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="746"/> <source>allow</source> <translation>permite</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="290"/> <source>by executable</source> <translation>după executabil</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="295"/> <source>by command line</source> <translation>după linia de comandă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="300"/> <source>by destination port</source> <translation>după portul de destinație</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="305"/> <source>by destination ip</source> <translation>după adresa IP de destinație</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="310"/> <source>by user id</source> <translation>după identificatorul utilizatorului</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="186"/> <source>center</source> <translation>centru</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="191"/> <source>top right</source> <translation>sus la dreapta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="196"/> <source>bottom right</source> <translation>jos la dreapta</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="201"/> <source>top left</source> <translation>sus la stânga</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="206"/> <source>bottom left</source> <translation>jos la stânga</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="342"/> <source>Pop-up default duration</source> <translation>Durată implicită fereastră de notificare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="345"/> <source>Duration</source> <translation>Durată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="270"/> <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="273"/> <source>Filter connections also by:</source> <translation>Filtrează conexiunile și după:</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="97"/> <source>User ID</source> <translation>ID utilizator</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="113"/> <source>Destination port</source> <translation>Port destinație</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="129"/> <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>Dezactivează ferestrele de notificare, arată doar o alertă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="396"/> <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="399"/> <source>Default timeout</source> <translation>Durată implicită pentru alegere</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="540"/> <source>Nodes</source> <translation>Noduri</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="546"/> <source>Process monitor method</source> <translation>Metodă monitorizare procese</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="563"/> <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="566"/> <source>Log file</source> <translation>Fișier de jurnalizare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="580"/> <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="583"/> <source>Default duration</source> <translation>Durată implicită</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="596"/> <source>Apply configuration to all nodes</source> <translation>Aplică configurația la toate nodurile</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="619"/> <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="638"/> <source>HostName</source> <translation>NumeGazdă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="700"/> <source>until restart</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="705"/> <source>always</source> <translation>întotdeauna</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="713"/> <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="716"/> <source>Address</source> <translation>Adresă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="764"/> <source>Version</source> <translation>Versiune</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="815"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="827"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="832"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="856"/> <source>Default log level</source> <translation>Nivel implicit de jurnalizare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="871"/> <source>Database</source> <translation>Bază de date</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="918"/> <source>Database type</source> <translation>Tip bază de date</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="925"/> <source>Select</source> <translation>Selectare</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="946"/> <source>In memory</source> <translation>În memorie</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="951"/> <source>File</source> <translation>Fișier</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1008"/> <source>Close</source> <translation>Închide</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1019"/> <source>Apply</source> <translation>Aplică</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1030"/> <source>Save</source> <translation>Salvează</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="358"/> <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="126"/> <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="166"/> <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="622"/> <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="726"/> <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="80"/> <source>Default options</source> <translation>Opțiuni implicite</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="332"/> <source>Default position on screen</source> <translation>Compozziția implicită pe ecran</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="437"/> <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><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>Nu salva regulile de durată</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="466"/> <source>Time</source> <translation>Timp</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="476"/> <source>Destination</source> <translation>Destinație</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="486"/> <source>Protocol</source> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="496"/> <source>Process</source> <translation>Proces</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="506"/> <source>Rule</source> <translation>Regulă</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="516"/> <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><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="460"/> <source>Events tab columns</source> <translation>Coloane etichete evenimente</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="14"/> <source>Rule</source> <translation>Regulă</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/> <source>Node</source> <translation>Nod</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="45"/> <source>Apply rule to all nodes</source> <translation>Aplică regula la toate nodurile</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="115"/> <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>/cale/către/executabil, .*/bin/executabil[0-9\.]+$, ...</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="158"/> <source>Action</source> <translation>Acțiune</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> <source>To this port</source> <translation>Pentru acest port</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="172"/> <source>To this list of domains</source> <translation>Pentru această listă de domenii</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="195"/> <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>Rețea locală (LAN)</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="219"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="224"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="234"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="244"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="249"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="259"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="269"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="274"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="279"/> <source>fd00::/8</source> <translation>fd00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="310"/> <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><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="318"/> <source>once</source> <translation>o dată</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> <source>30s</source> <translation>30s</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="328"/> <source>5m</source> <translation>5m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/> <source>15m</source> <translation>15m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> <source>30m</source> <translation>30m</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> <source>1h</source> <translation>1o</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> <source>always</source> <translation>întotdeauna</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="364"/> <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="375"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domeniu.org, .*\.domeniu.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="382"/> <source>To this host</source> <translation>Pentru această gazdă</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/> <source>Duration</source> <translation>Durată</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="406"/> <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="416"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="436"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="441"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="449"/> <source>Protocol</source> <translation>Protocol</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="456"/> <source>From this executable</source> <translation>De la acest executabil</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="471"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="507"/> <source>From this command line</source> <translation>De la această linie de comandă</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> <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><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="557"/> <source>Name</source> <translation>Nume</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> <source>Enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/> <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>lăsați gol pentru creare automată</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="620"/> <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="628"/> <source>Priority rule</source> <translation>Regulă prioritate</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="648"/> <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="651"/> <source>Case-sensitive</source> <translation>Sensibil la majuscule</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>Salvează într-un fișier CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="115"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="166"/> <source>Create a new rule</source> <translation>Creare regulă nouă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="196"/> <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="235"/> <source>Status</source> <translation>Stare</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1697"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="279"/> <source>Start or Stop interception</source> <translation>Porniți sau opriți interceptarea</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="324"/> <source>Events</source> <translation>Evenimente</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="344"/> <source>Filter</source> <translation>Filtru</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="357"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="366"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="384"/> <source>Ex.: firefox</source> <translation>De exemplu: firefox</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="411"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="416"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="421"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="426"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="439"/> <source>Delete all intercepted events</source> <translation>Șterge toate evenimentele de interceptare</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="825"/> <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><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="1601"/> <source>Rules</source> <translation>Reguli</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="610"/> <source>enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="617"/> <source>Edit rule</source> <translation>Editare regulă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="631"/> <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><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>Căutare nume regulă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="781"/> <source>Application rules</source> <translation>Reguli aplicație</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="796"/> <source>Permanent</source> <translation type="unfinished">Permanent</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="810"/> <source>Temporary</source> <translation>Temporar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="876"/> <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><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="984"/> <source>Applications</source> <translation>Aplicații</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1051"/> <source>Delete all intercepted applications</source> <translation>Șterge toate aplicațiile interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1109"/> <source>Addresses</source> <translation>Adrese</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1211"/> <source>Ports</source> <translation>Porturi</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1313"/> <source>Users</source> <translation>Utilizatori</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1436"/> <source>Connections</source> <translation>Conexiuni</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1491"/> <source>Dropped</source> <translation>Aruncate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1546"/> <source>Uptime</source> <translation>Durată activitate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1671"/> <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>Șterge toate conexiunile care se potrivesc cu această regulă</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="712"/> <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>Șterge toate gazdele interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1159"/> <source>Delete all intercepted addresses</source> <translation>Șterge toate adresele interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1261"/> <source>Delete all intercepted ports</source> <translation>Șterge toate porturile interceptate</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1371"/> <source>Delete all intercepted users</source> <translation>Șterge toți utilizatorii interceptați</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="39"/> <source>Statistics</source> <translation>Statistici</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="40"/> <source>Enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="41"/> <source>Disable</source> <translation>Dezactivează</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="42"/> <source>Help</source> <translation>Ajutor</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Close</source> <translation>Închide</translation> </message> </context> <context> <name>popups</name> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="51"/> <source>until reboot</source> <translation>până la repornire</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="53"/> <source>forever</source> <translation>mereu</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="89"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="90"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="265"/> <source>Outgoing connection</source> <translation>Conexiune de ieșire</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="270"/> <source>Process launched from:</source> <translation>Procesul a fost lansat din:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="299"/> <source>from this executable</source> <translation>de la acest executabil</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="301"/> <source>from this command line</source> <translation>de la această linie de comandă</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="305"/> <source>to port {0}</source> <translation>către portul {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="364"/> <source>to {0}</source> <translation>către {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="308"/> <source>from user {0}</source> <translation>de la utilizatorul {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <source>to {0}.*</source> <translation>către {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="374"/> <source>to *.{0}</source> <translation>către *.{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> <source>to *{0}</source> <translation>către *{0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="411"/> <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="415"/> <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="421"/> <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> </context> <context> <name>preferences</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/> <source>Exception saving config: {0}</source> <translation>Exception saving config: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="279"/> <source>Warning</source> <translation>Avertisment</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="279"/> <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="285"/> <source>DB type changed</source> <translation>Tipul bazei de date a fost schimbat</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="285"/> <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="340"/> <source>Applying configuration on {0} ...</source> <translation>Se aplică configurația pe {0} ...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="169"/> <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="199"/> <source>Error loading {0} configuration</source> <translation>Error loading {0} configuration</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="385"/> <source>Configuration applied.</source> <translation>Configurația a fost aplicată.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/> <source>Error applying configuration: {0}</source> <translation>Eroare la aplicarea configurației: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="416"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="96"/> <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="115"/> <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="155"/> <source>loading...</source> <translation>Se încarcă...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="124"/> <source>There're no nodes connected.</source> <translation>Nu există niciun nod conectat.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="135"/> <source>Rule applied.</source> <translation>Regulă aplicată.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="137"/> <source>Error applying rule: {0}</source> <translation>Eroare la aplicarea regulii: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="290"/> <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="395"/> <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="409"/> <source>Protocol regexp error</source> <translation>Protocol regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="413"/> <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="427"/> <source>Process path regexp error</source> <translation>Process path regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="431"/> <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="445"/> <source>Command line regexp error</source> <translation>Command line regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="449"/> <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="463"/> <source>Dst port regexp error</source> <translation>Dst port regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="467"/> <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="481"/> <source>Dst host regexp error</source> <translation>Dst host regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="485"/> <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="507"/> <source>Dst IP regexp error</source> <translation>Dst IP regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="519"/> <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="533"/> <source>User ID regexp error</source> <translation>User ID regexp error</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="537"/> <source>Lists field cannot be empty</source> <translation>Lists field cannot be empty</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> <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="573"/> <source><b>Rule not supported</b></source> <translation><b>Regula nu este sprijinită</b></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="284"/> <source>Not running</source> <translation>Nu rulează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="285"/> <source>Disabled</source> <translation>Dezactivată</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="286"/> <source>Running</source> <translation>Rulează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="475"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch Network Statistics {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="477"/> <source>OpenSnitch Network Statistics for {0}</source> <translation>OpenSnitch Network Statistics for {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="591"/> <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="598"/> <source>Warning:</source> <translation>Avertisment:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="640"/> <source>Allow</source> <translation>Permite</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="641"/> <source>Deny</source> <translation>Refuză</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="644"/> <source>Always</source> <translation>Întotdeauna</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="645"/> <source>Until reboot</source> <translation>Până la repornire</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="653"/> <source>Disable</source> <translation>Dezactivează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="655"/> <source>Enable</source> <translation>Activează</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="658"/> <source>Duplicate</source> <translation>Duplică</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="659"/> <source>Edit</source> <translation>Editare</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="660"/> <source>Delete</source> <translation>Șterge</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="668"/> <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="1003"/> <source> Are you sure?</source> <translation> Sigur doriți acest lucru?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="786"/> <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="1003"/> <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="1490"/> <source>Save as CSV</source> <translation>Save as CSV</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="261"/> <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="262"/> <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="263"/> <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="264"/> <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="265"/> <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="266"/> <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="267"/> <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="268"/> <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="269"/> <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="270"/> <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="271"/> <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="272"/> <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="273"/> <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="274"/> <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="276"/> <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="280"/> <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="281"/> <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="282"/> <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>Argumente</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="277"/> <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="278"/> <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="279"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>DstPort</translation> </message> </context> </TS> ���opensnitch-1.5.8.1/ui/i18n/locales/ru_RU/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017714�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/ru_RU/opensnitch-ru_RU.ts����������������������������������������0000664�0000000�0000000�00000361725�14401326716�0023506�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="299"/> <source>User ID</source> <translation>ID пользователя</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>Заметка</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>Исходный IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>ID процесса</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>IP назначения</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation>Порт назначения</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation>из этого исполняемого файла</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>из этой командной строки</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>этот порт назначения</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>этот пользователь</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>этот ip-адрес назначения</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>один раз</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30 секунд</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5 минут</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15 минут</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30 минут</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <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="763"/> <source>forever</source> <translation>постоянно</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Запретить</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>до перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</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="472"/> <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="454"/> <source>Default timeout</source> <translation>Тайм-аут по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <source>Pop-up default duration</source> <translation>Продолжительность всплывающего окна по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="777"/> <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="314"/> <source>Default target</source> <translation>Цель по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>в центре</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>в правом верхнем углу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>в нижнем правом углу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>в левом верхнем углу</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>исполняемым файлом</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>по командной строке</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>по порту назначения</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>по IP-адресу назначения</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>по ID пользователя</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="881"/> <source>once</source> <translation>один раз</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30 секунд</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5 минут</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15 минут</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30 минут</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <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="240"/> <source>forever</source> <translation>постоянно</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="923"/> <source>deny</source> <translation>запрещено</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <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="734"/> <source>Nodes</source> <translation>Узлы</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="740"/> <source>Process monitor method</source> <translation>Метод мониторинга процесса</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="774"/> <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="899"/> <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="902"/> <source>Address</source> <translation>Адрес</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1042"/> <source>Default log level</source> <translation>Уровень логирования по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="950"/> <source>Version</source> <translation>Версия</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="813"/> <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="757"/> <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="760"/> <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="832"/> <source>HostName</source> <translation>Имя хоста</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="886"/> <source>until restart</source> <translation>до перезапуска</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <source>always</source> <translation>всегда</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1013"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1018"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <source>Apply configuration to all nodes</source> <translation>Применить конфигурацию ко всем узлам</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1057"/> <source>Database</source> <translation>База данных</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1092"/> <source>In memory</source> <translation>В памяти</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1097"/> <source>File</source> <translation>Файл</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1363"/> <source>Close</source> <translation>Закрыть</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1374"/> <source>Apply</source> <translation>Применить</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1385"/> <source>Save</source> <translation>Сохранить</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>до перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1111"/> <source>Database type</source> <translation>Тип базы данных</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1118"/> <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="350"/> <source>Show advanced view by default</source> <translation>Показывать расширенный вид по умолчанию</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="660"/> <source>Action</source> <translation>Действие</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Длительность</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="347"/> <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="150"/> <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="816"/> <source>Default action when the GUI is disconnected</source> <translation>Обычное действие, когда интерфейс отключен</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="912"/> <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="321"/> <source>Default position on screen</source> <translation>Положение по умолчанию на экране</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="713"/> <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><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>Не сохранять правила длительности</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="596"/> <source>Time</source> <translation>Время</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="676"/> <source>Destination</source> <translation>Назначение</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="644"/> <source>Protocol</source> <translation>Протокол</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="692"/> <source>Process</source> <translation>Процесс</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="612"/> <source>Rule</source> <translation>Правило</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="628"/> <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="584"/> <source>Events tab columns</source> <translation>Столбцы вкладки "События"</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <source>by PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="461"/> <source>Disable pop-ups, only display an notification</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="494"/> <source>Desktop notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="512"/> <source>Use system notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="528"/> <source>Use Qt notifications</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="557"/> <source>Test</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="570"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="705"/> <source>Theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="909"/> <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="1196"/> <source>minutes</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1222"/> <source>Minutes between events purges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1245"/> <source>days</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1255"/> <source>Maximum days of events to keep</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>Детали процесса</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="852"/> <source>Node</source> <translation>Узел</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="875"/> <source>Apply rule to all nodes</source> <translation>Применить правило ко всем узлам</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/> <source>From this command line</source> <translation>Из этой командной строки</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="341"/> <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="456"/> <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>30 секунд</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> <source>5m</source> <translation>5 минут</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> <source>15m</source> <translation>15 минут</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> <source>30m</source> <translation>30 минут</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> <source>1h</source> <translation>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="366"/> <source>To this port</source> <translation>К этому порту</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="247"/> <source>From this user ID</source> <translation>От этого ID пользователя</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="492"/> <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="503"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.domain.org, .*\.domain.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/> <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="406"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="513"/> <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>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>fd00::/8</source> <translation>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="449"/> <source>Protocol</source> <translation>Протокол</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="373"/> <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="912"/> <source>Name</source> <translation>Имя</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/> <source>Enable</source> <translation>Включить</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="928"/> <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>оставьте пустым для автосоздания</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="891"/> <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="899"/> <source>Priority rule</source> <translation>Правило приоритета</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="770"/> <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="773"/> <source>Case-sensitive</source> <translation>Чувствительно к регистру</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="442"/> <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><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="658"/> <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 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="221"/> <source>Applications</source> <translation type="unfinished">Приложения</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <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="240"/> <source>Is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/> <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="274"/> <source>From this PID</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>is regular expression</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="360"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>List of domains/IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="616"/> <source>To this list of network ranges</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="623"/> <source>To this list of IPs</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="649"/> <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="684"/> <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="712"/> <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="727"/> <source>To this list of domains (regular expressions)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> <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="764"/> <source>More</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>Сохранить в CSV.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="294"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="330"/> <source>Create a new rule</source> <translation>Создать новое правило</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="360"/> <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="399"/> <source>Status</source> <translation>Статус</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1627"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="437"/> <source>Start or Stop interception</source> <translation>Начать или остановить перехват</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="482"/> <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>Ex.: фаерфокс</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="724"/> <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="1531"/> <source>Rules</source> <translation>Правила</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="833"/> <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="680"/> <source>Application rules</source> <translation>Правила приложений</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Permanent</source> <translation>Постоянно</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="791"/> <source>Temporary</source> <translation>Временно</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="895"/> <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="982"/> <source>Applications</source> <translation>Приложения</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1089"/> <source>Addresses</source> <translation>Адреса</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1176"/> <source>Ports</source> <translation>Порты</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1260"/> <source>Users</source> <translation>Пользователи</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1366"/> <source>Connections</source> <translation>Соединения</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1421"/> <source>Dropped</source> <translation>Сброшено</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1476"/> <source>Uptime</source> <translation>Время работы</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1601"/> <source>Version</source> <translation>Версия</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation>Удалить все перехваченные события</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="840"/> <source>Edit rule</source> <translation>Редактировать правило</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="854"/> <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="773"/> <source>All applications</source> <translation>Все приложения</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="177"/> <source>0</source> <translation type="unfinished"></translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>Статистика</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Помощь</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Закрыть</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Включить</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Выключить</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>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="547"/> <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="91"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</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="285"/> <source>Outgoing connection</source> <translation>Исходящее соединение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>Процесс запущен из:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>из этой командной строки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <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="52"/> <source>until reboot</source> <translation>до перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <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="390"/> <source>to {0}</source> <translation>в {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>от пользователя {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>в {0}.*</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="108"/> <source>New outgoing connection</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/> <source>from this PID</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="230"/> <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="477"/> <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="321"/> <source>Exception saving config: {0}</source> <translation>Конфигурация сохранения исключений: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="418"/> <source>Applying configuration on {0} ...</source> <translation>Применение конфигурации к {0}...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/> <source>Error loading {0} configuration</source> <translation>Ошибка при загрузке конфигурации {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="479"/> <source>Error applying configuration: {0}</source> <translation>Ошибка применения конфигурации: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/> <source>Warning</source> <translation>Предупреждение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/> <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="351"/> <source>DB type changed</source> <translation>Тип БД изменен</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/> <source>Restart the GUI in order effects to take effect</source> <translation>Перезапустите графический интерфейс, чтобы эффекты вступили в силу</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="509"/> <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="127"/> <source>System</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="135"/> <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="387"/> <source>UI theme changed</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/> <source>Restart the GUI in order to apply the new theme</source> <translation type="unfinished"></translation> </message> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>загрузка...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="164"/> <source>There're no nodes connected.</source> <translation>Нет подключенных узлов.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <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="537"/> <source>protocol can not be empty, or uncheck it</source> <translation>протокол не может быть пустым, или снимите галочку</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="551"/> <source>Protocol regexp error</source> <translation>Ошибка регулярного выражения протокола</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="555"/> <source>process path can not be empty</source> <translation>путь процесса не может быть пустым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="569"/> <source>Process path regexp error</source> <translation>Ошибка регулярного выражения пути процесса</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="573"/> <source>command line can not be empty</source> <translation>командная строка не может быть пустой</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="587"/> <source>Command line regexp error</source> <translation>Ошибка регулярного выражения командной строки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="591"/> <source>Dest port can not be empty</source> <translation>Порт назначения не может быть пустым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="605"/> <source>Dst port regexp error</source> <translation>Ошибка регулярного выражения порта назначения</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="609"/> <source>Dest host can not be empty</source> <translation>Хост назначения не может быть пустым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="623"/> <source>Dst host regexp error</source> <translation>Ошибка регулярного выражения хоста назначения</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="627"/> <source>Dest IP/Network can not be empty</source> <translation>Целевой IP/сеть не могут быть пустыми</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="649"/> <source>Dst IP regexp error</source> <translation>Ошибка регулярного выражения Целевой IP</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="661"/> <source>User ID can not be empty</source> <translation>Укажите идентификатор пользователя</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="675"/> <source>User ID regexp error</source> <translation>Ошибка регулярного выражения ID пользователя</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="207"/> <source>Error applying rule: {0}</source> <translation>Ошибка применения правила: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Lists field cannot be empty</source> <translation>Поле списков не может быть пустым</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="751"/> <source>Lists field must be a directory</source> <translation>Поле списков должно быть каталогом</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="794"/> <source><b>Rule not supported</b></source> <translation><b>Правило не поддерживается</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="439"/> <source><b>Error loading rule</b></source> <translation><b>Ошибка загрузки правила</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="181"/> <source>There's already a rule with this name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="679"/> <source>PID field can not be empty</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="693"/> <source>PID field regexp error</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="781"/> <source>Select at least one field.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation>Не запущено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>Отключено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <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="899"/> <source> Your are about to delete this rule. </source> <translation> Вы собираетесь удалить это правило. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/> <source> Are you sure?</source> <translation> Вы уверены?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="583"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch статистика сети {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="585"/> <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="298"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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="24"/> <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="1901"/> <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="765"/> <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="758"/> <source>Disable</source> <translation>Отключить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="760"/> <source>Enable</source> <translation>Включить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="763"/> <source>Duplicate</source> <translation>Дублировать</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="764"/> <source>Edit</source> <translation>Редактировать</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="918"/> <source>Rule not found by that name and node</source> <translation>Правило не найдено по этому имени и узлу</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> <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="955"/> <source>Warning:</source> <translation>Предупреждение:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="744"/> <source>Allow</source> <translation>Разрешить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="745"/> <source>Deny</source> <translation>Запретить</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="749"/> <source>Always</source> <translation>Всегда</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="750"/> <source>Until reboot</source> <translation>До перезагрузки</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/> <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="285"/> <source>Name</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Имя</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="286"/> <source>Address</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Адрес</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> <source>Status</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Статус</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> <source>Hostname</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Имя хоста</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="386"/> <source>Version</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Версия</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="383"/> <source>Rules</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Правила</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="292"/> <source>Time</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Время</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> <source>Action</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Действие</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> <source>Duration</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Продолжительность</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> <source>Node</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Узел</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> <source>Enabled</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Включено</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="405"/> <source>Hits</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Посещаемость</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> <source>Protocol</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Протокол</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> <source>Process</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Процесс</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> <source>Destination</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Назначение</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> <source>Rule</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Правило</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> <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="308"/> <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>Аргументы</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <source>DstHost</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Хост назначения</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> <source>DstPort</source> <comment>This is a word, without spaces and symbols.</comment> <translation>Порт назначения</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="652"/> <source>New node connected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="23"/> <source>What</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <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="297"/> <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="384"/> <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="385"/> <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="404"/> <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="736"/> <source>Apply to</source> <translation type="unfinished"></translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="746"/> <source>Reject</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.5.8.1/ui/i18n/locales/tr_TR/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017712�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/i18n/locales/tr_TR/opensnitch-tr_TR.ts����������������������������������������0000664�0000000�0000000�00000354377�14401326716�0023507�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="299"/> <source>User ID</source> <translation>Kullanıcı Kimliği</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="333"/> <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="630"/> <source>TextLabel</source> <translation>TextLabel</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="426"/> <source>Source IP</source> <translation>Kaynak IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="449"/> <source>Process ID</source> <translation>İşlem Kimliği</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="582"/> <source>Destination IP</source> <translation>Hedef IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="605"/> <source>Dst Port</source> <translation>Hedef Bağlantı Noktası</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="679"/> <source>from this executable</source> <translation>bu programdan</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="684"/> <source>from this command line</source> <translation>bu komut satırından</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="689"/> <source>this destination port</source> <translation>bu hedef bağlantı noktası</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="694"/> <source>this user</source> <translation>bu kullanıcı</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="699"/> <source>this destination ip</source> <translation>bu hedef IP</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="728"/> <source>once</source> <translation>bir kere</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="733"/> <source>30s</source> <translation>30sn</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="738"/> <source>5m</source> <translation>5dak</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="743"/> <source>15m</source> <translation>15dak</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="748"/> <source>30m</source> <translation>30dak</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="753"/> <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="763"/> <source>forever</source> <translation>sonsuza kadar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="789"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="818"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="847"/> <source>+</source> <translation>+</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="758"/> <source>until reboot</source> <translation>yeniden başlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/res/prompt.ui" line="704"/> <source>from this PID</source> <translation>bu işlem kimliğinden</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="472"/> <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="454"/> <source>Default timeout</source> <translation>Öntanımlı zaman aşımı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="331"/> <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="777"/> <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="314"/> <source>Default target</source> <translation>Öntanımlı hedef</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="170"/> <source>center</source> <translation>merkez</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="175"/> <source>top right</source> <translation>sağ üst</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="180"/> <source>bottom right</source> <translation>sağ alt</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="185"/> <source>top left</source> <translation>sol üst</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="190"/> <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="274"/> <source>by executable</source> <translation>programa göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="279"/> <source>by command line</source> <translation>komut satırına göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="284"/> <source>by destination port</source> <translation>hedef bağlantı noktasına göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="289"/> <source>by destination ip</source> <translation>hedef IP'ye göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="294"/> <source>by user id</source> <translation>kullanıcı kimliğine göre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="881"/> <source>once</source> <translation>bir kere</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="210"/> <source>30s</source> <translation>30sn</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="215"/> <source>5m</source> <translation>5dak</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="220"/> <source>15m</source> <translation>15dak</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="225"/> <source>30m</source> <translation>30dak</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="230"/> <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="240"/> <source>forever</source> <translation>sonsuza kadar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="923"/> <source>deny</source> <translation>reddet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="932"/> <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="734"/> <source>Nodes</source> <translation>Düğümler</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="740"/> <source>Process monitor method</source> <translation>İşlem izleme yöntemi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="774"/> <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="899"/> <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="902"/> <source>Address</source> <translation>Adres</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1042"/> <source>Default log level</source> <translation>Öntanımlı günlük kaydı düzeyi</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="950"/> <source>Version</source> <translation>Sürüm</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="813"/> <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="757"/> <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="760"/> <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="832"/> <source>HostName</source> <translation>Ana makine adı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> <source>unix:///tmp/osui.sock</source> <translation>unix:///tmp/osui.sock</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="886"/> <source>until restart</source> <translation>yeniden başlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="891"/> <source>always</source> <translation>her zaman</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1013"/> <source>/var/log/opensnitchd.log</source> <translation>/var/log/opensnitchd.log</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1018"/> <source>/dev/stdout</source> <translation>/dev/stdout</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="790"/> <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="1057"/> <source>Database</source> <translation>Veri tabanı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1092"/> <source>In memory</source> <translation>Bellekte</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1097"/> <source>File</source> <translation>Dosya</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1363"/> <source>Close</source> <translation>Kapat</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1374"/> <source>Apply</source> <translation>Uygula</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1385"/> <source>Save</source> <translation>Kaydet</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="235"/> <source>until reboot</source> <translation>yeniden başlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1111"/> <source>Database type</source> <translation>Veri tabanı türü</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1118"/> <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="350"/> <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="660"/> <source>Action</source> <translation>Eylem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="366"/> <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="334"/> <source>Duration</source> <translation>Süre</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="254"/> <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="257"/> <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="451"/> <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="347"/> <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="150"/> <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="816"/> <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="912"/> <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="321"/> <source>Default position on screen</source> <translation>Ekranda öntanımlı konum</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="713"/> <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><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>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="596"/> <source>Time</source> <translation>Zaman</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="676"/> <source>Destination</source> <translation>Hedef</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="644"/> <source>Protocol</source> <translation>İletişim kuralı</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="692"/> <source>Process</source> <translation>İşlem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="612"/> <source>Rule</source> <translation>Kural</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="628"/> <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="584"/> <source>Events tab columns</source> <translation>Olaylar sekmesi sütunları</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="299"/> <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>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="494"/> <source>Desktop notifications</source> <translation>Masaüstü bildirimleri</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="512"/> <source>Use system notifications</source> <translation>Sistem bildirimlerini kullan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="528"/> <source>Use Qt notifications</source> <translation>Qt bildirimlerini kullan</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="557"/> <source>Test</source> <translation>Test</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="570"/> <source>System</source> <translation>Sistem</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="705"/> <source>Theme</source> <translation>Tema</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="909"/> <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="1196"/> <source>minutes</source> <translation>dakika</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1222"/> <source>Minutes between events purges</source> <translation>Olay temizlemeleri arasındaki dakikalar</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1245"/> <source>days</source> <translation>gün</translation> </message> <message> <location filename="../../../opensnitch/res/preferences.ui" line="1255"/> <source>Maximum days of events to keep</source> <translation>Saklanacak azami etkinlik günü sayısı</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="852"/> <source>Node</source> <translation>Düğüm</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="875"/> <source>Apply rule to all nodes</source> <translation>Kuralı tüm düğümlere uygula</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/> <source>From this command line</source> <translation>Bu komut satırından</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="341"/> <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="456"/> <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>30sn</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> <source>5m</source> <translation>5dak</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> <source>15m</source> <translation>15dak</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> <source>30m</source> <translation>30dak</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> <source>1h</source> <translation>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="366"/> <source>To this port</source> <translation>Bu bağlantı noktasına</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="247"/> <source>From this user ID</source> <translation>Bu kullanıcı kimliğinden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="492"/> <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="503"/> <source>www.domain.org, .*\.domain.org</source> <translation>www.etkialani.org, .*\.etkialani.org</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/> <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="406"/> <source>TCP</source> <translation>TCP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> <source>UDP</source> <translation>UDP</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> <source>UDPLITE</source> <translation>UDPLITE</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> <source>TCP6</source> <translation>TCP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> <source>UDP6</source> <translation>UDP6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> <source>UDPLITE6</source> <translation>UDPLITE6</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="513"/> <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>LAN</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> <source>127.0.0.0/8</source> <translation>127.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> <source>192.168.0.0/24</source> <translation>192.168.0.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> <source>192.168.1.0/24</source> <translation>192.168.1.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> <source>192.168.2.0/24</source> <translation>192.168.2.0/24</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> <source>192.168.0.0/16</source> <translation>192.168.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> <source>169.254.0.0/16</source> <translation>169.254.0.0/16</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> <source>172.16.0.0/12</source> <translation>172.16.0.0/12</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> <source>10.0.0.0/8</source> <translation>10.0.0.0/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> <source>::1/128</source> <translation>::1/128</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> <source>fc00::/7</source> <translation>fc00::/7</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> <source>ff00::/8</source> <translation>ff00::/8</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> <source>fe80::/10</source> <translation>fe80::/10</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> <source>fd00::/8</source> <translation>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="449"/> <source>Protocol</source> <translation>İletişim kuralı</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="373"/> <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="912"/> <source>Name</source> <translation>Ad</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/> <source>Enable</source> <translation>Etkinleştir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="928"/> <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>otomatik oluşturmak için boş bırakın</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="891"/> <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="899"/> <source>Priority rule</source> <translation>Öncelik kuralı</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="770"/> <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="773"/> <source>Case-sensitive</source> <translation>Büyük/küçük harfe duyarlı</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="442"/> <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><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="658"/> <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="221"/> <source>Applications</source> <translation>Uygulamalar</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> <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="240"/> <source>Is regular expression</source> <translation>Düzenli ifadedir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/> <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="274"/> <source>From this PID</source> <translation>Bu işlem kimliğinden</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> <source>is regular expression</source> <translation>düzenli ifadedir</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="360"/> <source>Network</source> <translation>Ağ</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> <source>List of domains/IPs</source> <translation>Etki alanlarının/IP'lerin listesi</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="616"/> <source>To this list of network ranges</source> <translation>Bu ağ aralıkları listesine</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="623"/> <source>To this list of IPs</source> <translation>Bu IP listesine</translation> </message> <message> <location filename="../../../opensnitch/res/ruleseditor.ui" line="649"/> <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="684"/> <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="712"/> <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="727"/> <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="754"/> <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="764"/> <source>More</source> <translation>Daha fazla</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>CSV olarak kaydet.</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="294"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="330"/> <source>Create a new rule</source> <translation>Yeni bir kural oluştur</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="360"/> <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="399"/> <source>Status</source> <translation>Durum</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1627"/> <source>-</source> <translation>-</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="437"/> <source>Start or Stop interception</source> <translation>Araya girmeyi başlat veya durdur</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="482"/> <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="199"/> <source>50</source> <translation>50</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="204"/> <source>100</source> <translation>100</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="209"/> <source>200</source> <translation>200</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="214"/> <source>300</source> <translation>300</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="724"/> <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="1531"/> <source>Rules</source> <translation>Kurallar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="833"/> <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="680"/> <source>Application rules</source> <translation>Uygulama kuralları</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="782"/> <source>Permanent</source> <translation>Kalıcı</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="791"/> <source>Temporary</source> <translation>Geçici</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="895"/> <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="982"/> <source>Applications</source> <translation>Uygulamalar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1089"/> <source>Addresses</source> <translation>Adresler</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1176"/> <source>Ports</source> <translation>Bağlantı noktaları</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1260"/> <source>Users</source> <translation>Kullanıcılar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1366"/> <source>Connections</source> <translation>Bağlantılar</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1421"/> <source>Dropped</source> <translation>Bırakıldı</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1476"/> <source>Uptime</source> <translation>Çalışma süresi</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="1601"/> <source>Version</source> <translation>Sürüm</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="227"/> <source>Delete all intercepted events</source> <translation>Araya girilen tüm olayları sil</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="840"/> <source>Edit rule</source> <translation>Kuralı düzenle</translation> </message> <message> <location filename="../../../opensnitch/res/stats.ui" line="854"/> <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="773"/> <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="177"/> <source>0</source> <translation>0</translation> </message> </context> <context> <name>contextual_menu</name> <message> <location filename="../../../opensnitch/service.py" line="43"/> <source>Statistics</source> <translation>İstatistikler</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="46"/> <source>Help</source> <translation>Yardım</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="47"/> <source>Close</source> <translation>Kapat</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="44"/> <source>Enable</source> <translation>Etkinleştir</translation> </message> <message> <location filename="../../../opensnitch/service.py" line="45"/> <source>Disable</source> <translation>Devre dışı bırak</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>notifications</name> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="547"/> <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="91"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> <source>forever</source> <translation>sonsuza kadar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/> <source>Outgoing connection</source> <translation>Giden bağlantı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/> <source>Process launched from:</source> <translation>İşlem şuradan başlatıldı:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/> <source>from this command line</source> <translation>bu komut satırından</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/> <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="52"/> <source>until reboot</source> <translation>yeniden başlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/> <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="390"/> <source>to {0}</source> <translation>{0} hedefine</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/> <source>from user {0}</source> <translation>{0} kullanıcısından</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/> <source>to {0}.*</source> <translation>{0}.* hedefine</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/> <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="434"/> <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="438"/> <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="444"/> <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="108"/> <source>New outgoing connection</source> <translation>Yeni giden bağlantı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/> <source>from this PID</source> <translation>bu işlem kimliğinden</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="230"/> <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="477"/> <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="321"/> <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="418"/> <source>Applying configuration on {0} ...</source> <translation>{0} üzerinde yapılandırma uygulanıyor...</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/> <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="479"/> <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="345"/> <source>Warning</source> <translation>Uyarı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/> <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="351"/> <source>DB type changed</source> <translation>V.T. türü değişti</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/> <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="509"/> <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="127"/> <source>System</source> <translation>Sistem</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="135"/> <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="387"/> <source>UI theme changed</source> <translation>Kullanıcı arayüzü teması değiştirildi</translation> </message> <message> <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/> <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> </context> <context> <name>proc_details</name> <message> <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/> <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="116"/> <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="156"/> <source>loading...</source> <translation>yükleniyor...</translation> </message> </context> <context> <name>rules</name> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="164"/> <source>There're no nodes connected.</source> <translation>Bağlı düğüm yok.</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/> <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="537"/> <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="551"/> <source>Protocol regexp error</source> <translation>İletişim kuralı düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="555"/> <source>process path can not be empty</source> <translation>işlem yolu boş olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="569"/> <source>Process path regexp error</source> <translation>İşlem yolu düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="573"/> <source>command line can not be empty</source> <translation>komut satırı boş olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="587"/> <source>Command line regexp error</source> <translation>Komut satırı düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="591"/> <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="605"/> <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="609"/> <source>Dest host can not be empty</source> <translation>Hedef ana makine boş olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="623"/> <source>Dst host regexp error</source> <translation>Hedef ana makine düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="627"/> <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="649"/> <source>Dst IP regexp error</source> <translation>Hedef IP düzenli ifade hatası</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="661"/> <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="675"/> <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="207"/> <source>Error applying rule: {0}</source> <translation>Kural uygulanırken hata oluştu: {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> <source>Lists field cannot be empty</source> <translation>Listeler alanı boş olamaz</translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="751"/> <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="794"/> <source><b>Rule not supported</b></source> <translation><b>Kural desteklenmiyor</b></translation> </message> <message> <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="439"/> <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="181"/> <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="679"/> <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="693"/> <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="781"/> <source>Select at least one field.</source> <translation>En az bir alan seçin.</translation> </message> </context> <context> <name>stats</name> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> <source>Not running</source> <translation>Çalışmıyor</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> <source>Disabled</source> <translation>Devre dışı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> <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="899"/> <source> Your are about to delete this rule. </source> <translation> Bu kuralı silmek üzeresiniz. </translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/> <source> Are you sure?</source> <translation> Emin misiniz?</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="583"/> <source>OpenSnitch Network Statistics {0}</source> <translation>OpenSnitch Ağ İstatistikleri {0}</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="585"/> <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="298"/> <source>Rules</source> <translation type="obsolete">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="300"/> <source>Action</source> <translation type="obsolete">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="24"/> <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="1901"/> <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="765"/> <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="758"/> <source>Disable</source> <translation>Devre dışı bırak</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="760"/> <source>Enable</source> <translation>Etkinleştir</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="763"/> <source>Duplicate</source> <translation>Çoğalt</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="764"/> <source>Edit</source> <translation>Düzenle</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="918"/> <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="948"/> <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="955"/> <source>Warning:</source> <translation>Uyarı:</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="744"/> <source>Allow</source> <translation>İzin ver</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="745"/> <source>Deny</source> <translation>Reddet</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="749"/> <source>Always</source> <translation>Her zaman</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="750"/> <source>Until reboot</source> <translation>Yeniden başlatılana kadar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/> <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="285"/> <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="286"/> <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="287"/> <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="288"/> <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="386"/> <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="383"/> <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="292"/> <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="293"/> <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="294"/> <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="295"/> <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="296"/> <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="405"/> <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="299"/> <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="300"/> <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="302"/> <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="306"/> <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="307"/> <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="308"/> <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>Argümanlar</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> <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="304"/> <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="305"/> <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="652"/> <source>New node connected</source> <translation>Yeni düğüm bağlandı</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="23"/> <source>What</source> <translation>Ne</translation> </message> <message> <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/> <source>Network name</source> <translation>Ağ adı</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> <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="297"/> <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="384"/> <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="385"/> <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="404"/> <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="736"/> <source>Apply to</source> <translation>Uygula</translation> </message> <message> <location filename="../../../opensnitch/dialogs/stats.py" line="746"/> <source>Reject</source> <translation>Geri çevir</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.5.8.1/ui/i18n/opensnitch_i18n.pro������������������������������������������������������0000664�0000000�0000000�00000002366�14401326716�0021000�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 FORMS += ../opensnitch/res/prompt.ui \ ../opensnitch/res/ruleseditor.ui \ ../opensnitch/res/preferences.ui \ ../opensnitch/res/process_details.ui \ ../opensnitch/res/stats.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 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch-ui.spec������������������������������������������������������������0000664�0000000�0000000�00000006350�14401326716�0020124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%define name opensnitch-ui %define version 1.5.8 %define unmangled_version 1.5.8 %define release 1 %define __python python3 %define desktop_file opensnitch_ui.desktop Summary: Prompt service and UI for the opensnitch 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: Simone "evilsocket" Margaritelli <evilsocket@protonmail.com> Url: https://github.com/evilsocket/opensnitch Requires: python3, python3-pip, (python3-pyinotify or python3-inotify), python3-qt5, python3-notify2 Recommends: (python3-slugify or python3-python-slugify), python3-protobuf >= 3.0 # 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 for i in $(ls /home) do if grep /home/$i /etc/passwd &>/dev/null; then path=/home/$i/.config/autostart/ if [ ! -d $path ]; then mkdir -p $path fi if [ -f /usr/share/applications/%{desktop_file} ];then ln -s /usr/share/applications/%{desktop_file} $path 2>/dev/null || true else echo "No desktop file: %{desktop_file}" fi fi done gtk-update-icon-cache /usr/share/icons/hicolor/ || true fi if [ $1 -eq 1 ]; then echo -e "\n You need to install 2 more packages: unicode_slugify and grpcio-tools. pip3 install grpcio-tools pip3 install unicode_slugify " fi %postun if [ $1 -eq 0 ]; then 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 "No desktop file for this user: $path" fi fi done pkill -15 opensnitch-ui 2>/dev/null || true echo "" echo " Remember to uninstall grpcio-tools and unicode_slugify if you don't" echo " need them anymore:" echo " pip3 uninstall unicode_slugify" echo " pip3 uninstall grpcio-tools" echo "" fi %build cd i18n; make; cd .. cp -r i18n/locales/ opensnitch/i18n pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc sed -i 's/^import ui_pb2/from . import ui_pb2/' opensnitch/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 %clean rm -rf $RPM_BUILD_ROOT %files -f INSTALLED_FILES %defattr(-,root,root) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016631�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/__init__.py��������������������������������������������������������0000664�0000000�0000000�00000000000�14401326716�0020730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/config.py����������������������������������������������������������0000664�0000000�0000000�00000014132�14401326716�0020451�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_CONFIG_URL = "https://github.com/evilsocket/opensnitch/wiki/Configurations" 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) RULES_DURATION_FILTER = () DEFAULT_DURATION_IDX = 6 # until restart DEFAULT_TARGET_PROCESS = 0 ACTION_DENY_IDX = 0 ACTION_ALLOW_IDX = 1 # don't translate ACTION_ALLOW = "allow" ACTION_DENY = "deny" ACTION_REJECT = "reject" 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" POPUP_CENTER = 0 POPUP_TOP_RIGHT = 1 POPUP_BOTTOM_RIGHT = 2 POPUP_TOP_LEFT = 3 POPUP_BOTTOM_LEFT = 4 DEFAULT_THEME = "global/theme" 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_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_TIMEOUT = 30 NOTIFICATIONS_ENABLED = "notifications/enabled" NOTIFICATIONS_TYPE = "notifications/type" NOTIFICATION_TYPE_SYSTEM = 0 NOTIFICATION_TYPE_QT = 1 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_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" # 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_ALLOW) 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.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): if ignore_temporary_rules: if temp_rules == 1: Config.RULES_DURATION_FILTER = (Config.DURATION_ONCE) elif temp_rules == 0: 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) else: Config.RULES_DURATION_FILTER = () ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/customwidgets/�����������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0021532�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/customwidgets/__init__.py������������������������������������������0000664�0000000�0000000�00000000000�14401326716�0023631�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/customwidgets/addresstablemodel.py���������������������������������0000664�0000000�0000000�00000004665�14401326716�0025575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5 import Qt, QtCore 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 import time import math 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 fillRows(self, q, upperBound, force=False): super().fillRows(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 Exception as e: col[2] = "" finally: self.items[n] = col self.lastItems = self.items ���������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/customwidgets/generictableview.py����������������������������������0000664�0000000�0000000�00000027560�14401326716�0025435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5 import Qt, QtCore 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 import time import math from PyQt5.QtCore import QCoreApplication as QC class GenericTableModel(QStandardItemModel): rowCountChanged = 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 data(self, index, role=QtCore.Qt.DisplayRole): if role == QtCore.Qt.DisplayRole: 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): 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)) 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)) # refresh the viewport with data from the db. def refreshViewport(self, scrollValue, maxRowsInViewport, force=False): # 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 upperBound > 0 or self.realQuery.at() < 0: self.fillRows(self.realQuery, upperBound, force) def fillRows(self, q, upperBound, force=False): rowsLabels = [] self.setVerticalHeaderLabels(rowsLabels) self.items = [] cols = [] self.blockSignals(True) #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.blockSignals(False) 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 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 def __init__(self, parent): QTableView.__init__(self, parent) #eventFilter to catch key up/down events and wheel events self.installEventFilter(self) self.verticalHeader().setVisible(True) 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.refresh() 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 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 onValueChanged(self, vSBNewValue): self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport, force=True) def onRowCountChanged(self): scrollBar = self.vScrollBar totalCount = self.model().totalRowCount scrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False) scrollBar.setMinimum(0) # we need to substract the displayed rows to the total rows, to scroll # down correctly. scrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport+1)) self.model().refreshViewport(scrollBar.value(), self.maxRowsInViewport) def getCurrentIndex(self): return self.selectionModel().currentIndex().internalId() 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 _selectLastRow(self): internalId = self.getCurrentIndex() idx = self.model().createIndex(self.maxRowsInViewport-2, 0, internalId) self.selectionModel().setCurrentIndex( idx, QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent ) def _selectRow(self, pos): internalId = self.getCurrentIndex() self.selectionModel().setCurrentIndex( self.model().createIndex(pos, 1, internalId), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent ) def onKeyUp(self): if self.selectionModel().currentIndex().row() == 0: self.vScrollBar.setValue(max(0, self.vScrollBar.value() - 1)) def onKeyDown(self): if self.vScrollBar.isVisible() == False: return if self.selectionModel().currentIndex().row() >= self.maxRowsInViewport-2: self.vScrollBar.setValue(self.vScrollBar.value() + 1) self._selectLastRow() 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._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) if newValue >= self.model().rowCount(): self._selectLastRow() return if newValue < self.model().rowCount(): self.vScrollBar.setValue(newValue) self._selectRow(0) 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() == 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 True #return False return super(GenericTableView, self).eventFilter(obj, event) ������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/customwidgets/main.py����������������������������������������������0000664�0000000�0000000�00000056173�14401326716�0023044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import Qt, 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 # PyQt5 >= v5.15.8 (#821) if hasattr(Qt, "QItemDelegate"): from PyQt5.Qt import QItemDelegate, QStyle else: from PyQt5.QtWidgets import QItemDelegate, QStyle class ColorizedDelegate(QItemDelegate): def __init__(self, parent=None, *args, config=None): QItemDelegate.__init__(self, parent, *args) self._config = config self._alignment = QtCore.Qt.AlignLeft | QtCore.Qt.AlignHCenter def paint(self, painter, option, index): if not index.isValid(): return super().paint(painter, option, index) nocolor=True value = index.data(QtCore.Qt.DisplayRole) for _, what in enumerate(self._config): if what == value: nocolor=False painter.save() painter.setPen(self._config[what]) if 'alignment' in self._config: self._alignment = self._config['alignment'] if option.state & QStyle.State_Selected: painter.setBrush(painter.brush()) painter.setPen(painter.pen()) painter.drawText(option.rect, self._alignment, value) painter.restore() if nocolor == True: super().paint(painter, option, index) 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.5.8.1/ui/opensnitch/database.py��������������������������������������������������������0000664�0000000�0000000�00000036376�14401326716�0020766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel, QSqlQuery import threading import sys from datetime import datetime, timedelta class Database: db = None __instance = None DB_IN_MEMORY = ":memory:" DB_TYPE_MEMORY = 0 DB_TYPE_FILE = 1 @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_name = dbname def initialize(self, dbtype=DB_TYPE_MEMORY, dbfile=DB_IN_MEMORY, db_name="db"): if dbtype != Database.DB_TYPE_MEMORY: self.db_file = dbfile self.db = QSqlDatabase.addDatabase("QSQLITE", self.db_name) self.db.setDatabaseName(self.db_file) 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 self._create_tables() 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: q = QSqlQuery("PRAGMA journal_mode = OFF", self.db) q.exec_() q = QSqlQuery("PRAGMA synchronous = OFF", self.db) q.exec_() q = QSqlQuery("PRAGMA cache_size=10000", self.db) q.exec_() else: q = QSqlQuery("PRAGMA synchronous = NORMAL", self.db) q.exec_() 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 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, " \ "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_() 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_() 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.fromisoformat(oldt) newest = datetime.fromisoformat(newt) 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, action_on_conflict="OR IGNORE"): qstr = "UPDATE " + action_on_conflict + " " + table + " SET " + fields + " 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) 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() error", query_str) print(q.lastError().driverText()) 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()) 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 insert_rule(self, rule, node_addr): self.insert("rules", "(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", (datetime.now().strftime("%Y-%m-%d %H:%M:%S"), node_addr, rule.name, str(rule.enabled), str(rule.precedence), rule.action, rule.duration, rule.operator.type, str(rule.operator.sensitive), rule.operator.operand, rule.operator.data), action_on_conflict="IGNORE") ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/desktop_parser.py��������������������������������������������������0000664�0000000�0000000�00000015457�14401326716�0022244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from threading import Lock import configparser import pyinotify import threading import glob import os import re import shutil import locale 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) 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): # 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 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/") 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.5.8.1/ui/opensnitch/dialogs/�����������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020253�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/dialogs/__init__.py������������������������������������������������0000664�0000000�0000000�00000000000�14401326716�0022352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/dialogs/preferences.py���������������������������������������������0000664�0000000�0000000�00000062211�14401326716�0023130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys import time import os import json 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 from opensnitch.notifications import DesktopNotifications from opensnitch import ui_pb2 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_NODES = 2 TAB_DB = 3 SUM = 1 REST = 0 def __init__(self, parent=None, appicon=None): QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) self._themes = Themes.instance() self._saved_theme = "" self._cfg = Config.get() self._nodes = Nodes.instance() self._db = Database.instance() self._notification_callback.connect(self._cb_notification_callback) self._notifications_sent = {} self._desktop_notifications = DesktopNotifications() self.setupUi(self) self.setWindowIcon(appicon) self.dbFileButton.setVisible(False) self.dbLabel.setVisible(False) self.dbType = None 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.checkUIRules.toggled.connect(self._cb_check_ui_rules_toggled) 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.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) if QtGui.QIcon.hasThemeIcon("emblem-default") == False: self.applyButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogApplyButton"))) self.cancelButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCloseButton"))) self.acceptButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogSaveButton"))) self.dbFileButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DirOpenIcon"))) if QtGui.QIcon.hasThemeIcon("list-add") == False: self.cmdTimeoutUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp"))) self.cmdTimeoutDown.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowDown"))) self.cmdDBMaxDaysUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp"))) self.cmdDBMaxDaysDown.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowDown"))) self.cmdDBPurgesUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp"))) self.cmdDBPurgesDown.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowDown"))) def showEvent(self, event): super(PreferencesDialog, self).showEvent(event) try: self._settingsSaved = False self._reset_status_message() self._hide_status_label() self.comboNodes.clear() 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() 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.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.comboDBType.currentIndexChanged.connect(self._cb_db_type_changed) self.checkDBMaxDays.toggled.connect(self._cb_db_max_days_toggled) # True when any node option changes self._node_needs_update = False def _load_themes(self): theme_idx, self._saved_theme = 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) 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.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.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)) # 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 ) 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) 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.spinDBMaxDays.setValue(dbMaxDays) self.spinDBPurgeInterval.setValue(dbPurgeInterval) self._load_themes() self._load_node_settings() self._load_ui_columns_config() def _load_node_settings(self): addr = self.comboNodes.currentText() if addr != "": 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('Server') != None: self.comboNodeAddress.setEnabled(True) self.comboNodeLogFile.setEnabled(True) self.comboNodeAddress.setCurrentText(node_config['Server']['Address']) self.comboNodeLogFile.setCurrentText(node_config['Server']['LogFile']) 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 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['InterceptUnknown'] = self.checkInterceptUnknown.isChecked() if node_config.get('Server') != None: # skip setting Server Address if we're applying the config to all nodes if self.checkApplyToNodes.isChecked(): node_config['Server']['Address'] = self.comboNodeAddress.currentText() node_config['Server']['LogFile'] = self.comboNodeLogFile.currentText() 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_ui_columns_config(self): cols = self._cfg.getSettings(Config.STATS_SHOW_COLUMNS) if cols == None: return for c in range(7): 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.checkHideDst.setChecked(checked) elif c == 4: self.checkHideProto.setChecked(checked) elif c == 5: self.checkHideProc.setChecked(checked) elif c == 6: 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.labelNodeName.setText("") self.labelNodeVersion.setText("") def _save_settings(self): self._save_ui_config() self._save_db_config() if self.tabWidget.currentIndex() == self.TAB_NODES: self._show_status_label() 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))) self._node_needs_update = False self.saved.emit() self._settingsSaved = True def _save_db_config(self): dbtype = self.comboDBType.currentIndex() 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())) if self.comboDBType.currentIndex() == self.dbType: return if dbtype == self._db.get_db_file(): return if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY: if self.dbLabel.text() != "": self._cfg.setSettings(Config.DEFAULT_DB_FILE_KEY, 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) return Message.ok( QC.translate("preferences", "DB type changed"), QC.translate("preferences", "Restart the GUI in order effects to take effect"), QtWidgets.QMessageBox.Warning) self.dbType = self.comboDBType.currentIndex() def _save_ui_config(self): self._save_ui_columns_config() 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()) ) 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()) if self._themes.available() and self._saved_theme != self.comboUITheme.currentText(): Message.ok( QC.translate("preferences", "UI theme changed"), QC.translate("preferences", "Restart the GUI in order to apply the new theme"), QtWidgets.QMessageBox.Warning) # 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) 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.checkHideDst.isChecked(): cols.append("3") if self.checkHideProto.isChecked(): cols.append("4") if self.checkHideProc.isChecked(): cols.append("5") if self.checkHideRule.isChecked(): cols.append("6") self._cfg.setSettings(Config.STATS_SHOW_COLUMNS, cols) 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 if addr.startswith("unix://"): self._cfg.setSettings(self._cfg.DEFAULT_DEFAULT_SERVER_ADDR, self.comboNodeAddress.currentText()) else: 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("Exception saving node config {0}: {1}").format((addr, str(e)))) return addr + ": " + str(e) return None 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.cmdDBMaxDaysUp.setEnabled(enable) self.cmdDBMaxDaysDown.setEnabled(enable) self.cmdDBPurgesUp.setEnabled(enable) self.cmdDBPurgesDown.setEnabled(enable) @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_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_db_type_changed(self): if self.comboDBType.currentIndex() == Database.DB_TYPE_MEMORY: self.dbFileButton.setVisible(False) self.dbLabel.setVisible(False) else: self.dbFileButton.setVisible(True) self.dbLabel.setVisible(True) def _cb_accept_button_clicked(self): self.accept() if not self._settingsSaved: self._save_settings() def _cb_apply_button_clicked(self): 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(15) def _cb_node_combo_changed(self, index): self._load_node_settings() def _cb_node_needs_update(self): self._node_needs_update = True def _cb_check_ui_rules_toggled(self, state): self.comboUIRules.setEnabled(state) def _cb_db_max_days_toggled(self, state): self._enable_db_cleaner_options(state, 1) def _cb_cmd_spin_clicked(self, spinWidget, operation): if operation == self.SUM: spinWidget.setValue(spinWidget.value() + 1) else: spinWidget.setValue(spinWidget.value() - 1) 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): 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 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/dialogs/processdetails.py������������������������������������������0000664�0000000�0000000�00000027722�14401326716�0023663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os import sys import json from PyQt5 import QtCore, QtGui, uic, QtWidgets from opensnitch import ui_pb2 from opensnitch.nodes import Nodes from opensnitch.desktop_parser import LinuxDesktopParser from opensnitch.utils import Message 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") == False: self.cmdClose.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCloseButton"))) self.iconStart = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPlay")) self.iconPause = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPause")) @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.5.8.1/ui/opensnitch/dialogs/prompt.py��������������������������������������������������0000664�0000000�0000000�00000061055�14401326716�0022155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import threading import sys import time import os import os.path import pwd import json import ipaddress from PyQt5 import QtCore, QtGui, uic, QtWidgets from PyQt5.QtCore import QCoreApplication as QC from slugify import slugify from opensnitch.desktop_parser import LinuxDesktopParser from opensnitch.config import Config from opensnitch.version import version from opensnitch import ui_pb2 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 ACTION_IDX_DENY = 0 ACTION_IDX_ALLOW = 1 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" # don't translate DURATION_30s = "30s" DURATION_5m = "5m" DURATION_15m = "15m" DURATION_30m = "30m" DURATION_1h = "1h" # don't translate # 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.setupUi(self) self.setWindowIcon(appicon) self._width = None self._height = None 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.denyButton.clicked.connect(self._on_deny_clicked) # also accept button self.applyButton.clicked.connect(self._on_apply_clicked) self._apply_text = QC.translate("popups", "Allow") self._deny_text = QC.translate("popups", "Deny") self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) 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) if QtGui.QIcon.hasThemeIcon("emblem-default") == False: self.applyButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogApplyButton"))) self.denyButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCancelButton"))) def showEvent(self, event): super(PromptDialog, self).showEvent(event) self.activateWindow() if self._width is None or self._height is None: self._width = self.width() self._height = self.height() self.setMinimumSize(self._width, self._height) self.setMaximumSize(self._width, self._height) self.move_popup() 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): self.applyButton.setText("%s" % self._apply_text) self.denyButton.setText("%s" % self._deny_text) 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) self.checkUserID.setVisible(state) self._ischeckAdvanceded = state def _button_clicked(self): self._stop_countdown() def _set_elide_text(self, widget, text, max_size=132): if len(text) > max_size: text = text[:max_size] + "..." widget.setText(text) def promptUser(self, connection, is_local, peer): # one at a time with self._lock: # reset state 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 _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): if self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) == self.ACTION_IDX_ALLOW: self.applyButton.setText("%s (%d)" % (self._apply_text, self._tick)) self.denyButton.setText(self._deny_text) else: self.denyButton.setText("%s (%d)" % (self._deny_text, self._tick)) self.applyButton.setText(self._apply_text) 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("") 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) else: self.appPathLabel.setVisible(False) self.appPathLabel.setText("") 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) self.argsLabel.setToolTip(app_args) else: self.argsLabel.setVisible(False) self.argsLabel.setText("") 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) self.destPortLabel.setText(str(con.dst_port)) 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) 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() if int(con.process_id) > 0: self.whatCombo.setCurrentIndex(int(self._cfg.getSettings(self._cfg.DEFAULT_TARGET_KEY))) else: self.whatCombo.setCurrentIndex(2) 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_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 """ 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 ) 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) 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): if combo.itemData(what_idx) == self.FIELD_PROC_PATH: return Config.RULE_TYPE_SIMPLE, "process.path", self._con.process_path elif combo.itemData(what_idx) == self.FIELD_PROC_ARGS: # this should not happen if len(self._con.process_args) == 0 or self._con.process_args[0] == "": return Config.RULE_TYPE_SIMPLE, "process.path", self._con.process_path return Config.RULE_TYPE_SIMPLE, "process.command", ' '.join(self._con.process_args) elif combo.itemData(what_idx) == self.FIELD_PROC_ID: return Config.RULE_TYPE_SIMPLE, "process.id", "{0}".format(self._con.process_id) elif combo.itemData(what_idx) == self.FIELD_USER_ID: return Config.RULE_TYPE_SIMPLE, "user.id", "%s" % self._con.user_id elif combo.itemData(what_idx) == self.FIELD_DST_PORT: return Config.RULE_TYPE_SIMPLE, "dest.port", "%s" % self._con.dst_port elif combo.itemData(what_idx) == self.FIELD_DST_IP: return Config.RULE_TYPE_SIMPLE, "dest.ip", self._con.dst_ip elif combo.itemData(what_idx) == self.FIELD_DST_HOST: return Config.RULE_TYPE_SIMPLE, "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, "dest.network", text elif combo.itemData(what_idx) == self.FIELD_REGEX_HOST: parts = combo.currentText().split(' ') text = parts[len(parts)-1] # ^(|.*\.)yahoo\.com dsthost = '\.'.join(text.split('.')).replace("*", "") dsthost = "^(|.*\.)%s" % dsthost[2:] return Config.RULE_TYPE_REGEXP, "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, "dest.ip", "%s" % '\.'.join(text.split('.')).replace("*", ".*") def _on_deny_clicked(self): self._default_action = self.ACTION_IDX_DENY self._send_rule() def _on_apply_clicked(self): self._default_action = self.ACTION_IDX_ALLOW 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.enabled = True self._rule.action = Config.ACTION_DENY if self._default_action == self.ACTION_IDX_DENY else Config.ACTION_ALLOW self._rule.duration = self._get_duration(self.durationCombo.currentIndex()) 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) 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()) 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": "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": "user.id", "data": str(self._con.user_id)}) rule_temp_name = slugify("%s %s" % (rule_temp_name, str(self._con.user_id))) if self._is_list_rule(): data.append({"type": self._rule.operator.type, "operand": self._rule.operator.operand, "data": self._rule.operator.data}) self._rule.operator.data = json.dumps(data) self._rule.operator.type = Config.RULE_TYPE_LIST self._rule.operator.operand = Config.RULE_TYPE_LIST self._rule.name = rule_temp_name 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.5.8.1/ui/opensnitch/dialogs/ruleseditor.py���������������������������������������������0000664�0000000�0000000�00000102657�14401326716�0023201�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 json import sys import os from opensnitch import ui_pb2 import time import ipaddress from opensnitch.config import Config from opensnitch.nodes import Nodes from opensnitch.database import Database from opensnitch.version import version from opensnitch.utils import Message, FileDialog 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 = "10\.\d{1,3}\.\d{1,3}\.\d{1,3}" classB_net = "172\.1[6-9]\.\d+\.\d+|172\.2[0-9]\.\d+\.\d+|172\.3[0-1]+\.\d{1,3}\.\d{1,3}" classC_net = "192\.168\.\d{1,3}\.\d{1,3}" others_net = "127\.\d{1,3}\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}" LAN_RANGES = "^(" + others_net + "|" + classC_net + "|" + classB_net + "|" + classA_net + "|::1|f[cde].*::.*)$" LAN_LABEL = "LAN" ADD_RULE = 0 EDIT_RULE = 1 WORK_MODE = ADD_RULE _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) def __init__(self, parent=None, _rule=None, appicon=None): super(RulesEditorDialog, self).__init__(parent) QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) self._notifications_sent = {} self._nodes = Nodes.instance() self._db = Database.instance() self._notification_callback.connect(self._cb_notification_callback) self._old_rule_name = None self.setupUi(self) self.setWindowIcon(appicon) 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.Apply).clicked.connect(self._cb_apply_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.dstPortCheck.toggled.connect(self._cb_dstport_check_toggled) self.uidCheck.toggled.connect(self._cb_uid_check_toggled) self.pidCheck.toggled.connect(self._cb_pid_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) if QtGui.QIcon.hasThemeIcon("emblem-default") == False: self.actionAllowRadio.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogApplyButton"))) self.actionDenyRadio.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCancelButton"))) if _rule != None: self._load_rule(rule=_rule) def _bool(self, s): return s == 'True' 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_dstport_check_toggled(self, state): self.dstPortLine.setEnabled(state) def _cb_uid_check_toggled(self, state): self.uidLine.setEnabled(state) def _cb_pid_check_toggled(self, state): self.pidLine.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 _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_apply_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 @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 get_rule_from_records(self, records): rule = ui_pb2.Rule(name=records.value(2)) rule.enabled = self._bool(records.value(3)) rule.precedence = self._bool(records.value(4)) rule.action = records.value(5) rule.duration = records.value(6) rule.operator.type = records.value(7) rule.operator.sensitive = self._bool(records.value(8)) rule.operator.operand = records.value(9) rule.operator.data = "" if records.value(10) == None else str(records.value(10)) return rule def _reset_state(self): self._old_rule_name = None self.rule = None self.ruleNameEdit.setText("") 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.uidLine.setText("") self.pidCheck.setChecked(False) self.pidLine.setText("") self.dstPortCheck.setChecked(False) self.dstPortLine.setText("") 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.enableCheck.setChecked(rule.enabled) self.precedenceCheck.setChecked(rule.precedence) 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: rule_options = json.loads(self.rule.operator.data) for r in rule_options: _sensitive = False if 'sensitive' in r: _sensitive = r['sensitive'] op = ui_pb2.Operator(type=r['type'], operand=r['operand'], data=r['data'], sensitive=_sensitive) self._load_rule_operator(op) return True def _load_rule_operator(self, operator): self.sensitiveCheck.setChecked(operator.sensitive) if operator.operand == "protocol": self.protoCheck.setChecked(True) self.protoCombo.setEnabled(True) self.protoCombo.setCurrentText(operator.data.upper()) if operator.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 == "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 == "user.id": self.uidCheck.setChecked(True) self.uidLine.setEnabled(True) self.uidLine.setText(operator.data) if operator.operand == "process.id": self.pidCheck.setChecked(True) self.pidLine.setEnabled(True) self.pidLine.setText(operator.data) if operator.operand == "dest.port": self.dstPortCheck.setChecked(True) self.dstPortLine.setEnabled(True) self.dstPortLine.setText(operator.data) if operator.operand == "dest.ip" or operator.operand == "dest.network": self.dstIPCheck.setChecked(True) self.dstIPCombo.setEnabled(True) if operator.data == self.LAN_RANGES: self.dstIPCombo.setCurrentText(self.LAN_LABEL) else: self.dstIPCombo.setCurrentText(operator.data) if operator.operand == "dest.host": self.dstHostCheck.setChecked(True) self.dstHostLine.setEnabled(True) self.dstHostLine.setText(operator.data) if operator.operand == "lists.domains": self.dstListsCheck.setChecked(True) self.dstListsCheck.setEnabled(True) self.dstListsLine.setText(operator.data) self.selectListButton.setEnabled(True) if operator.operand == "lists.domains_regexp": self.dstListRegexpCheck.setChecked(True) self.dstListRegexpCheck.setEnabled(True) self.dstRegexpListsLine.setText(operator.data) self.selectListRegexpButton.setEnabled(True) if operator.operand == "lists.ips": self.dstListIPsCheck.setChecked(True) self.dstListIPsCheck.setEnabled(True) self.dstListIPsLine.setText(operator.data) self.selectIPsListButton.setEnabled(True) if operator.operand == "lists.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): self._db.insert("rules", "(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", (datetime.now().strftime("%Y-%m-%d %H:%M:%S"), node_addr, self.rule.name, str(self.rule.enabled), str(self.rule.precedence), self.rule.action, self.rule.duration, self.rule.operator.type, str(self.rule.operator.sensitive), self.rule.operator.operand, self.rule.operator.data), action_on_conflict="REPLACE") 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.name = self.ruleNameEdit.text() self.rule.enabled = self.enableCheck.isChecked() self.rule.precedence = self.precedenceCheck.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 = "protocol" self.rule.operator.data = self.protoCombo.currentText() rule_data.append( { "type": Config.RULE_TYPE_SIMPLE, "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 = "process.path" self.rule.operator.data = self.procLine.text() rule_data.append( { "type": Config.RULE_TYPE_SIMPLE, "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 = "process.command" self.rule.operator.data = self.cmdlineLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, '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.dstPortCheck.isChecked(): if self.dstPortLine.text() == "": return False, QC.translate("rules", "Dest port can not be empty") self.rule.operator.operand = "dest.port" self.rule.operator.data = self.dstPortLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, '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 = "dest.host" self.rule.operator.data = self.dstHostLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, '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.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 = "dest.ip" self.rule.operator.type = Config.RULE_TYPE_REGEXP dstIPtext = self.LAN_RANGES 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 = "dest.ip" self.rule.operator.type = Config.RULE_TYPE_SIMPLE except Exception: self.rule.operator.operand = "dest.network" self.rule.operator.type = Config.RULE_TYPE_NETWORK if self._is_regex(dstIPtext): self.rule.operator.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(): if self.uidLine.text() == "": return False, QC.translate("rules", "User ID can not be empty") self.rule.operator.operand = "user.id" self.rule.operator.data = self.uidLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, 'operand': 'user.id', 'data': self.uidLine.text(), "sensitive": self.sensitiveCheck.isChecked() }) if self._is_regex(self.uidLine.text()): rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP if self._is_valid_regex(self.uidLine.text()) == False: return False, QC.translate("rules", "User ID regexp error") if self.pidCheck.isChecked(): if self.pidLine.text() == "": return False, QC.translate("rules", "PID field can not be empty") self.rule.operator.operand = "process.id" self.rule.operator.data = self.pidLine.text() rule_data.append( { 'type': Config.RULE_TYPE_SIMPLE, '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(): if self.dstListsLine.text() == "": return False, QC.translate("rules", "Lists field cannot be empty") if os.path.isdir(self.dstListsLine.text()) == False: return False, QC.translate("rules", "Lists field must be a directory") self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = "lists.domains" rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': 'lists.domains', 'data': self.dstListsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = json.dumps(rule_data) if self.dstListRegexpCheck.isChecked(): if self.dstRegexpListsLine.text() == "": return False, QC.translate("rules", "Lists field cannot be empty") if os.path.isdir(self.dstRegexpListsLine.text()) == False: return False, QC.translate("rules", "Lists field must be a directory") self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = "lists.domains_regexp" rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': 'lists.domains_regexp', 'data': self.dstRegexpListsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = json.dumps(rule_data) if self.dstListNetsCheck.isChecked(): if self.dstListNetsLine.text() == "": return False, QC.translate("rules", "Lists field cannot be empty") if os.path.isdir(self.dstListNetsLine.text()) == False: return False, QC.translate("rules", "Lists field must be a directory") self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = "lists.nets" rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': 'lists.nets', 'data': self.dstListNetsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = json.dumps(rule_data) if self.dstListIPsCheck.isChecked(): if self.dstListIPsLine.text() == "": return False, QC.translate("rules", "Lists field cannot be empty") if os.path.isdir(self.dstListIPsLine.text()) == False: return False, QC.translate("rules", "Lists field must be a directory") self.rule.operator.type = Config.RULE_TYPE_LISTS self.rule.operator.operand = "lists.ips" rule_data.append( { 'type': Config.RULE_TYPE_LISTS, 'operand': 'lists.ips', 'data': self.dstListIPsLine.text(), 'sensitive': self.sensitiveCheck.isChecked() }) self.rule.operator.data = json.dumps(rule_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 = json.dumps(rule_data) 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 = self.get_rule_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(2) 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() ���������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/dialogs/stats.py���������������������������������������������������0000664�0000000�0000000�00000261506�14401326716�0021775�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 import ui_pb2 from opensnitch.config import Config from opensnitch.version import version from opensnitch.nodes import Nodes from opensnitch.dialogs.preferences import PreferencesDialog from opensnitch.dialogs.ruleseditor import RulesEditorDialog from opensnitch.dialogs.processdetails import ProcessDetailsDialog from opensnitch.customwidgets.main import ColorizedDelegate, ConnectionsTableModel from opensnitch.customwidgets.generictableview import GenericTableModel from opensnitch.customwidgets.addresstablemodel import AddressTableModel from opensnitch.utils import Message, QuickHelp, AsnDB DIALOG_UI_PATH = "%s/../res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__) class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): RED = QtGui.QColor(0xff, 0x63, 0x47) GREEN = QtGui.QColor(0x2e, 0x90, 0x59) PURPLE = QtGui.QColor(0x7f, 0x00, 0xff) _trigger = QtCore.pyqtSignal(bool, bool) settings_saved = 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_DSTIP = 3 COL_PROTO = 4 COL_PROCS = 5 COL_RULES = 6 GENERAL_COL_NUM = 7 # 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 # procs COL_PID = 6 TAB_MAIN = 0 TAB_NODES = 1 TAB_RULES = 2 TAB_HOSTS = 3 TAB_PROCS = 4 TAB_ADDRS = 5 TAB_PORTS = 6 TAB_USERS = 7 # row of entries RULES_TREE_APPS = 0 RULES_TREE_NODES = 1 RULES_TREE_PERMANENT = 0 RULES_TREE_TEMPORARY = 1 RULES_COMBO_PERMANENT = 1 RULES_COMBO_TEMPORARY = 2 RULES_TYPE_PERMANENT = 0 RULES_TYPE_TEMPORARY = 1 FILTER_TREE_APPS = 0 FILTER_TREE_NODES = 3 # 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 } # restore scrollbar position when going back from a detail view LAST_SCROLL_VALUE = None # try to restore last selection LAST_SELECTED_ITEM = "" commonDelegateConf = { Config.ACTION_DENY: RED, Config.ACTION_REJECT: PURPLE, Config.ACTION_ALLOW: GREEN, 'alignment': QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter } commonTableConf = { "name": "", "label": None, "cmd": None, "view": None, "model": None, "delegate": commonDelegateConf, "display_fields": "*" } TABLES = { TAB_MAIN: { "name": "connections", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": commonDelegateConf, "display_fields": "time as Time, " \ "node as Node, " \ "action as Action, " \ "CASE dst_host WHEN ''" \ " THEN dst_ip || ' -> ' || dst_port " \ " ELSE dst_host || ' -> ' || dst_port " \ "END Destination, " \ "protocol as Protocol, " \ "process as Process, " \ "rule as Rule", "group_by": LAST_GROUP_BY, "last_order_by": "1", "last_order_to": 1, "rows_selected": False }, TAB_NODES: { "name": "nodes", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": { Config.ACTION_DENY: RED, Config.ACTION_REJECT: PURPLE, Config.ACTION_ALLOW: GREEN, Nodes.OFFLINE: RED, Nodes.ONLINE: GREEN, 'alignment': QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter }, "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, "rows_selected": False }, TAB_RULES: { "name": "rules", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": commonDelegateConf, "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 0, "rows_selected": False }, TAB_HOSTS: { "name": "hosts", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": commonDelegateConf, "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "rows_selected": False }, TAB_PROCS: { "name": "procs", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": commonDelegateConf, "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "rows_selected": False }, TAB_ADDRS: { "name": "addrs", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": commonDelegateConf, "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "rows_selected": False }, TAB_PORTS: { "name": "ports", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": commonDelegateConf, "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "rows_selected": False }, TAB_USERS: { "name": "users", "label": None, "cmd": None, "cmdCleanStats": None, "view": None, "filterLine": None, "model": None, "delegate": commonDelegateConf, "display_fields": "*", "header_labels": [], "last_order_by": "2", "last_order_to": 1, "rows_selected": False } } def __init__(self, parent=None, address=None, db=None, dbname="db", appicon=None): super(StatsDialog, self).__init__(parent) QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) self._current_desktop = os.environ['XDG_CURRENT_DESKTOP'] if os.environ.get("XDG_CURRENT_DESKTOP") != None else None 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_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_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_ARGS = QC.translate("stats", "Args", "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_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_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() # 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.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._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.startButton.clicked.connect(self._cb_start_clicked) self.prefsButton.clicked.connect(self._cb_prefs_clicked) self.saveButton.clicked.connect(self._on_save_clicked) 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.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) # 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_PRECEDENCE, self.COL_STR_ACTION, self.COL_STR_DURATION, "operator_type", "operator_sensitive", "operator_operand", "operator_data", ] 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_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_DESTINATION, self.COL_STR_PROTOCOL, self.COL_STR_PROCESS, 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", 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]) 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_RULES]['cmdCleanStats'].setVisible(False) self.TABLES[self.TAB_NODES]['cmdCleanStats'].setVisible(False) 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_RULES]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.TABLES[self.TAB_RULES]['view'].customContextMenuRequested.connect(self._cb_table_context_menu) for idx in range(1,8): 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)) self.TABLES[idx]['label'].setStyleSheet('color: blue; font-size:9pt; font-weight:600;') self.TABLES[idx]['label'].setVisible(False) self.TABLES[idx]['view'].doubleClicked.connect(self._cb_table_double_clicked) self.TABLES[idx]['view'].selectionModel().selectionChanged.connect(self._cb_table_selection_changed) self.TABLES[idx]['view'].installEventFilter(self) 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 = QtGui.QIcon().fromTheme("media-playback-start") self.iconPause = QtGui.QIcon().fromTheme("media-playback-pause") if QtGui.QIcon.hasThemeIcon("document-new") == False: 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 self._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.setWindowTitle(window_title) self._refresh_active_table() 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(2, 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): self.iconStart = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPlay")) self.iconPause = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPause")) self.newRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileIcon"))) self.delRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_TrashIcon"))) self.editRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogDetailedView"))) self.saveButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogSaveButton"))) self.prefsButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogDetailedView"))) self.startButton.setIcon(self.iconStart) self.cmdProcDetails.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogContentsView"))) self.TABLES[self.TAB_MAIN]['cmdCleanStats'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogResetButton"))) for idx in range(1,8): self.TABLES[idx]['cmd'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowLeft"))) if self.TABLES[idx]['cmdCleanStats'] != None: self.TABLES[idx]['cmdCleanStats'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogResetButton"))) def _load_settings(self): 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_text != None: # prevent from firing textChanged signal self.filterLine.blockSignals(True); self.filterLine.setText(dialog_general_filter_text) self.filterLine.blockSignals(False); 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/4), 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) 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) 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()) 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_rule(self, rule_name, node_addr): nid, noti = self._nodes.delete_rule(rule_name, node_addr, self._notification_callback) 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() selection = self.TABLES[cur_idx]['view'].selectedIndexes() if selection: rows = sorted(index.row() for index in selection) columns = sorted(index.column() for index in selection) rowcount = rows[-1] - rows[0] + 1 colcount = columns[-1] - columns[0] + 1 table = [[''] * colcount for _ in range(rowcount)] for index in selection: row = index.row() - rows[0] column = index.column() - columns[0] table[row][column] = index.data() stream = io.StringIO() csv.writer(stream, delimiter=',').writerows(table) QtWidgets.qApp.clipboard().setText(stream.getvalue()) 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 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")) 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")) # 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 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) except Exception as e: print(e) finally: self._clear_rows_selection() return True 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 = self._rules_dialog.get_rule_from_records(records) rule.name = "cloned-{0}-{1}".format(idx, rule.name) self._db.insert_rule(rule, node_addr) 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 = self._rules_dialog.get_rule_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._db.insert_rule(rule, node_addr) self._notifications_sent[nid] = noti def _table_menu_change_rule_field(self, cur_idx, model, selection, field, value): 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 = self._rules_dialog.get_rule_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 def _table_menu_enable(self, cur_idx, model, selection, is_rule_enabled): rule_status = "False" if is_rule_enabled == "True" else "True" 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 = self._rules_dialog.get_rule_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 def _table_menu_delete(self, cur_idx, model, selection): ret = Message.yes_no( QC.translate("stats", " Your are about to delete this rule. "), QC.translate("stats", " Are you sure?"), QtWidgets.QMessageBox.Warning) if ret == QtWidgets.QMessageBox.Cancel: return False 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) def _table_menu_edit(self, cur_idx, model, selection): 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("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 # 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_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_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", "<b>Error:</b><br><br>", "{0}").format(reply.data), QtWidgets.QMessageBox.Warning) 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 or self.IN_DETAIL_VIEW[self.TAB_RULES] == True: # the only table with context menu for now is the main rules table return self._context_menu_active = True 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 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]: 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") table = self.TABLES[cur_idx]['name'] label = self.TABLES[cur_idx]['label'].text() field = "dst_host" if cur_idx == self.TAB_NODES: field = "node" if label[0] == '/': label = "unix:{0}".format(label) 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" self._db.remove("DELETE FROM {0} WHERE what = '{1}'".format(table, label)) self._db.remove("DELETE FROM connections WHERE {0} = '{1}'".format(field, label)) 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._clear_rows_selection() self.IN_DETAIL_VIEW[cur_idx] = False self._set_active_widgets(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): 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(True, str(data)) p, addr = self._nodes.get_addr(data) self._set_nodes_query(addr) elif idx == StatsDialog.COL_PROCS: 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(True, str(data)) self._set_process_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(True, str(data)) self._set_rules_query(r_name, node) else: return 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 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(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(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) ) # selection changes occur before tableview's clicked event # if there're no rows selected, accept the selection. Otherwise clean it. def _cb_table_selection_changed(self, selected, deselected): cur_idx = self.tabWidget.currentIndex() # only update the flag (that updates data), if there's more than 1 # row selected. When using the keyboard to move around, 1 row will # be selected to indicate where you are. # NOTE: in some qt versions you can select a row and setQuery() won't # reset the selection, but in others it gets resetted. self.TABLES[cur_idx]['rows_selected'] = len(self.TABLES[cur_idx]['view'].selectionModel().selectedRows(0)) > 1 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) 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) parent = item.parent() parent_row = -1 if parent != None: parent_model = self.rulesTreePanel.indexFromItem(parent, col) parent_row = parent_model.row() self._set_rules_filter(parent_row, item_model.row(), item.text(0)) 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_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) 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 _clear_rows_selection(self): cur_idx = self.tabWidget.currentIndex() self.TABLES[cur_idx]['view'].selectionModel().reset() self.TABLES[cur_idx]['rows_selected'] = False def _are_rows_selected(self): cur_idx = self.tabWidget.currentIndex() return self.TABLES[cur_idx]['rows_selected'] 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) 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}%' ".format(text) elif idx == StatsDialog.TAB_HOSTS or idx == StatsDialog.TAB_PROCS or \ idx == StatsDialog.TAB_ADDRS or idx == StatsDialog.TAB_PORTS: 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): model = self._get_active_table().model() lastQuery = model.query().lastQuery() if "LIMIT" not in lastQuery: lastQuery += self._get_limit() self.setQuery(model, lastQuery) def _get_active_table(self): return self.TABLES[self.tabWidget.currentIndex()]['view'] def _set_active_widgets(self, 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) header = self.TABLES[cur_idx]['view'].horizontalHeader() if state == True: # going to normal state self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_COL_STATE, cur_idx), header.saveState()) else: # going to details 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); 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 active: self.rulesSplitter.refresh() self.comboRulesFilter.setVisible(self.rulesTreePanel.width() == 0) items = self.rulesTreePanel.selectedItems() if len(items) == 0: self._set_rules_filter() return item_m = self.rulesTreePanel.indexFromItem(items[0], 0) parent = item_m.parent() if parent != None: self._set_rules_filter(parent.row(), item_m.row(), item_m.data()) def _set_rules_tab_active(self, row, cur_idx, name_idx, node_idx): data = row.data() 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.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 Destination LIKE '%" + filter_text + "%'" \ " OR Rule LIKE '%" + filter_text + "%'" \ " OR Node LIKE '%" + filter_text + "%'" \ " OR Time 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): s = "AND c.src_ip='%s'" % data if '/' not in data else '' 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.dst_ip as {5}, " \ "c.dst_host as {6}, " \ "c.dst_port as {7}, " \ "c.process || ' (' || c.pid || ')' as {8}, " \ "c.process_args as {9}, " \ "c.process_cwd as CWD, " \ "c.rule as {10} " \ "FROM connections as c " \ "WHERE c.node LIKE '%{11}%' {12} GROUP BY {13}, c.process_args, c.uid, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol {14}".format( self.COL_STR_TIME, self.COL_STR_ACTION, self.COL_STR_HITS, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_DST_IP, self.COL_STR_DST_HOST, self.COL_STR_DST_PORT, self.COL_STR_PROCESS, self.COL_STR_PROC_ARGS, self.COL_STR_RULE, data, s, self.COL_STR_PROCESS, self._get_order() + self._get_limit())) def _get_nodes_filter_query(self, lastQuery, text): base_query = lastQuery.split("GROUP BY") qstr = base_query[0] if "AND" in qstr: # strip out ANDs if any os = qstr.split('AND') qstr = os[0] if text != "": qstr += "AND (c.time LIKE '%{0}%' OR " \ "c.action 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_args LIKE '%{0}%')".format(text) if len(base_query) > 1: qstr += " GROUP BY" + base_query[1] return qstr def _set_rules_filter(self, parent_row=-1, item_row=0, what=""): section = self.FILTER_TREE_APPS if parent_row == -1: if item_row == self.RULES_TREE_NODES: section=self.FILTER_TREE_NODES what="" 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 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 * FROM rules as r %s %s %s" % (what, self._get_order(), self._get_limit())) self._restore_details_view_columns( self.TABLES[self.TAB_RULES]['view'].horizontalHeader(), "{0}{1}".format(Config.STATS_VIEW_COL_STATE, self.TAB_RULES) ) 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.dst_port as {5}, " \ "CASE c.dst_host WHEN ''" \ " THEN c.dst_ip " \ " ELSE c.dst_host " \ "END {6}, " \ "c.process as {7}, " \ "c.process_args as {8}, " \ "c.process_cwd as CWD " \ "FROM connections as c " \ "WHERE {9} GROUP BY c.process, c.process_args, c.uid, {10}, c.dst_port {11}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_UID, self.COL_STR_PROTOCOL, self.COL_STR_DST_PORT, self.COL_STR_DESTINATION, self.COL_STR_PROCESS, self.COL_STR_PROC_ARGS, condition, self.COL_STR_DESTINATION, 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.dst_port as {6}, " \ "c.dst_ip as {7}, " \ "c.process || ' (' || c.pid || ')' as {8}, " \ "c.process_args as {9}, " \ "c.process_cwd as CWD, " \ "c.rule as {10} " \ "FROM connections as c " \ "WHERE c.dst_host = '{11}' GROUP BY c.pid, {12}, c.process_args, c.src_ip, c.dst_ip, c.dst_port, c.protocol, c.action, c.node {13}".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_DST_PORT, self.COL_STR_DST_IP, self.COL_STR_PROCESS, self.COL_STR_PROC_ARGS, 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}, " \ "CASE c.dst_host WHEN ''" \ " THEN c.dst_ip || ' -> ' || c.dst_port " \ " ELSE c.dst_host || ' -> ' || c.dst_port " \ "END {5}, " \ "c.pid as PID, " \ "c.process_args as {6}, " \ "c.process_cwd as CWD, " \ "c.rule as {7} " \ "FROM connections as c " \ "WHERE c.process = '{8}' " \ "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 {9}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_ACTION, self.COL_STR_UID, self.COL_STR_DESTINATION, self.COL_STR_PROC_ARGS, 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}, " \ "CASE c.dst_host WHEN ''" \ " THEN c.dst_ip " \ " ELSE c.dst_host " \ "END {6}, " \ "c.dst_port as {7}, " \ "c.process || ' (' || c.pid || ')' as {8}, " \ "c.process_args as {9}, " \ "c.process_cwd as CWD, " \ "c.rule as {10} " \ "FROM connections as c " \ "WHERE c.dst_ip = '{11}' GROUP BY c.pid, {12}, c.process_args, c.src_ip, c.dst_port, {13}, c.protocol, c.action, c.uid, c.node {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_DESTINATION, self.COL_STR_DST_PORT, self.COL_STR_PROCESS, self.COL_STR_PROC_ARGS, self.COL_STR_RULE, data, self.COL_STR_PROCESS, self.COL_STR_DESTINATION, 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.dst_ip as {6}, " \ "CASE c.dst_host WHEN ''" \ " THEN c.dst_ip " \ " ELSE c.dst_host " \ "END {7}, " \ "c.process || ' (' || c.pid || ')' as {8}, " \ "c.process_args as {9}, " \ "c.process_cwd as CWD, " \ "c.rule as {10} " \ "FROM connections as c " \ "WHERE c.dst_port = '{11}' GROUP BY c.pid, {12}, c.process_args, {13}, c.src_ip, c.dst_ip, c.protocol, c.action, c.uid, c.node {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_DST_IP, self.COL_STR_DESTINATION, self.COL_STR_PROCESS, self.COL_STR_PROC_ARGS, self.COL_STR_RULE, data, self.COL_STR_PROCESS, self.COL_STR_DESTINATION, 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.uid, " \ "c.node as {1}, " \ "count(c.dst_ip) as {2}, " \ "c.action as {3}, " \ "c.protocol as {4}, " \ "c.dst_ip as {5}, " \ "c.dst_host as {6}, " \ "c.dst_port as {7}, " \ "c.process || ' (' || c.pid || ')' as {8}, " \ "c.process_args as {9}, " \ "c.process_cwd as CWD, " \ "c.rule as {10} " \ "FROM connections as c " \ "WHERE c.uid = '{11}' GROUP BY c.pid, {12}, c.process_args, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol, c.action, c.node {13}".format( self.COL_STR_TIME, self.COL_STR_NODE, self.COL_STR_HITS, self.COL_STR_ACTION, self.COL_STR_PROTOCOL, self.COL_STR_DST_IP, self.COL_STR_DESTINATION, self.COL_STR_DST_PORT, self.COL_STR_PROCESS, self.COL_STR_PROC_ARGS, 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.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.settings_saved.emit() def _on_save_clicked(self): 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): tableWidget.setSortingEnabled(True) if model == None: model = self._db.get_new_qsql_model() if delegate != None: tableWidget.setItemDelegate(ColorizedDelegate(self, config=delegate)) if verticalScrollBar != None: tableWidget.setVerticalScrollBar(verticalScrollBar) tableWidget.vScrollBar.sliderPressed.connect(self._cb_scrollbar_pressed) tableWidget.vScrollBar.sliderReleased.connect(self._cb_scrollbar_released) self.setQuery(model, "SELECT " + fields + " FROM " + table_name + group_by + " ORDER BY " + order_by + " " + sort_direction + limit) tableWidget.setModel(model) 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) # 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: self._trigger.emit(is_local, need_query_update) 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: 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 or self._are_rows_selected(): 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.rowCount())) else: self.labelRowsCount.setText("") except Exception as e: print(self._address, "setQuery() exception: ", e) finally: self._show_columns() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/nodes.py�����������������������������������������������������������0000664�0000000�0000000�00000026530�14401326716�0020321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from queue import Queue from datetime import datetime import time import json from opensnitch import ui_pb2 from opensnitch.database import Database from opensnitch.config import Config class Nodes(): __instance = None 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): self._db = Database.instance() self._nodes = {} self._notifications_sent = {} def count(self): return len(self._nodes) def add(self, peer, client_config=None): try: proto, _addr = self.get_addr(peer) addr = "%s:%s" % (proto, _addr) if addr not in self._nodes: self._nodes[addr] = { 'notifications': Queue(), 'online': True, 'last_seen': datetime.now() } else: self._nodes[addr]['last_seen'] = datetime.now() self._nodes[addr]['online'] = True self.add_data(addr, client_config) self.update(proto, _addr) return self._nodes[addr] except Exception as e: print(self.LOG_TAG, "exception adding/updating node: ", e, "addr:", addr, "config:", client_config) return None def add_data(self, addr, client_config): if client_config != None: self._nodes[addr]['data'] = self.get_client_config(client_config) self.add_rules(addr, client_config.rules) def add_rule(self, time, node, name, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data): # 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, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", (time, node, name, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data), action_on_conflict="REPLACE") def add_rules(self, addr, rules): try: for _,r in enumerate(rules): self.add_rule(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), addr, r.name, str(r.enabled), str(r.precedence), r.action, r.duration, r.operator.type, str(r.operator.sensitive), r.operator.operand, r.operator.data) except Exception as e: print(self.LOG_TAG + " exception adding node to db: ", e) def update_rule_time(self, time, rule_name, addr): self._db.update("rules", "time=?", (time, rule_name, addr), "name=? AND node=?", action_on_conflict="OR REPLACE" ) def delete_all(self): self.send_notifications(None) self._nodes = {} def delete(self, peer): 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) if addr in self._nodes: del self._nodes[addr] 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: 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): peer = peer.split(":") # WA for backward compatibility if peer[0] == "unix" and peer[1] == "": peer[1] = "/local" return peer[0], peer[1] 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 start_interception(self, _addr=None, _callback=None): return self.firewall(not_type=ui_pb2.LOAD_FIREWALL, addr=_addr, callback=_callback) def stop_interception(self, _addr=None, _callback=None): return self.firewall(not_type=ui_pb2.UNLOAD_FIREWALL, addr=_addr, callback=_callback) def firewall(self, not_type=ui_pb2.LOAD_FIREWALL, 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): if reply == None: print(self.LOG_TAG, " reply notification None") return if reply.id in self._notifications_sent: if self._notifications_sent[reply.id] != None: 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] 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, proto, addr, status=ONLINE): try: 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, addr), "addr=?" ) except Exception as e: print(self.LOG_TAG + " exception updating DB: ", e, addr) def update_all(self, status=OFFLINE): try: for peer in self._nodes: 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, addr), "addr=?" ) except Exception as e: print(self.LOG_TAG + " exception updating nodes: ", e) def delete_rule(self, rule_name, addr, callback): rule = ui_pb2.Rule(name=rule_name) rule.enabled = False rule.action = "" rule.duration = "" rule.operator.type = "" rule.operator.operand = "" rule.operator.data = "" noti = ui_pb2.Notification(type=ui_pb2.DELETE_RULE, rules=[rule]) if addr != None: nid = self.send_notification(addr, noti, None) else: nid = self.send_notifications(noti, None) self._db.delete_rule(rule.name, addr) return nid, noti ������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/notifications.py���������������������������������������������������0000664�0000000�0000000�00000011572�14401326716�0022062�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python 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 def __init__(self): 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"): try: ntf = self.ntf2.Notification(title, body, icon) # timeouts seems to be ignored (on Cinnamon at least) timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15) # -1 and 0 are special values if timeout > 0: timeout = timeout * 1000 ntf.set_timeout(timeout * 1000) ntf.timeout = timeout * 1000 ntf.set_category(self.CATEGORY_NETWORK) # used to display our app icon an name. ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui") ntf.show() except Exception as e: print("[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("allow", self.ACTION_ALLOW, callback, connection) ntf.add_action("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.5.8.1/ui/opensnitch/res/���������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017422�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/__init__.py����������������������������������������������������0000664�0000000�0000000�00000000000�14401326716�0021521�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/icon-alert.png�������������������������������������������������0000664�0000000�0000000�00000046216�14401326716�0022176�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������x��� pHYs����od���tEXtSoftware�www.inkscape.org<�� �IDATxwx$қ;sĊ BQ"*bD+tPc V^T[ H{v..{33πHd""%efU?Wv_^ٗd�F 3 ^,~ `utDO�m]Hq򆁿,`&0ŸG4�HhYi0?F_dz֓7L#o( *h�7fV 8h@iר|cBI4�H̘Y%1pp:yK(_cqAlMd@D��-|}<XG 0 %4�HęYC-p)�_7 wIP�D$"̬> rO 69H� "ff@ KdCAL@D̎.�;$)[@ c$>i�B19pYx21_4�.Um>9RSAL@D df+@-)ǀQڒXvEO�ws~`w�> P9G"; FzHh�IrfaOO/{ ;DAH3;_|w( y/ �"In xLj �"IҀ9o9ywyGH߽U' uALI2f(0K$ h@$y"qc!q!]ZI@fV̆�7)}O3u" L+�" } {Hi@$AY)3{KdKjfV;F"K+�" ̪-&A0;D"C+�"qN7);D"CH3[qqfN ?Cf <"Ik6%!R<�D⌙#| Iz� ;DN�D∙|%�_E!Rt�Dℙ7#[D 0n!R4: ` zn_,ֱi&�rs]~_eIMKlٲ/Wrʒp}[Cd4�\O-̌ 2w<.\Ȃ Y`!L֭#'''LKK\rTRZ5kRf jתMZ5[.B#z87 u!k�DB)V/[ne4i2IzjY|0|0>h(ʗO}4 ;DvNH:޻%֖-[_|_|ɄaÆ IEa c嘣k X"� 2fVn1zX>?YfcfIUbE846mL, � "_mK.e153gyL h,.8-G~wN,!4�D(用ٺu+|~\$WZv-S\9h8)!_�DB 4pwK4.C}euJxG{NKl;'Z;Dh�qh$H1s&:|YoKD)))4>:\ٞG;'~$5!@ĕw-yט0k#:pPN�`wH� C3HKKKϣz|"?য়&diO~GYCNҬiD#oę�2xȻ#RTߏoM" ;"%8)"ΝMF1q9p4�đӦs%ѱ,X;6JH,uԾ >7&Zң#q]rT`4�ĈL{wHغu+}Ys7nwNq3 !&i6ėK];{>k ~f`V9A-A۞Hpyk:]9-[{TwRQyA$փ BL37QnAav?V+Ӄ ز>�fxyF=N^wH� dfu}VcgG``=o| &ڴ>]'##;(ALI�$̬pЄT2?D\7&M³~Cs + df*D�ōjfٗr`p%/ϧ8m xb0� 1efكf6 <L'eFD͛ڭ;{?6m)s#�:3mfwO�t߯H ;}.2,YRX." �fVڙX`zq5utZm?{[f(X(3efɻ-n0yt߽8Xl\t ?;0xG$* �fvGާGI"6lnཡxF3;;"i�b3ZW@kt1H\_)"Oم `yt"ǧ=zoxΉFZ']?]'xi^xey̪xG$ �R(fv&9GD"gͷz{gJU/� df l pwDϣ?];B̪wD`=;eg]�o \I*uL'i\4�̬yD$ m޼NySva3�JY_G؊kpuNdYRۼ#$gfsD$D-Z-ANNwJA:Yux IYLDWNL5y; 偻#$dfuO@) W_>; ךYmx ɘTfƽu?NQpwD�$NSDdڵvǝaj3;"i�HfV LF""EOx=34X4�$83kH[D$ʫL1;cGY x Y+s`/I999qlڴ;e{F@2w=[D$̝;_y;cGכYYx Y<(yuU.'zH fV \""m֭<A;e{YwD� ̬0h""ǟ&1|H/4�$�3 | ""ǟ ++;c{{ ==)pwKXdee1o-[F5\LV$++ҥK)Stʖ-Kը\2ժUcRvmRS(R+Vkopzls4�?n񐝝͔m<͛yYlYDST){/WBԬY3W}.Vhf" + �q̪doׯǟ&1q|?q"ӧ`֭Q?-[;wsO?׫T̿<nHãCCJ$7xчS:A;$4�!3+! /Xرٸq̘9+T\O>\rܱ|҉rIԨ$y >W^P h  3 �q&Va-2o|>c~!;֭[ǟ|ǟ| @C3ϠY&~u"ѕ/ =㝲M{4� Go}8ϻ%֯_ !/_X9fMry{1PzJ5R;CJ %%*V`  +�ė^$؛…{֬]5?¦+;F$Jrssyŗy^)vwHXi�fv#pwG$_~5ՄöXT%ӿ$>%h�)m̬9yބpuGjELnn.}ئV�Bꓷ\_6id. *N#"Q4l2333; ̬"P޻~؉]?#"1oFNh�)3K{Gvv6<׆#"1 lpU# �uyYĝAf-yޡڸGDbgٲew>mF�BNzw+=\ݱ[oE{CN\0�2fVH]7zXjڜ N ,];t3;"l4�[^-qFҕokCLpqCApwDh�++VVm.!zHHhUD�fVxһ A9)"b_�8; l4�@@%ɡ[qdgg{HO���gyGΆ z ""qdGy'@u�GyG�gfV8xXŲehw|ŗ)"gygN �$KΙC .d֬)">ǎp;�٩2eT./c_}wV/�wL>W]CVǟشiwF%3}VMkC#vf쟹zظq#;0�*g͚ͥ'sI |wa@U/\־E$~4;7 �1,_;v"33;EDYزewV�i�Fi&kw$7/xgϿ;i�!3ؑqg{:uw$9S8;" 4�@)=w$igx'��b.1cyᥗ3D$I̙w@0�;:xb3N$1w޼0S; 4�ĀU&dsrrά[;EDȆ ?34� Vn�yGl_f)"Bp@��J;ӤIʫ".Z䝠� � MVVљIR!oD��b!T,w$rxy3+M@aI̞3~;CD_KW��*zx�]13|E]V��zx�%{Mf$ V\��4�DQ3Bryɧ3DD�زe 3Buk �sw6/ ˖-[VZ �(� 4;CDY>�w@j{G�ٻ6lkN@t�X6_Dg˖- {xx�afĻodeeygCnsi�3 6з� lNp Bw#33;CD@99~W4 �ww�N)3S�ZH$fvPϻc:gwN(; �0t ""^wNx$6m1c3DDv)=#;A+� >5k7٥t� �"ypb)UwPr ryY;CX.b7k!1B=�@?snMU-Sʕ^O1_<�=Djjw:�o�"~6;AĎ>!#O 2DA*U@+��"Jy60o<D"&--/ǎs!$Ux'� �rΏ<e*%$իc0_oH 5k_/ : BoD昣fck{  2VWKKKK㚫:Q4kgnI1�ӈi�C<uVO 35kgf`>:-g_ �0klܸ3A$9hF|0TPV�@�Pbf ~ 2eEܔ*UMIܲ5jԠRŊv'5 �%W(0cLËY&?K@z'�L0Px�Pr{{ "n.Kٲes� NX`=jfh�(9דY999̝;3A$Tm"ɇ9ssd�hfƚs�.�)M~Α|  f5gפjy|Ş ?#qᝓj׮EڮdJich�(9M/"qTR\{|<v4M:;'i5<(83~Lyſk@jժ =CiG7� |bfcE@ɹ>rɒKN81{P }tCj L5!Ѡ<LËĭmw 5ꝓj֬A}(p3{(N $ԿJ_rE^:uxy핗[wN:Ը~`oc)�J׬YyxS;juHI/LTr  �%`fi l )v͌9?;'addsܱxgDJ3;;4�L�m$yݗ^OQF wqǑIfvwHIh�(`ӦM " y|<vԤx\%Ӏͬ1šd\�3#773A$-[#8csNFF:gJs�A@jj*999;[o*exzΐbد^=~?cO<ŪUi'^ ͬukc K@ɸ�F ¥L) h<N?4zoyWpѲEsX9 `wLa@ɸi*7m?0JMM {gfwHah�(\ٲ "Ico`.Z*@NN﹏իW{ҁ yqe!$.B>p ; £f1ٴ:kfyfvwȮh�(3+mfY/3 xл\9�A*yч4xwN^'ʼn4Ïosb&3�vR��� reקF =Lߋdۏ&[vzU2; �v̎1g{ ]D'--WG'&8%6u4ǟx w{SLNfvwȎ4�l'3|D2NhU^;ADvF<l/:쳷wN(egghs4oy.dڸ~!��Yu3,�G^Z!n4jtG Nu.tѓF'JGcҥITxl1@wBpUQO\n*48r{Zvv6.]YpwR#o% KI9�t1U;AD`M{Ɩ-[xgȻٴ9q/N]P;$�3lf�Ȼ?nZ3Ahd*rrr1rg6m#=��̬J@B<{ESժU~5֭z[lͷzsM [nN*=~�0Ӏ)ݎp4 "%pbF.8_;tH�� �IDAT Bff&{Ys8G2é�lo3{{Cwe$֠w6ٹ$oSlYztB*0͟E^]dݺu958p �fff�6=pa�F[ZX|YYqfsfƠߡIwNq,K 5�?x;a i>N8^h�ع$�VZu7͛sΒ%KP 1�_єt_q|�sI<� 9&O>N{GVm1swJQ�kfbu�06q“8`jzXl︋UVy$ Ѻm;{ErsssTbٸ�,0ہ{cNcTɻ"|ʔI.999ןj'~杓rrrx\;(:߽uq9�'<Ia ՕB�T$oΙ]Ba7nxZ9syV 0̢׸�h`"»%LRRR8304�Sߺu+jȏ EӪ ª6Nq3�/ |Md'twJoiyɧusu70` j s)ͬ<0}ߥSO9ҥK�O :�,[}Qxytރ{HI g ECoofȻ_oQByă}+`E~g6m7?` wu7999)N&AxP�fv<-pwKhѼwV�)3g xܐ&i 9NȦMSvPK4^8]|FRHO==|O�O 0�Y=uvL>;G aϹ򪎬_;ew4n�ȿQ` yR~Zc  �ǧ�̌~g5O,aҀ,١�,xt jN{ -NW�͟;pi'86ywl]9&/Ҁ[I'6RwJ*1}G@jE^x-oΑ4y21wQ=ffu"b�,#33spwK"HKKQIL�U:�|ǜѤ)="[lΑ7*�"bfVm}On e˗p"k K,adeen9[2S|ycYv-}[nuTʕ-VdzgÑG¤Ihbu/"QֶMkѝ ZA0/:v( )o^_0e4f͞Ŝ91goY;-"rrr%_�yf^} ^yxeL"Wݺ\שw<efAPOyn#7ӤI|L'f͞EvF,q,5~.�N5ݺ`…)c}GN)@./6?@^ mΜl8jSN &#wԅp�Xt)=y1c?N'fƝn:֠wNAـ }bѫ@X;̌S0vG|6~<-N�ɡ߀<a/\b ;{#;CսsvT QS). 1bGԛd`)<AfwҥKtM=?JѝfJ+1S%obI999|8 ķ}#qdr�233yɧykԩx^{]);�Ro=fv.0=P֭c8h0wġT3fT07Ջasssy<T/23uKZ 4n|wʎ6 _o�`fHr8^_V>dY#qysWC(_1~Ռ3ڭ;SMq%UXާvpΛA\Uo�`fU؏yfzoO 1Χ$ bE5kY3ܘWǿ<M@ $x3+ !Ǎ&[ēO_"Jfư#8is~[oRl&OQƢ~S? Ia̝;K.k5^%*t'�1�f͚^wʕzb /̜9ygZ3+_o�`fNzhW_Z_G�Q�֬]K=8L<%jǑyf=4hOx}T�3;x.M? ^O?=%ꖥ؋&@f?&0pP1u4z睱[̬П,">�H]ofןVm.`Y9$@W�fv]J{rD3=D_/ @(nT^GGOĔ.$bڵYtѓs[8x$7P3vtGa0tkFO&7sIBz" %>`f|0l8g6mF/1ŗ__zglH3;0_*}"4|H.˖-N$2- fqS V�͟W^ŝaŊ)z>̖-[3wEa(o/w!rrrޣ'Y\`y6b �֭ÏҴ|ͷQ) 1` ]hfevEafFⵢiÆ \wLw�C-_3|)PU3cQ<,[<q"ESBy>h,+WN  J`fjkpW_B%Uoϧ}zPZ6|;c{W "q nNE DB'B\�s/H2ob%R|g^ �fv�pOI^#/X.f"?)Q$z�FsڙMyvHy73I. @z _#j,\ŗ^+%V �6K.kϭߩ߻wz;L�{�0Yhӛă hS�6lǟ99B ph*�ob'E_-ˮ`ҥ)"n`Ѥ9oٛ[:Fܠvmw6gwJ`SLpK_/W�jb…p 7zvI6l`{glrg('J7ұ ̝;;EP2SSٔW4kq_M"q"77;U-gnz"/''oMb$]x�.IX-/�H4�YyHEړOC}$.%㭀dY^)4@QW��%oj&BK WAH&|5 .΀MJ�wkD"`w"Ŷ4 H\̌#Fyg�T�EY (ƛocƍ)"ŖoxCӈ�7F<֝;CDV@ �,,\ȴ33�N ;-%3h;5;CĒpE52�ǚ\y[(ۼyǼ3D"~ 1C$QǐQ8|_�`f'�Drss.:/q޾׸^)1=HZbL΀Nf TW?f?22ҹ=r'6j*AٲY1\�$| PjF5/^ӽ)͚Gc)]zrwhDS�lB+`V�.&oPx#dgg{gGySԨQ_PvH +"11{,[;cKSh&|~%l[9};v$�BvP'3c„3�;]hfGG$g7lƒ).$�-KP<`W/AH7 xgG.sq�$IM<;v7�.f9a^{M H:p5Wi@w�H?YrUT̨/vv )P36-ֻO?VZ!KʇcFn I4�h�If3fN5�(d֮}3Dvj_xS7q �"Ia T3jP)�3+E ~fZ c=N\yE -�d6=,/wb#Rca 8;C5m½wE͚<KVT�I0j@ق �,wbXd,_;CotNc#v�t$\RR\¨ _"!ӯw|ƌ7H�z$M6G5�fv0pKv&M?{gдYwO.L �5Xۢſ%xԂ�baý$>k<lؼCR �0g/N �!l޼1c>ΐ$J`7@=XkwB-n�0 x|Ƨҵ}%$`@�իjYblaYc?;ALԯ/$�t N( TwI^%0P<.Q@FF_w-_ԯ>N>>1(&edx'��jn?�양履&vmw$ƍOn:)U ty^81HD!$@4�3+ /NWf uwnEda �H'=<K�>KIPJꫮSǎdd{�Ĵ~zn�h_̙w$nHp{I6IH[lN�(m�po{'HV*ﺃsnI9E� =47~ "55/jǭ7DsMHbڼ9\iӼ$4hp(ݻu_GRbi: rrr>W&R�ד+Vdɒ *VJ z?f3bJi@$Qm޼  = �>c%AyC;J9u1/3;C.]3$ql�\M@޾ҭkN88")]}P �ӦON8WuSk±Hz2irbH ?a�Xb+WL8Pn]vSOqdH.]) z^B.--K.n=p߭ZDJc`CPֳ`ѢE;cֵ ի""1!01 pa=/!TZ5.Zh""q{w�t2KpEr-񽅯ȮYw�+�˗/<AHχ#;ED$B}`Ŋgiiith7tKX!5isR3׬<8jxЃ~)""1S�X~A=""Uf ? o�p]<ؙgNz)"".[;�`lܸC+T眖-SDD\Wx��3:пu/=,5j/{*SLV@O�$)SPl})"n:a ׫ 4{$~Hg'zg}��g"K \n︋UGINzw,�\Oć#G⌳;Ct $?; V �h�HFk֮qeW09 E BrO#G?0m[CU&ժVuj(;;}6_?{爈DͿ8;`涿p�Wyx 3fpn<dgks(I<'6:;B"!ëAf-lx: L�ԭSB';]ONǟz爈AHZ53Arߤ�{</!ٸ4iւW^{-[x爈۩�d9��dײ7Shl&L;GDX5m�0aI:�^{Q][p!̊+w ""!>{ӠA(v�fq_(Ug 3cѤ} "Z4o� i_p_�8~}#YYY<aڜ!g٥,O `��xϘAC]chP> a @kx-q.77sO8eyw%8MkmquJ9\rTT3A܊+\t̙wNDlݲ;ADJ ==-{g@SS5-;Dptã<$AL#gך'[ oE"q3Nb �3 X/b��8c$AlٲW^{=]"{'l3_6�̉aH=hI0۶9EEAHl8�<Kp�UTfU)`ƍϷ~Õ}73/ԗh1=\yƗ-!;aL �;�lcU 8c5AED7<ص)I {HKJi�{2  6?4�M:;Aܹ+DmېyzGl_ dN9$ʖ-! nۖg6mƀtDLҥҋ3Y| ==Ok!Ib,uymg)9"�Z:ի{glno[<4AȬY9‹=:- "ŖUW^靱7v� XG8 TX;CմN Hh֔} ͥ KF1P8K5k관YJJ v;c{ B7��l�ki{U7Z938�Lw93$ə}jJ~tZ@D 7 '� 0B _twwC=U 2ewL6{シ3ˋq�`hCyhO� 3gѶE:- "H:ygl y[Agm\ri}wvZ1p:- :^s55j3 ӀO*\EfZvι2idqPv-:o@~qA+�czjm;Cdfv]=ui$sݝՇԧ .l�X(SwN{C9IsI'tb`x(P�fHT5tGą5kڭ;۶ci9"%˗Czg  ;[�_˜VwH̘96\ȝaJI4}5k๢~N f(Rʕ+MdAd$"ShӺwƎ`yQiW+��3&oۚC>;CH2׬nҪ:- Ԯ=B)<Yo�ЗwѝT"vZ࣏?NbHKKOiA8߸ LBr1 8/;CX̌YCqVMD޻;#j~V��^(Gí7-Hkۺ]zwFA)Cvf@Ь[fdh: ""QY<_( �%9H5lx];CDDX:uxg)UwJA`cI^P@oKrH8c3DD$UT7_*U*{d0/R�'JzHJII ""222x՗Sv JHQdcmjժ3O?DD$"xgwΌ HPiH45:;CDD\jj*O>('twάnԋe�9,#s3DD$NcТy3`O�� IDAT]HX �xgHIIIч{pN)2 x&/X�~HFDB2ex8SDD$Nr޹xJ.1|"�ɈHP<|uxHȕ.]y:^ HhqV�s իRj ӷy)'/\ ]HDǀ}4?Ԯ]n0_{�WDϠ^wD0dx^l@C%Y�x~ѯ[DD&gɐYwJa,"D@+{#?CD}Nq-<l/22ҽs #,ZK۔t�UBԭSw G"""1{kpmk;h@\KUbE~Ƨ"""1#dCi<Eb ")##W_~n>&A)T:^}V8߿&7`�|׋ yIOsA""RHujf`ymyAL"6�׌-3d@g) 9o<`$W�`%MZ9gNbkM?ؕrydS 3(nT-[x{#""Ttix cF)e9Ad�O\Dqu3j8N8;EDDveC[l.`@K+ nԭSo=S|y~K/Я[p9%uG\De��#h~Aml\ @jxn=Ok x3 j@.Q>FTԨQ^O=iPTR%n>C.lw>IpwDTo .&qVs axᅗ?DD^ժUо=_..О &j3;�T!%%[q-{k,];KD$w_.ڴn4C~ +f't`4P:VnjM6ӷ?,Α8rIj:^JLJ3Ap\~%|҉D촋e)A&W%@X7Zrss/ӷ|wH\V*{mڴbzsi58)!ۋu&؝  >?;GD$ʔ)I'6u8q$o?t]> ǎ\a"c~_~a@D^ҥi7miOzc $!�R!@kŋ7ۻ::﯃mlh Kw##P ØC" &D xeP \xaBW1ƈA&"*bHܢQv]Iw9'뚶~s+y&}9\~{~{yΜ9S:$aw^ݾ=#g&W2\l<aw144M7{vrΝmn".d/J(7N06> cl|Ǐ3;N*MV\sev4a஺<귒Yfn~|t:a\055EW*d9NOO,B9oQ133ܲ_sg3^SSSgnw@>&-[t~啌m.$po֢x�CT{>V:$I7Ǻ'6B- �tIަii U� 3/OH=qtBZD?(Enn5+��"LD<| hZ$:W#a.D�;4AI@9|*"~W:H?Ժ��z\U:$�K�[Kg$$PD|bh @OaJg$3qtPOʕ$YQ`Р2sUJg$ _.d5f`x J.p[h B!Y$It�\Dt3� ESJg$5ʫa� �wD|4ݰ!II1?%2sUGHcxx$"S:LiW�duwY$Ix\N R"%V][ۑ$W`Dw?�,ۀG Ǒ$?_B+ |4pi8UZU�dUTCH.K7#S:L�̫/P4 ITa@Of^<�|^6$<GJi* 2sg}:y.xts[Ff^OU #Im56<2s#!~n.(Imxꐞ'"y*d滀/a W<`ͅ>ܽ<X޾y`Eo#qlv� ~T- %IGCqlv�Xxսv0g/HoP59 x!"9,�.JU\۽vWqv/d(^8 ^'i5T8ej5,�j*3IlreE^3 Y]ʶ=KY*Z9kl3m?BqjCź1N4գs=T='u8;vwbϧ#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����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/icon-off.png���������������������������������������������������0000664�0000000�0000000�00000047343�14401326716�0021643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������x��� pHYs����od���tEXtSoftware�www.inkscape.org<�� �IDATxy|}/yfՌv Іd$! &싓&mgMH۴N A|KR鯷V齷MKSj͌d~WmM jX6F?@E-3s3y^~< >ss�""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""k͔<)4<�>)O1 O^)e]/MpWJR7 #d � F]BD�iOw8R*!ؿ�P�Hp!��.;R64ܹ|M �"ƞ={\iiiu�I) !H)�Vm �0#?ݵk׈`Dwc@DʴEѵB5Rʵ�p!�?F:[nTR �"J9iOEQ�o H)_nhh+D)�Usss]4)�TgPi~n ,�(暛W)�P:Ō�ۑH;w:%'�D`p>_R8J�ƈ@<X�Ѵ޽;~DQ$RR~}ǎoC?.<OHW'.l>;: Y �"FÆauq @f۳unaZX�C~mBgQ5 iޱcaX�ѸZZZ2�|ډ/ho: �{_��dCݾ}+X�[sg!_drYu �"OWB?ؾ}QAH,�RX0\ ϥUg0|SJ円ÐZ,�R/B nmK熔CCC@b@BZ[[mna|q: % ��X: i6[n=:% �$w?Q4a)_ٮ]FTc@G !JUg!8noر A(X�%}e�~Uufgg͛7C(455B1Yڤma:  �$h/**ڻPFXmWB:  �$ /̊D"Kq"i  �" O�WL;Eu9 ,, >[pD7 @8Y�~[uJYߗR~_u�Dܜ+lR_uJy߾} AhX�YHKK˼h4=�WUu `02l�;TaR4Ytp8p8`!a<n#h4 4FDC#/۶mc` ,�4~HOOGzz:^/^/<<v;G\1::aB;�FG[!7LV�TP0 dgg#77ɉ ?### zzzۋp8ǿ9Omٲ%:M�ܼjZZ 0k,|qR===z*_4UJ(!ď\.Ǟ~!Yh|,�4Bo�5Y%33EEEGff817::˗/===T  >k׮AA,�4o>w8�V%P\\'ݍv:lҧ:݋�&n!YEٳgcΜ9SG~\pI>37Tgc@ݻwUgǃ2rH$N;wCCI;e~ƍ~gU[X�)v{_P%rrrPYY|GRҥKx۫:N<svv7o&5`DIi淐dmχE'I a֬YM@HͲe^fAU JU/+Fvv('x8}4.]:RLH)?UTT YYRr"9 ܦ:K,dgg>OuΑ8@@QQ9fbѢEQ'\.ƍɞCG~{;:Kb(Ro16 UUUx`)D8y$n޼:L K)644:H*b@@kL�> WPP;~MDQ8{wlFOܹ S�D  +?`mҰtRTWWky O*2 yyy(**7 2 e˖}^lcE�Phiiʹ�=RL}aDa�¡Pfihh'lKKKF$9$U+--ł 8k'NIB=۷o+a@o߾h4Zm<!<)eJ @6@�zoy!))'~[.. -BAA(4I8z(^:tF}}7UH,�HK---Ein�NJ|XkRʟR~@LSVPPŋtBp98qjkM\cǎ3|R۳gOfZZ`JՙRa7oΝ: �|Mܸa3x~ ڵkDudǝ�I*!'`̊\.,[ P dffbݺu8z(TǙe.�~Gud�%B|',V~aҥ</IYlJ@J)jhh Ɍ�޽{vK)? `<2qS$w5a| è۶muA�(.EEEO-�||Xf ~ӟZ٦i$+v�(ݛoٶ� `<41͆KPuJp87x===L'Qudbyi_)�|nLs+V@NΤG$c&|M+.xYJYpMud)�@ �g6IUn{: )dqۺ?!P iA ;�4-MMMO�B+WBRرchooWa۷:H2a@Sa| Yhj^/V\4QHCmmm8s";;{͛-p &ia `,4ucm4ZDQttt2}}}_g$ v�衂`i& y$˅իWO$đ#Gݭ:DnFڝ;wW$@zJ)(pp88O&%K0::k״\t1 �~Uud;:z8=BRYhl6V\Gh0^{5 2)X}U:v�9hoOB3xb4-cDBO�-\#XMu�ROJ)?/b<4sCyydav>Rq7|gU2N`0X!lIuR,^̃)6p1s.;;NJa@`(8'l,\&Nqq1***TOEooV!o߾p8IY(v֯_g)L믿׵;pTmٲE V@inn^"ҥK9S\phwwI8nPªX�`0XoޤSYY|1(n-ז!~{T">^|Eo(jRr$j1(bΜ9p(wH) Ugv�\sssYIfaɒ%y(1,X�Gn!~vcĚVyPQ: /%fq*0Oa5,�T lƏ�pb8I|>nCJigOJ{RJ¦@ $eٰh"1(vqE`CX $"@`7{<$Ja)v,X@u{!:pHξoƍaI_]M\cǎU~$ݻwSB]]҆n V޽;-==;�~Au]8Nvttpf{{P(7obhhh4ѿqbcݣ ϟWc̰(ڲeK F@ꃿnGvv6zt8Θ_+ ƍD?qƍjϟMVuu5UGp8�T �ڷo;p8\!333aG wkh~:_޸v ?itbܹ8y(c#q EEEUg7׋""33S樂իWq  Mxi+80۶mkSCgG DJ)Μ9ӂ$^zQ^^:vk=Nx<5kQZZ ǃH$2r()Q!^: �@JڿQCgz_I)YWݎb̙309΀fÓO> tDYA :� 6{^Ldzc&-- sܹsߏ .kRk8%l6<cxwTGnma�VDWa�$ٻ󡶶}ѼdvQPPrl6 NX˗ngN֐v-B8ߪ:8`MMM x@,3䨎h4={Z"+9uΜ9:�p8l٢Ŝnx[{.0off&*++m6 3g:::piB!s窎G4exwa(飣}At@cv BuJOOGMM UGўeee()) 022Ȓ\.٩: 8X�+)擓UQQџxRu0 X~=)2 Qh{1��Rʧ9֍�MOK)Uc:|>.\]RXff&ק|K��eRʽsLQVV: iD��>�`[D3~!h`eظq#"DG|SAG,�4cƟ�x5vB݊+Y nkH1 ݇F~Z)Rc<V^GՈhB%%%#��zt@/W=)**ºu됝: i,//Om,�c&BTc2k񗚈f TǀiTg � 455y9EKZu"ٳg�!DΡ�: h+WDqq(Dd1fҡc(�UB'Z:K�0k֬A^^(DdAaS-4]X�(wr�:äcHOOW,L` :afk�9&zj*nQf͚:RJ:.X�( 7IKKʕ+ńǣ~'ە@F4ݪsLdlΟGQ,竎�4Π � VTu񤥥ODq',�nc`{q9c۱b Q\hsh�``.AQ:6PTX�$!~_uh]'%7 �5~_D:`X;><яBix40X�$̞={\aŢET ɦbzQ IKKPuv,]Bp_ "J I)Y�@B466RJ/^DP^v1M�X�$DqqTq-$b;�� RRnuuucQR]�s @- s1 K.nWR@J^,*�ܭ\8(Ei8 z<:ۍ*"Q `q�:`GNSZMM nEDji�q$ crrrPR…D8Z[[P@477NuA?<�C'r``@- Ub'i~---am""�Zt�FY�~Mu�cca� Hu�,�⠩i9�X3DDSR:@2B =4M" T�fϞ 7\D!է�`c{-Hu�􇈴�R�P !�@s)U4@=�1&E[ގ… RCtaTcC~`NcAJp8cǎW^AooHhT x4e:@21 c%,9JJJ?cK�i:tG�gCN�<>L\z^ �'mR/^Duu5˕;�` X:Cvv6239RDpǏחT"4X�HkkMJZuN𯯯@ {o�bd```�ޅ#1yޱiK.Hr:{C3@D3dggvAtTzѣxW@QA^z5;�,�bqxOɠ±ctiSiب1 �0�|t"?RJ\p@{{;7J2t�R�Vr N[Vb1hsZ 9 +�`�Tf)J,9-\nT><X�ĀaTg5kDu۞chHm^�X�ĄFu^9-`};�B� R*�8@x>6-i U%d��1!PZ�dggsRϟǁi P�\Rʔd*/�B$Qۜ~�C`'@ZuX�о}�|*3?ґizKfht��`-[[[mè`FFFJ(m%� :]JN8p�Ν㴀f4�Iվ~勹 !'@q9|!D?8B7~R`fap^|Z@:wd҄j0:L"p!!D{^'.�~sZ@&cH B?)`*xT^Rh1Zʸ~`0{NM^<�9˥ _oo/^y?~ Fק:T}t6,�fHJ:t:*/O4!RJ;w/2TIzZQ@  S ;�+|؇B!9rUIZ.]Ra&�ni SPyy _OO~ɓF$+W ?_:H,�!)el)*Y �Vg⥗^V�+RUpSSrYf !�te) 7ÇfR)IVbˁ@࣪GSZ�v';�|2^~e>}jݪ#ă?ϫ2],�fNi@d0(N>_~9dnhh(bx> MccӤ(ٮv5�� �IDATr!֯_L=kP(cTAAꐖ48}7l٢tL U,:IOOW>&]UM 7 #$ʯnii,�fFot2̳YM$'ꫯZi{ۄNE;Y,�f (�D"R�:~###h'-- K,IJV `035ps":::K/ݧ6mBUUU* ~Lu")سgtnB|Da�3-Zeez"@JE٨Cvv(ډF@{{{x0l۶ a'M5!'�hc�*!̙y] ]]]IɔRvlu۶m:X�Lyi`3y&R^^:1RQRR:Lŋq…d^Py6{g_]~C+�}LFaa!/sKj�D塮ڍ7ގNk ظm6m��{<Yuedeeaݺuc� !PYY*hxw Yf_f֭[8w: ^xaV$}�;pk_gq8Ї>:ƸX�=b…|hOJN={CCCK7ng}vXu,�lB:L}V,�&VRR t=)%qԩdXRO444(]RGɵdD,,ybehhH&ى˗/c(--UGkBc8wΞ=k'>,h6OF3L�H2;;[g#{=1e&._B梬 hVxiSO=ڿAU�hnn^�vN:hráe1͆|b`` |ꩧڿ)O5�{ͷl ` |L_^up �|>,[MϟǩSz6ʠrMCCñD_8F) 6H_nܸh4:͐a|@ _fRg:2}>:�{-RΒhVB^^`h222dde%d%:::fśW?y퀔k�a48Oǣ]5�Df*++tRcyYYY(,,DooՎh D]T۬Y{1;�Dŋ?Lĉ'p9QD۷kYz~< f;?�\~iAD`Q[[uq0P[[vuR>ܜ#^-طo;r,:YrVۋ@|,\ÇϲW{=:�MMM thNK.Ŋ+8'XFF֬YQ&kYooE,U�2 �jUgիWUG !PVVM6Xurj*XW!ė~zxy~o4΢ 6hs8�(effb…VL gΜSJ6ߛ!ĥH$xΝqijhnnB+8OJwwD)pk׮௡*թHRBͶ/^uTeA�Tg �Dj`Æ ah˱tR+r L<X_y0)t Ou zb <<"l2+_1K_u0R��Śv0 TWWcÆ W�˗/׽ !<oݯ8 uvnLww& χףZ"?? -`XV@ e�{`t pu1’%KrJC1PPPŋCm!ܻo߾/iq-`p:KU~8ǚ04x"C TUUYj{Yba2H$e�_ś)/uZ[[m}}}/�xZuǃtv#-- . vvR;HhΏN'uogQƛo:F`…TL}Fl6[8VJ)@PfCvv^/222tnAӉ Xf9* SuW|bot- p8χ<se?>R(�TPPP|deeq'�nKB,_9u3y%Q0U\;ބٳQXX A(eYp8P]]9sp uCUG;'�[-*O())Aqq1$ ٳg.Ku˗qa1Nvv͛7OqN455=% "I0PPPrx^,\>_wZ$QPP ;wNu-, ā@k�8vv;̙ &{fseIJxJ7}᩾0!{.w`n=N8D4MQ,BnJӷ۩0hllgggx_+^ @yy9/_lqFdyxzXt)hM5e˖=K/SyQ;�g`H>Ȱt(l6̝;lӌ̞=RnſMEq-�nL</uuu<ޓXŵk022:~S,�V 755-RNyNB5!˱~zDxO'O1t:QWW:=~&._| R+^//^\Qh øu(^fϞٳgk@�Lxwbݺu,,??7nDuu5:ݞI&bAJ-/v .Dqq(D4M ,@aa(B\.֦:a�:oi`޽�|xu8Ya† 8eee=%閖IYhoH)-'n~~>V^ ׫: M1o<I0P[[:ݼhSƘM=#|/P]]ͣy,(-- 'm|>˪قI@KKK'0d̛7?!PYY7r'4ijjz7Ť�F 3/6 ˗/GII(D4EyyyX~=jjj'-y<1Fl>f\�<'Á+Wrc"q\Xd VZB+TVVjRVkkCسg G9N~xYa3g͛=!XZZJKKޮ: �? &r�`L#\.V\B|>ꐞ: єUUURJQ M<� }}9NDrcʕɲƺ�:Brcc7�'RJt_Ocsg***PUUv?%sjRx??m~Wg,NXl23~(pkumm-RRz5k\: Oa`ZS�B?kIKBDvOɨ\u�ܔ@ Gf(N,X"1hc 6pOJjfe~f];Bu "@nn.֭[Z8qJM�G _)&qb('' .T1ի0RJII;W^)�6b'vn7-[nIQJB7ndReU~?/Nz +bifnQbFJϣOu)((ŋIkBSm~6ab yyycL__<6aqf]9" ??NSu !I<4O>塪ꑧYB8ӧq;8͔Rk*ѥKK.%k?ƹs9L.bDŪ#�~ҒF/͟?dy}}}hkkp�䐛 ˅1K�1G�inAU|>T D|Rf]#�qM46 -RhZ;�DCWv�W9?y&Gu )E[['vBk)C;�hƙ<ǃs窎A4%pǏǡC4,�BVҖ;qNhmmu}:1m|,CJ .ԩS~S�De֬Y7ha!@oo/ !8W󡰰Pu IN<�%l \G�B&,#TWW@H^�Qr` �a9x94_J\#77Wu I)Յwy1}_"JnnPHY���ROp%,CtvxX�%,�;ꤔhZ̙ottGb@|4ؽ{w0NO?c@t)%qɸ5�D'==]up|�@Z3+##>!�ֆބ\�䓖|x�0 C?Ig@|^5�RJĹ@QQ)�t:a?Bhnn~ aTǠv?4 *@Ja5qURR:Q<y[�%'�nO߇xRA{qJ8)%:::pɘn3 "өLٳ}"A !PR�;]�p8^)RJH@)"ɓhoon�QrҮ ܠ.-vJN8q###QQ�]NQ;|> c݉bbppǎUGy$v���{cc㭇 �湵?Q<DQ>}Ν͜}��`֬Yn;�|b�/_F[[UG�DINe�0[DJ"7oqeQ�QrI7v RPw}gϞE4Ugڬ2UADSC0[b0\OӃ6ջl;�DIL�(U@vvdaPUGh4p8:)bŞ  Ge ˥2Y.\Sn뿪ATVVFu JRa5�*+,e&DZcק: ѤiL)ӕ?HDp)?Dd9:�Hdxl Cev�h/Y�:�P\�@2886BD4#:�p8d{&@B !xT]4g-|F25ltMKKϥKpqmKD0HDy4MC峦Y} _"U&�(/�?1MΜ9c-|F=K�nҭ69z(' �vep:ʮM-{L?�pX\ u]rmmmy(DD Ap=΋]p8'N]u"Sdi]�`lBݮl R ǏW^ T^�!n0MP022cǎҥK)322|#;S� �J~}}}8|0'744:�vAD?�D[aJ{|+nZ χa$588:p^^%@@\%sźu됓:J/<y(�###JG`nZ222f,ZCu"R]��8>;�,�RSYY6l؀ٳgBDCCCB3 �3Hۍe˖PDTGЫ�aTTT 6Tu"~F?/�B  <ip`Xj$tUB@y[qF̛7DQҸy&nܸ:Ɲ'��*^Ǔ~BTUUaÆ (((PhƮ\:mw0MSiIrMnDx<x'|r.$"Kӡ�0 {~k׮�ir�=Taa!6mڄj!T!"p8?wxl_V&h5"ifׯGnn8DDv%:NXg#222zj,Y.Ku"GR�^{ �o[ֈd1%%%((('ѡCuMD􀑑-BkZ��t8,Z8vX92 SH]]]Zz'<x k r!KӒu8}tl/kתAD3ѡ:�Dql @{<55YذaJJJTlj >@dm,rgy�>,�(n7,Y+WZ~Ka�Dޮ� xi�FO&48zzzTG$aTWW0G@CVMDxc~0 �p8GA7oD @uu56n܈|q�"@$Q�^' �غuk7 4NP<x<X˖-Ԫzv�IJ .16o<nwŒ[!$ �ٳgcƍx8Q\rCCCc��8R$1q&v-e۱`[999<"k:wc D?yF�ZD/##k֬ŋt:U�YONٺu!)�KHVRHii)6mڄr\ѣ={Vu;S�466`Kf'7Á:X�Y.]:Ƙk L2 !~<f&HL^Zi 3ghs0r߮]F=|49 UL � 7o4vI!DO[RLs"QBM ]Vٴ��:=r}}G}BOɓRr,YK,Ii�YАNwB&}|˜ErymRJJJqF̙3wDt'Ojs/ߚ>P�<8<<˗NLt… fdggz�鯷WC |q˖-|0 nSMF)ݑ5k`ѢEq}Z";#-FL'ŘpD1%!ʰi&TTTef@qsq �4�pke%jkk2-)�"}E":uJuI�1 e˖bi +ڵkdɒM @'O"T{|T^[g&fN<:Ѥ=-PVV6�zzzp1!T_3am۶WuʲKu Iq:Xhь7b@h4G}/�BH!7f)vN8h4:ѤeeeaXp!ǔ_5�Dyw044:=S>h4 2D1 :fh23g6m4i�DzҮ۷t^O;wvbg@9˃cMt8@!;vLuH)`}-iokRJۺͽMTX�!7@$EC)}# VG}}}x=-&MdX�'đ#Gt>G?7xd R)m.oOƍ7T i{ ��XIDATիW#++끟g@ɓ'<Foǎ39*> R4~3mN_"]uuuL � RiM&U�444}Aa 6!O9[Os5=zTu޺ukLd*0�ty\tIu ;[`HKKS(%7е|5 e,h@CCM� AzzD)ghhn!ėw5R0�h5F">| 122:D^ٶm7Rm۶�xܼyG�DD4-xpMQ&2j!D)2 &Xz*T "" BRLߔ }sWdB]pOV,͛8tփ? iii7sFn\q8}4Ο?:inpp}򙧟~:GN�x駇13ǏGw$"$uu<xP��)jhh~<{;deeH)2LH)[o "tww_Q\D"_כO�ؼysTh4M9r]]]&Ν;#G=g݌7ߔRz,ăaXx1UG!""ELıcѡ:ʤH)544|.טf6� K\u4=Ёl1? �΍ĭ?&&Ǎb,+***`FD"zzzoj.ivx_(f#a #zx)..ŋyQR̙38s挥vB۷a"elh4 'V]]]O<ֈ0z-2UofeeI.^x Xgn,[ 99Z+DD48z_? 6RXo \u""P(6\%1BH0aA1Mmmm8|08DD4 x嗭:W=1� Rxwn,]yyy$ܸqmmmv(3۷"N-oO=T!Pyyy|JHShgϞő#G043rFJy /_Rq= ,Tx<Xh|>(DDt8y򤕞Ȁ} 'U8~L3�I1|nQR˗qI  &OWeoa|1^p(vx8-@D`׮]éS۫:JH)аGuqxx<Fqq1&"˗/ٳI5W:E[lO222V�J!ҥKx"N'YŐi[os!R6.Gww_z%-&NҒF_(Q׌ TTT6[B(" ގ .$⾉|WJɆm6I- /PDD^7N'̙9sp $I)qtttʕ+0MSux7-[hHx; .R�kǓ>%%%(,,dWhJ O?݆J&�Ǜ@AA󑟟=f.Y .^.Ko3UR]vi9l[0#u$@ff& PPP,Ց*W^ťKRjпˋ[=P2a=b֬YANN<HDD3"mb0?7q&ԉpQ>R PVDBHZјSa$D+�E MD8B*\ >Ԃh@B: 3r;c3]9g4>>qMLL(˲б1o/9cK^IZ%yqFm۶M===ڼy8VREK1ȅzg̙3:wCGw+á,E IZf=յ^fvyK)Bhdǵ/eY_!Ms{7,[U|^沤R\A KCCC1I>��/fT*=:r~rTDRG �gfffv/$IJzB��rݓ߿%'@�Hw��0eIH  IǏsE?Y��(*CY4MoY��0ivSVv: �#M}RA)`N$:t �3q|wooCgiܾ0_\>t6t�@ۛq/юBw� .G% �ЖT*d-\$wI;�jյђ`F7�hiW.:zi IG}\'Bg�13{T*}?t`N$ٷ}[,�sI#Q=P,_&)�4<<5˲Y9t�@nNoB $I>G썡��r3BTz"t<h INp~IJ)t�@0yҥG.m[� >CR m|�XiIIN7m_�$I63S�Ё%=EW⋡U Z]Q}: �dE_-J &:�̩jw!Iw�Xf4M Utll)$~I �,/YaZMGk%IrKq��;-iP(BiUy;P(|JR[B�H.ٓfvX,>:L;�,ZvܹCW| �'w?eI5` FFF^%-iw<�^4ra`oi43}~�Z?|@kj3kfwI9t&�hs3{hR'tNCh9l;ݒ6�yp(zVү4}Q�HZھ};63%i%�״$S_%̞  Q�щ',5˲[ i|HQ,i$FI7Hݤ@Y�TҔ)wمfv'%MJ4h48OWFXuM 8. [{l[,^蘙u7M sHkVʹo4 +}n-KMlkB\ 3`f76|X{~ݧfvQW^E_%]XAzt|ݐt(.iZϲl* Jy�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������8^X����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/icon-pause.png�������������������������������������������������0000664�0000000�0000000�00000040222�14401326716�0022173�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������x��� pHYs����od���tEXtSoftware�www.inkscape.org<�� �IDATxwxTeC脎*`kA,`o`G.}wuײZ]{(*(UA@�!FLBgf~R|Q""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""DDM YY}W^,P��r?_� = ,!%%H�D$̬&5=C?~j:d-d`0%¡GB4�H@g`b;6ޭ_)/B|DL1l`Wb;]g50Zē�I3 tvF~t @DZ[Y3wBs9 �"wfp p<sN* !s4@Div251>4�HY-bKn?Bc$i� 38d|`0,`=4�HY5`Rt6x BX#Cj;#9GJxx 0;FOW[9H#v!1]�DO ZH0=wD�8"pHz( vOBzHth�b;-0 ܢK4�d43w$M._!1GH2-w/Daw$� Rx<_@Ce!!\�D2< "48w$G>[$cYC3{vRfv.@+�"izb !"�4T03ͬw$V�DҌ <n+1B"�4afh/0. H0̀Wü[$B;DNH3ⵅsd!ϼCjt@$yĖd if7xHh@$^}/ � b̬>xnN!� Bl+=`7b=BzH�af{_ "�`fj{DAAA-Xsb?feZUrXje+VVZ 'Xvmgg]l4h@iѼ9-Z4ylѲ%-[ ;;;b!Cd4�DEh"&L &2oi4�bi'vqGڵ݉ڳu6Y{`w.cB:3Ļ%V^_O?/2w\ i޼9{ՙ^;Ӻu+$g{H4�DPX^�NnIs27~ק~cGpӮN94B� 1fVx 8ܻ%-^̰3d0Rϧ6mZӽ[7N:G*B?� !nIu1| z-7<LB`=;qJϓ8è];82;D�0>-0y} z/_) ΢M9!@$̬10ӻ%̌O>g_~uF,WEj8sv/:wN? ELJy�w?&ϐaӿ4uwNJء}Gv'++;'N !{d: �" Otn37YΛ睓ZnEO㏣F9tBL@ĉzTUAA ?}_;'-mEK.|NyիWΉ;Bw{Gd2 �" o&pwKU9SMNmڴ+! 7bwHJ?="3gqW{b)iRd .;$i�I23;xڻ֮ͥT&;[b) +0;$h�I"3FJ}~.\"ԭ[K/;靪W L !$�$m^NWhP6?@@sy:Krw3|)R])18NOL �,�;;ءc $`)˥?fӁiҢ/6.Kۼ y7K.իsrf͚97pwD� dfՁ݀lU1L&5#U펻2twTB;dNG!@DYv%|II'sW3gOekSOIKx! #u/*/F?Pu}dgL=8\ N, �TfVDh^Jfn>;E`mJn!Hg�$ O4-͟^| ?w$Pݺu?{#Sc=K'x+ �0f 8؎k)oK/gɒ߽S$ B\} Sc2)4�H\0" %J)y=nv@=O<wߙ z0pwD:� qafuoodz{ f"Nvٗ':ux�8 0;$h�*1̀K u_gx3$vٹ#{i5ӀP@|i�J1 @>+((λS$Bڵ݉}f͚zDT5'SJAA7t owDo+/@ӦMSJ*Tt@Wuš[nNkNsQ>088W⠚wDU7{ͭhřޫlOSr9r*. 2CGv-Ry/Bܳ?۟Zjyd>cawH 4?K7˯Hފ b!Ii�?Y53clB+D)mx!]'Q3JsHu��3۞wΑ*I\wÍxH {y}@Huz̬:o"yO*fܹT.]"iFs)3sawH�l[`o\zz'qYf@Zhᝲ!nJ�2&ZK-^K. 6xl3;;"Ui�0fmfC*ƛo{gH#zgw@! bf7ѥ}iI83X_IjժlدkWu! H5�2 [$֮'2矽S$4k֔GƍS<$t ^/5OK<v4KwEnսuH5�Ҙe[λE/˯zgHpw=WA: 7I+Wq1Dz`B@7fİ4i�C?z �!3�A;_,[>wFqՀk#R4cf{�[$q&MygH{w򫯼3;̶H�҈�FM[$q;6 f]} j{G!??˖b V\+Xb+V o?.@y&Mhڴ M4e7N:^L<;Cg+^)E.0;C!Q�Ӏ <d9XjfY5g~f̜ɜ9؎5kmڰmغMڶmK|P_1+WCgI߶Hi5lGQÆ)E!M�̬7,t8g%7q'0nxM͚5}vnt޳3j%>tB!R}z-7Qdd� _&1/㑌7;D5kd;ҥ˾~ܱժo.[x1vkjeSF|ZBe�R nI\>l #F|ǣFTaM4a.rqQv*wūSHxu}E !e�R G3c_1` F5kx'Mݺu99{7:�jԨ6gί(m-YYY 6m;`&eJ O<w 2_ziӧ{$\ ޽g~T[_I { =O"GzGDbf[_[y3xWy~Et'AH%8gίH]/)!b�B'{GDafO][jƌ<c f m-{=;|+Tsp]wxg��zD`fՀw[b9<de{`-] =Fz/)v|6c7np\=(�xw#+-؂. 'λKRϕW\ƥ;B(�qf@ ڰa="}q֮ H&i֬)5kz!dRu#w'L'_$-Y;#>;bG;"4�D#οwKEX{ًӦy爈AoPw@;Laf;*bO[XtwD@?NBh5Z 3;qϽ._D`f z3�l{�1f5wGy͜5O>_|I_λQ��!?4n)Ao'NZ`!~wh� �r%pwĦtlN5t`DَwD W披a'zHhѢ9Y.hQ0�<Iw)TȂ [ �ppwDYa'|*?M"")hx �̬ wGY&LYga)">lwQ}0q"w!WN6i('d'3�Y\ҌGAA_;:;"*4�z7n<}=_; ��xD�'fvpwGI[9B]/"qͷz'�t �,رș;o2E$f̘Ɋ+3PH3‹.aɒ߽SD$ ?|wQ ̬pw6l%]iӼSD$M? 5 �;bcw՗Ͽ!"i."k<=]cc/ ({7F_3{f qоwF$̟?=;#nO�~,›:6}:|! XYYo\(++u\rss]'Z@�Z֭[UW_Gnnwd|fr ߖQ I̬wGqwsN>swV��Ly7S긿ysy'l�̶ywYd 7rwd杠�4�$D+ w o zDAdvJj;|82;CD2+�Q xG@nw3N hb:eh�w@_x)*7 j* 3yx�@f%pw… y'3DD''gwF}�o� LO<4W'"ʕ+2<� �;�bwV\坠; ]6�?C^^wr׹߂\�xSZ/YAz/"RP~5{7 �;�駟~D$r7'xx��fٻcҥ>pw__; MNݾ{]w"=7T:;�`Uw�tcfՈ�o1""%]}�!�4h1p ""[wV�~k֬aý3DDJU@w@;`#XfwHoėIj�?#Fx'z4l;# ̬ ҳaժU|X25k֔oM@|OX~wH5u?O:7�|wH6m❐A@q3㋱_z&lҖ[lᝐA@Nc% ""ԦMk �h�3k c?6mx'��⩝wx'lR+���O;y|ͷ ""eʢU3t� �:�̝;E{&l6lMڵ3Yo�gkύO<s""ұC}Ifvw' �sӦMܼHth~TmE3s1 �zVi<7/"R.Y(l�̪| 3=7/"IYYY @q-753;&Y4�G͌y{m^D\:oGz3r*0I �dE "I{;</~npC2w<͋K=]RQ'ߘ~! >zn|b_D-++N{?0Zn37 �t2͋ln{gTFp0{ēhߗyM:N7`>54�GύZZH|q�u�X+�D$¶zkfx=fcB@|�yn^DL -fd�@^^EDtG{'$ʩGfz/�@ҫP"va{D|efmC*J@|�YB,"R睐 c̬wHEh�3HTVc>;#Y1!=G|�uܼHϘᝐLu=U0%& >\9Bn;'Yj�/%!̬if|'zٞ)ƛosСs?x'%C513;,:{̬p2Du&vhw7N)S~~>Cca㎜tb=6M'5!% &Y 3�}{'iӸrŗ2rh3;$Z(A.#�KҢEs ۰a}<>oΉ'OOuViv !;8�cf OEw�͛k�Զh"|i9g91c>Nr4��f)u۪ Шgf|Xz{>8aO1#d�`f w3p*I[""�L#_y5w? L67Yv)'m;AD$!fϞ-AÏ>Ωjsfv|B25:'o "P~_rqVO:3; sN{'$Ÿ8SkYxwNe3-~�0݀OWscTCD$CCG‹/SPPT[�Mi1̬= Ilڴn!"TVqFg x̒?NN�&瓦ǒWgƍc{TSq5p_7V;o-{˾x'CpB$si3�YO`pw}KH~ {wJE=if'kc)0-O#�� �IDATf.0hQFk;CDʕk{)Zkfg�`f�zDE}t@DbW ʫ~٩t3PJ�fUxŏk(h}NoO?LN)SDo$�3k ԗl:Aڵ3DD"eѢE~f/ R^YB�`fߐWVڵ93DD"'77GwJyYѥ�`f#-QwGy'DOpǝwvLԋG~�0f(((&t4hP;CD$^yu.TB︋�`f̀K[RI͚59ü3DD"?/d)e C�̬089%%s ""7˯8 YfwJYv$$۸�PO�=ݦޫ3͛7 ' XvwJY/|mDn�(<1 ̻%UV#wI &N˯d)<efq=R@S:mV\Ɋ+<vզu$O>+|t.׋E§ 'oSPP>c&fbo1o,XK|_VVQ^]Br*߁HÏxB)=fVaNU_(AǑZH|f̜?LI1skV굊VDD<ZmUW\Rz=@�P<gꫯ7~B<HO<ُ۴y 3wᛪ�`f݁׼;i|g|<rey`f|ltwƪt jH+V#2t'L %""RsƠٺMjfB+.~"lذ>[g$""RNWK.AS'rOZ}BԻ̤�f0NaѢE>pĢEsDD$ΦMΝw;ec;;|sR�3k $s0s,xCφ sDD$|mԉNᝲfz·1L�PxD1c&?{ؾH㮻оڵN)nKDE182ۋEsͷr1;j/"a֭[ǕW_[{$5EpR�3;8ۊ\ûŠ7ߢ;IDD̜5m 7%|�0CD1t;xo+""煗^f ߐ6'v?nbJnN=BΛ#""RPPu7;ff1P�3H6?#13sPP/N 5 |Z6[o/Kz爈HĽ|ͷbf � Ik'”)?r ')""" vrxa̲WH{2SN?fyHa$x-N2FD�<였׍|~(^)"")'JOzR/�`fG/qXs/""")ߗG(|Q�3C%nElSO?_~"""iW^ٳ3t6x�NnD5} N>tO"""i$??;Me�0]Y&NxH<dhN(?<@ jTeܸ9VI""fy oSc|`8NB|לsQeaad�3k�k$_ .bڵ)"" x~EYU]ج?ME\kED$?<*5(퓕� wEe?fϞCs΋ҍDD$Ck{g9OTe~?!-^Ld)""^0H@kfOTj�0m󪔔�֭K/g)""/_{xg�v+]≠[SDDDxŗx@Mdz<dw�SMc)�Gʬ�\ x<˗_}#zF0fpVh�0-SK\O~~wȟ7xpOY^+�c\s,Z;EDD/,c�=�Y6pA\sɧ/zgwN�oTd\qZ槩SyH=zQDD/>9 6xg5�YpYB*!??o{)'' 'zgv/� lJz~L<;CDD\Fwlt ٳSO{g[D?] ̚G',yg3pQӁZi#G1j'"""gcv(~"`yމk) 6p?;:c/�̬#5e73W JaM�rYnO>;CDDf̜ի36=�Y "O{X`wH0ǟ3:No)[^^wHMxr86 !!,^;CDDfϞ㝰} ^x;ADD$.~d@S(e�06.,*gcԩ"""qZB+�%1T ""7sͣ;ֿ˗/g"""q…3Z@ e$=g# ~ED$,Z;.Np;z'˽J�Ln_-Z&Me˖y':�/53e+�`{3R4:�;3ADD$aV,_P:�tr_~ڵ""" P�Cȟ򿈈1 �ɧ """ .N!;oLI�̬ O2aD͋$\�v ĉx'$Ԇ �4t ÷ ""P5kmy�h@~~>3fLI5jx' �3gbݺu """ S�SNܼHRD`/ Ͽy _d;�0gίI:uj{'eS�ܼHR4j;/wL xn^DD$)�96�Kyiܸw"�|(QDD$"0�̃ �˖/ڴHRE�zjM$Uͽ@DD2Azp`>Dd�XfצEDDuV ̼6-""4mZNȧs�OEDDu+!|�A#mڹ_~R|�(H~GLM'#$\ǎ&$@zu6-""ufΈ�PWړ~^I;vN�RH �5kFЉ""v}7!E�PfM6hyʢk."�oy}hpw$IV[yn^DD$a<`�?iӦEDDCN�/�'mڸYDD$ڵkvn띱2V�V&va͋$G�y3�Df�hv'Us{ H܅8�o �:PDDnV[nQ��:v]DDDg�6`䵔lwNzqTn�Bk7@a {R6g=<7/""7stTv7n|ݴ$jwDDD씓{z'Fj�bLwۍ{g@OJD� ""R%>;!>�0> 1e:`DDD*eqaE/m)[-t938, u}O@!M9H6QDDBիVɒ[tQG""r8T5lQd@Y,i/iBʭy٩wHժU޽{ygYNP�0�@DD$rIlfE^!) 2�V;UnGPF MY&{wFqOo J;~C*QÆ'3N?-[xg40eS_T�0,1rrϓDDDTn].|J�B?�S x�Z)yi&E�o ˺֮\/HժUי{gI&{No!+5�*9Ww_\{QGm�,5�|Iԯ_^g!""';tO(¬~q@a�sC3DDD!=Jw-�7lʷO svd$""ԓ{]v(e}2}JYqЧw/^~5.]"ΦϜ;/;!Fʹӽ3"aٲe ܒ;87MUÕ)  Bgӵ˾) !]o*W_ϓN}v""z:+j;{+M�B*񖕕B\vە믻;ccV{yDrrO  M6<pceo{ <p11o)""沲xϰ{ylY\he7on 햛n Td�xWS׮]8픓3DD$uyF$IHaNU^BoJU`<[N9;EDD~]O坲!Uy�u_ͪxelqWN4y. !<^MCkTuu6um""&n[/;x<1`y<6/w,g:;CDDR\3OӸqc\BX�BX Ty!n;CDDRTyr-SJJah^Rљf@GV\ESNcr?YDD-[ʋ/кu+ !, VAƅx@Pq ԧ'٬Y3I)6; �eӁˉӦqXrwDX֭xg8(`|J��rqlvܑgN:)""Q;w^pQwPPxۮ}ZjyHtڅW^z&Mxl!J@ũ%߯+|ڵk{HD~)<I֭띲)qy "q'pn"",++ ?;<V7�"v( R7~D.b("5l=b.!Oz��!_ZiQ$@ý'; �`pX^3-^y\Ĕ)<wQDD;R\yB'zCq��l{ ;o999\s |<rw$@ {ٽwJE�>NrHgxf"dggqݵWSZ\}ދI?CW�C㑣N*S6W]q9o>ד#�m L%m\s0;EDD*a=;qo߻vmS*cgaQ27�I[~~><~|)F r s BviB&N-3{m۸q[3W)EVVܓFRb$BD�uρx[6G}Q""g=[h׮wJU#p^Rxix O\&qmw""sUWr!{BW@RYwbٔ;-7|K$ ""ܒ+.c9,x8 zo1afw�w&k{񖓓Oㅗ^fݺu9""io6mqܱT^;'^z$s�m&¢Ex h׮-ݻ.J9!��IKpBzx-֮]#"Ҳ8CutwN","wH_4if-]m+Vd7xW7wHJiѢ9'ЃSO>-[x$ kr3۞!??G⭷O?cQD$ԪU=O8.5@g!sm{5$2t ӽD$ժUv{#8ΎCcepLs75=;iٲe|:sF|˽DDI&_W<`<`LKdҸ8N^&PQ| c|ꀈ5jva}c龼_)v�� /isgy])?ȤIS?a_~5˖-)VZe}>t/Q݊8*;dS"1��I+M13~m˖-c]:֮eժU�֭#77_K^^_^g͚5lXmZ-eʲoRիXv-wի٠_?j!5l;!U";ޟ>ָQ#{/{/7nTISCkC#2� n)'BHw���bSTƯ#H ��fօ [DDDJ18!0;2"yC-"""%tJ՝?Dt��!:udN񎩊H(j�gxHF**+�EBY@sdvI? @q x"""a!pnawH��`fW6-""!,IXa")\(z�݋RDDʈ=bDK��/yHʚ\B;$YRB]%'ɈH:Z E C�gf{OzH . !!W� !|\,[T?'ԝ? @qfւ$""Rn󀻁gS鱽; ?nǀ{Cc"��3 \�4uXOwee}@3.!v@#I<`�70;&2j�(bf9""gBsc.#"f֌s/8爈H!*2z�(RxE@K)_ T3$"psU0}o68, �0݈ ut 灧C[҂M0Ć3N9""$A }{Ҋ 0sDD:`40x+7'}i�$3�@GA!ǹ'#h�38؉-|DD"m1wcB|s283j~@Wtid|`U/BZ%�̶"N@GkHs Bl?1/MJImݎ^5 VajZ7,). R>n,N X^¯Fw[vE?r ,!vibOM�̬.P+/ Ԉmj@8@8^}b"go3ޔb;荭(\兯 ΢yd!���IDATI/����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/icon-pause.svg�������������������������������������������������0000664�0000000�0000000�00000015150�14401326716�0022210�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.5.8.1/ui/opensnitch/res/icon-red.png���������������������������������������������������0000664�0000000�0000000�00000007422�14401326716�0021635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������x���bKGD������ pHYs�� �� ����tIME h��IDATx[r:Po+[eǖ%>fY@$0݀�������������������������������������������������������������������������������������������������������������������������������8�}<E9�05��a��|��@+(�� � QP��2�}�@��BE��((�G@�?� � QP��`�E��((�G@�?J� � QP���~�@��%��((�G @�?J� �~"����%��?(xI (�,ޗ��DxJ�z � $+�P޻w��B@�Xր�~Cz� hֆ��ApN�5c(�@ 7ĭG�m-YK �``cMYS`H~�n�p)'3� _0CzS� ` =d)�`X ~-O�/߬C��-�``bZ � +u�``bZ �)5�```bO[ �(J� �'9{:V�004QP�kZ@cP ?X;n�f>X �C%_=�7�-@�_`m@c@CYb@@{ N�7�6)9DA sf=0Q�,|D�ŎXᏁ f=? Ȇ9�/+ '%E~10a Ìo;�.>�_yN1U�J�p wYan7 M$߷ge(�6{ft�7�h p Oal2@> �ly78@l3� 6�0/�mf��/3W xlpxP�j6.ӿ}C  kqn�1��P�p@`_M ӿd�'��4M��P��\�}�8 ���@�_@���P���jN�(��0?wx<>|)�@�CzP���@CV<P� 1p�(�8� �S<P����Q�p�(�"@)(���^� �� CS�{kE�p��T?u'`�_���8a(��;n��3W��E�PP��eQ�p\(���J�vrP���XԀ"@`&?? d*?6�� g�@ (��NIgX^�@j׃E:`+�`ւX�?WkA\ �اu�.B�WsP��l (� L�{ל�=ln{ &陃=l{?-:sJx.,u7� �ӊ5yQ@)JwH�y%`B:FWz3S~cė� 7�^2 ,5ȂyT�JA]7j%;~{A& ml^݁W�wt@4J@<d,l.uSGE�,O !"N9 N4YGީ }Eõ:=`?gmY&8EٯX5@�ȵ- _nx0<@yzpP� cWg:e|01]*^ٽʰ�ew�{{ =g|ay'~YqQ?`8Cg pꇼ8KO�^؏� edʍ7�~ -:7=Y�0DqČI\Wd/`Z�@C `߂_+?0(7� ٫+\z A)yPo.� o72s}U?~ �p` GS? ޳wRpr�?0r(�/j?sip`pG\p�<x]O2N8�P�jqG3|�?~Pi9#�|U�TK��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�sQC")�7K�6� "�p �� N s&6l�E@]�N���(��WpyX$O`@6� @#�sO6@d=��\X� BYf�A o��@U�E�:Ǟ.�谁�f�p�*]��N+7�pj-�n�?�E�<.k�Uf2m�w0]6(@^ Oՙ<xp��@�N?-�݋� �@rXg€"�p �J6ȚBcy16� �q00H #ro]ij��}r7U�n�D�uo�]^(�?6�`Bg� ?[^4�?ϖ0�!kg=TE�@˫~q%��+ԇ�Z�{uKn�b&m"� rhY6s@6�LS�nsgb��7�Q��+�,ټDE�@*�?/36/W�y9y�@�0`,�s>oo`͂`�YYL ɛ˛�y2P%��X���hP�@�@�%��Z���hP�@ �@�E��\���hP�@�@�%��Z��pP�@�@�E��\���hP�?@��E�@{^(�_P��(�}@�@+���B_P� B�W���{��@������������������������������������������������������������������������������������������������������������������������������I7����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/icon-white.png�������������������������������������������������0000664�0000000�0000000�00000050430�14401326716�0022200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������x��� pHYs����od���tEXtSoftware�www.inkscape.org<�� �IDATxwxT睙Q JPW<pQQPH/boG" tHA2e}Q23ks_׹$!{ݳ 6@D]"rS"Pdn ŏ#D� l@�q��7qb<@t 9"�@D'"@iPF&e�0�.�8 � �ۜHc(K8�eH>p�|U9-3"y ``I#�"R#"Ec< 1�wS�X `Xc9D!�wP$y ?`Xaq*7Q�@D>(R4�P@U�v$+` eru�y]hj 0� 0+ e9D�yEH9�-y',0Ƹ(xp� ,H7PWN�k|Øc(q� LKt9\=!h�߄cc(0q� #@W}8 1&F; �"Jt0@IJUƻ"٣C�JDr; _(�X�0chǐq� HN:^�iP�?̘U!dM�k=\*k� 3fvY �"K�#�n! 7fvY�$v GÀƘ1�Q.S� [Hy�#GG tq� !"bw] 0' ptsv �$R�c<B t0ov�t4FO{lv p�1H}0�ŵ[(`lCc6joq(H$)ɟ2~s 1;\ 2N 0I۵[(mvyW�8"#�ɟՀ"o )&%Y�r(%`:'?8}1 C(x (%j5< `Sd�/ >$ @"p EB|BY(H0 [(� Í٩B($� PZX�-ŒYB�$*b$veW�KY&n2spY;wnHHHc/^9s^k`w\Qȕ+ :�@dqI"] bǃ}?8x<h;wΝ11ȓ;7(… [oEn-oA%P]ȟ?׏hqjP8�YإjwhILL֭?i&lڴ''q]wlٲ{Pȝ;vy1&A;ȂDF rUcbbŊX _wP<|A<x`۵mh`B�@d1"v޽/eb9^/_>P3x衇p8ec9B�@d!"~PGעcb0|̛;?(Oiɗ7/j֪f͚|91&V;".gxFWN'-ZSHhYB h޼)ԭwvi8B�H䥓[|Ht4M3fԩ93gN4luh%kKC(�"eNn-mر_a_xs,fFxrO_44$i�"U"w[iدnz픀u=/駫R=0p"TT؝d4n+Wa磰eV픠Q]jgԨQ=ÌOQ v7lٲ~֯wrw]Pzp<RsvG(�@)2[u|'XGw߽ի'*W]4v3K;$Tq� 3H;kwdG||<b v\.vN1g7{bEjdG�0fvH(�@GNG,ݒU˗aC#::Z;%EDD/;"<<0H p( sB%p� xz�["&(Nk*U #<U:.'� "@$ zл2Vpxp <DcŬ�*fi&Fi'Q2c 4m޽{"W\9YY1kG�D$%+c27@^�?ϱ8%i�g8e1+&"9]L.'Ot2ʠ"EnСCcjd8^#Tp� K"nI�K?!p�e?HJiGeҥ8}[c ڴm=D9s2/˜=! PR<n:� PZ)Tn|Gw/-W}nv<jI v�HMH;> L9)8qݻ`C~)I1ݵ#�D6 ;ףG7 վ};v];%#� f�Df'�#i1xPn|A|Gȟ?7h:�*c8� qzt@?->ķߌN!?)^8z,J*rC|Ø6�yǣ盽dR|ybQ)7d&cfhw#�I"�x�M}G(6.^| [ڢBJF)7rҥ!Ʀ@)R)� <[޿'O}<Dt{;̝r#G+�%.z pv e\LQ<y<xP;,fahڬvJzaL8�P8Ej ʜÇ}9BcѲe t�U1I!�(C";EV<cǎCyT3gi xC;"p/rx�?/Shۦ߯BgGfgSrT4vH0 �JDr9EF:hRl\^x#O!nok֬NIKN;0R;"X:]%|f-u h9_)`rʅS&;NIHci:�e"R)2G<6A>oOYrytz%?~\;%5�&�1I"]6�{(>3B `ǎ/wFBBvJjv'o:F ŋ`4\=}3g˜/jgPعc'=@;#-#Ee$v yǖ-[1p "s3#mN@k(!HD򹀯4n!CㆍBAncP6<�c,yb":?Px<ݳ7On=ظ8ks/iG*�!$I|Lo;+XR;ѣп_D�BD%L1X�a=]7oƨQ3(LvƵnqhG" %3Tn!O@)""##1ol-VL;JGvH @s<lր'O~}߆h\;yr�A% R�7ko[OA!hÆ :Z3g!��A+>>uG#)y(\vʕjD;"Pp q|`0xjcǎɟT]pvƵ$<I v ֡P^$&&jawθL*9S2+�A@D"]lCСx':d\.ve6vC @K9bgΜ?ñcp6.qqC\\=yɓϏ `(t(VJ,kjɒx.DW߿Zi"1jX�' .?ƾ>>9sǺ(U.PʗmcvnؿߎI _"W.k<PTWr3F8�(psB'sa?qFwl߾n['22*UWEr>])1'{m>Qvr'tn a<au�8\t� [|X"ڵK#aaa{'@2IIIL-kySdd7*��wM8�1n[:hJI…صo,+ZרZjr0&&Lbko뫝0cx[`:8�{]/^9b?`]9^WH<S4~YQL}n||jT'O;"""xeVN8b|>@8�$N4:iӦcV|ָOTP͛7EyÏ?~1eD׾C{[;�`FcfiwX�#}h͚8a"V\ǣ"_޼hպڴm 1Q8quDYe˗")�0#̘&Vō@He N߈V-;_W�b_?OWСq>?O%>>?L&�nwnnɎݻw_bE)F6«… r5F)7TK-Axxv Pa|+�`an[i햬ڷo>S=?ܲѼy3,XvQ|4l@;Ø;9E>C#+دCRRvY0s �8~ikнޘ\"'ףAF= B;u%)B �`A"RB/;2+Y 0?hfBZňH X ^rSNkEɓ+W@D3vpb\rK^ɟr9,ev�%"E#8EK#A7"JY��n �`"eq& .BÆeV"[eo�M(p#c}DD0jht{;Ν;CDrl2 vp��tӉ޽`磵S(,^X;�*MV@�0ow,^xE̙3W;ڵYaиǴ#'P�PN#=GeVذav $]v/\xR�HϾ}Тy+gv kj'�V@@N펴8p�?ON!�z% " .�)i u+3�(a� Gi9r0ڴjWe�7pvUp�PG#511GѾs8zv ͛6k'@8�\D$`\pܙOPA*8�x�ŵ;x^￵S(H:tgΜQm0\HNHe˖kgQ+<?iGX�?r�UZ3~qߎ m6�h`p�Kƍ`Р!D"٫�PL 8�ɥwߢqgϡW^p:)D"G;���~!"6c DB˥ ��p� we;ӌXg " 1. ;r���vÕ>GhgQ:xvBQ�+�c"�<ݑv=q" Qǎ4jX4p�1;vÕ&M-؍BV�p�)%@;<?AD!Q7<�>B>|C?^;B\%(�+�C&Kؼy3fϞAD<��a"C;B���3b;"";��q�WwwƏ3uDD��' R;�Htcد3.KLLDbbjCE(�sE#DDW9{ W�8� h�$o30@jp�&� q "xģEJ!";+ "T[+�8�xxF�VX={hgP{Ŏȟ?vY�m��5�?i'Iy4o 6$oB�" PM#66B;v0ctTvYDDD��orȫ1{l$%%iga_q0y$ 2S"+�K�� ��9sv�6 ͚7â_]_1 Ph�ضmv[;�`%&o<x__La H1 8U,#3gN pw]?L!&** Նjp�gy4DK,L ʍ�/ ,\�M5!` �pA;@y[nñcǵ3.�"_|:t0~q*W*vM+��ܫݰtRdM~+ǔ)cAȗW h'\4ƨD+Vj']fٲoмE3A�B~�"ݚ gΜwk&]%3iɟ??/ "EnU=ΫX�/HzOoGom˼yeA@(^p� {�i']%,,+�WlhѲ9~^�M6e�VDq�WPVaӦM DW۽;�ȟ?? ?J՟MYPBy�|���^!+�Ν}56*Tԩ1p�^  @ܹ38�W`_[x,K�lhٲ,M@�2 �."[Z�xG)̓o߱CD_ _*P�| � X rv&�$"�nlػ{RK�I,{#?~n\r ):Ez_zK�M@q�KIp� ;tl64hP?/ZvT @H�#]D7sfd(y|ǃh&᣻�2*_޼x__L<*!/gΜ(YvFj"oj�l2VǏ@bbfQ7T OG< y{W7H�Չ"NK�nj}awdnGV-h"^Pk'dC6`s~�dTku$:ZDi۬ޕʖ KzvBF0SDԟ\+�Ou� *�RS*1c:{ ҄T̴hJSI_�M| 3'JC.hۮ < zw/rȡ�,Iy];8�dT =sd+\=L8˔ ZժUN>I$"1 TΞ<<Q2�1s x;"#ke< f.`ܢ �OIc' CN/bނ^?9AwXqG�{.$! �)P.h"5se0<)f.! Dy�HL&@dM<�x'1oltJjzF;r SHg퐬�}�LV �|Z׻`xǵsNɒ%QB _`SS ip U"". \A+qsBXzvW/Q_}=�� �IDAT˗C"&vR@ߠv?tuEE1&`b1k?p(TΛ[F!BcR�!Q@ ]D_QQQݻ'fΚ-R%J\Q!68�d+�QQQ D!;I(X0 UtL zظ8z{p�4ܹsi'4c 5j e|5fϞ5/Ƅ p X!"^LpHMȨ$Ln ɃiSQbEK9}4>3T{i 2 ;wN;/D�v�.;YwWĴS1p`ͣ~R1hԨ15jI P,-�$\"h�x@>kRv]feXp6l�cvܱÆ =^={c?|�o {8�\AD\"-" ^.�,}-t D ``99sM붨].bccJ.`X @D N�:#chlSTq# t:񿯿1c-M-X�ci�!>�H!�rjdQ3RzĠoS,/,, GNQ%/gů1&^;$$�@G'Ojg\�QfϞwGӧOkXnGڵЭ[W-Pi�c!�x ApRߦ@DԠA},X8?7 ހܹPV]8w+Q |ؒD^p{ WɛN"ʂ|yba8q<J*cyIII5jbv�m"5~�HyfʷW(_^;U1sOhӶ5W2̙32do͛7kdH}� "7Dg�X]r^R�@q}|yjgZjGŋ9Ya`R*8/Iy- k.}"""3(\.֮-w6ǃ&^XzvNV6)1^ �c`]|pRJD ͛Wchht|L1@)0CDrA1�\ ?=LrrcШQcl߾];'q4iݻwkdn 0+ /+^"ʤ;vYϐ46i&id�/DxvSHDSnD/Ⓩ?~+[ U& (플:0f+�"T��D`ժըW>&LȓBh GS2*�(RlD6t'x\;piz^ 119!eϞ=h޴96mڤQa>H@ �I"l�[9�YiQf̙;O;'d:u?YfkdN�1�HKd 9vG;PLi "d]hժ ظ8휐>O?N"l-?�HH[AڵBΈhܸ)v]/`ၰ-o|}@H@5@Qn%Nh_Qn}?o'}^={.־xa�Ne[IQ""AoƋ;ᕗ;c9skuEbbvJDDnZr�H`Bh;_o[vQHLL矍B:jU@EҖ/[^b=TRqx/j I˥-VڰDAoݺh԰1F'JǪUѽ[K_9|MK �N C }qS "_9qþ}s .]}ނNIxѯ.[DL״[=V T<cMCX0DFFjgxƄ9s"W0m픴zSN[DN[VvqAۿGc"Q;zN q9?w9�r"<"Q9~ݳ.K_B\͚O?M6a!9ʤ_y ݺD7P9˜B+�:TOؾm;lيm۶a~8 )pӬ@o`̗cQx <۸vJjH�َS�\G�l𧸳g߱n:a#k72ǃ?|]BPd >dS0cfE� �ݴ";v`Xb%v>Q�صs ]BD׺b(VvNj �I"� 8y<lc_˗رIDAϟ矍¤IsX'9s/SNFܹsKØ9Y}�I"ϙ}5{ϝG} ~wʼn'SBQ?6#"1YV:�D 5,p7n,YO~e1dPY˩Ė.] &}{˽_h۬|D | ෨x"&O'}=T||+|o{w]Q;*,"S1\ �"R rQ2ŋ4{;9sF;he1DGGkPp: ̜P tyf?[L_W\.Ə<]}1ODxxk<S9|o믝q]D2 �7 �>֮ 6;#FO0jhԭ[+VΡ�hb̙;O;*g|z I�r$:C �|>9|X;Ĉa#㦛 j\&�~l I5 mO6ɟ(E9W^yܙ'X 2T;ZU"2 >�D$$?/^W<vmc|QJLL_NXlvEcѢz33u;}ھ2s,4j7BDYbJԫ�6 9 X+J}^�\"J- … ճ7y#x.x8tvSNFig\؁`�"RTg<ۨ ̙BDY1_E:dR A?L{θL" 6�`EcŊhղ <BDYnz4l,>Sstn 6B;JQNiF>k3vx|EDc˝q9"ʤuthO;֭ҥ˴3.3s8 �"R�üZrЧw_|gx<9D N_}5Ԯŋ!ʻө27 �Nc�yZW;ٳd"Ra4j}13";t~1S;#'?%0]�"5@쾎/:rc�s v={s5vX+tt@D|ӧOmvؼyv e Pf̚5[;(CbbbOY(sx5��ܕSN},uoݺ_! .he1_Yf@~;�ӧѡ=ODĉxǛ|w?ˬvz �`�y' �. Z5`9D7rYAhZ Qnz8N޵vܥBD7xQ\qA^F;#E~#K %ǃoš5kS(<>mؽ{vM8I;�`i^ONG�V|],\H;v1~z6f͚ N"իXbyH)exK7s,L0Q;ҰqxQ3b$Ο?CSO�sP+E^yf 0H;Rqim۴C73gBB%T�20 !^uqt}u$%%iD3:`9\s9,[\;�g'<d߼۔=n:<7B5v-Z-[eV,s8p\D~rv� 37ex�=ߓ=_~1' *+VĔѿ?sPx'VZX �24�HI� ߈/AE~oaTn�@XXxQp:Xhv�<v/dh�p]�Xѳg/nlhպ-YfeQ6̙;O;�v[~.2wѣǴ3bŊ0e$O" ؄SN`QN )p�'�\\~=~vQܟ�ǃk-CUn8�Z2'>>o՟Q@h٪e�@<V\�<r $�r>-ʄGhgeZ*1p@P>ßK�Dc͚5x<7}Iy hg﵏>oA)ūmZe/+�DAԩرc*V`"sH7r{HLL ݎ-[dx?5@\VRDq W7PM~I~[%KjgeHʕ0p`TP![%�ז- 3�oE70z%Q$JWr-r�6 l-7Z.ҶbJlvQl6ׯ}z!^{0�DAߓ'qq.\X3;S  =|( 4yk?5v/m۶� w&4oVƶm۴3?~ 6SN�mU/m"  ".?Ϻk']fi&ޣ;;�`s!{P¸�"t{bߴ3.+_<U*x@|>�p+R�`qߍ疿d yk]^ڻ3#w5՗�DĸWuy,?_;BfCMУG7˗w8~L"ӧO… Rk k:IoѢňOΠ�Qp:r.̈́�iYs(D˗=ztCMTp�`uq@R�iD`ƍbl67i7讲ܟHbccUo]p5 ԰%x<Bʕ/=US'��.�j9\R;BDyѽGw4mܟnD,7��xR)䲋/߹OeAѳWO,X@;'M\ Ng(q֬Y$ beʔAP})7�V�"("6v .DFF^Efi+�Dŋ ��D;KDDj* R՞C"ES2;'ө���p�7ݹc'?\AXxSooGX`TQ$Yc�H^voN #GyR'DDؒe (8Yn)Jj�lڴI܏Awhd/'+\0)�(""?5(\z酺u-W#o<$1) |&J0�r1fȑGpi Pv-[wEܹslX;/cA>``� �fl߱CW(ARS2,G b%<w+M<ѥKjݒQ ˡ_&@յ]viDlKDa+�)+�P�ۯyx �˔ƠP)DDb�� �~vuxHZgt>`%"JK�rj;zDԨQ}"EnN!"QQ .c@A<<YPbп—(=yN$/�Bytr@oKD| @�zOj,l6l*VBDS�� �<uJ,22_QHȧ?��Ꞅg4O1 <ŊN!"y�G�5oxIC<yƛ=Ьy3""ӾIh�p��~zV_#";͆"E6U] `AQv …oVȖr �@4O~Pr̗ܿB^ŵ&@Փ?`"9"ŋi' b `ɓh׶=$&&j۴q=�@k<|}gmM6iPv.c�l<�̩,"֭bA8vcPBy<�DFFj0 kEsDȧ Ў_ȝ'Iщ]I>[ �7Id .BZu1mDSE;.+ � <<YDٳׯ?ڷ{ QPzG})?Q�n*�6l@z '"!DQp(Znvw��;b$8Ę/ǢN9DDk'@mW@^bŊ9#GKk]q1",{'�`ݕ?c$) GpaS�%]6F өCD)y#<�Iʳ�*\vE0xQlvQUo z{6,K�09dʕ<<={mvӻ/N:CDtCuN�uW4� @re5OFD0klԩU~GF"4,X�>v�:�Xaw$ <qq0`f4@Ǐkgq ׷\ �"?h+<S8qf0ݎ֭[]+W.غu6iAD0o.SZ;�c/Mb% Pn&LDgjc99^v(S '�ړ?8k<Ѓ =y{ wsVݣi )~MSהTpi͚W>ls�;wnԪ]K;#ł~�`[Wxq-RD;HRRFuNJ+s2GDqș3v�u\P�cb�^ 8r0^2^ ǎΖ./"֭͆Zjgn+~O[cD R-FZuݸHD{'QDq �f{�Hõ3(]x#Gg5Ms�mh'+W�\r'Πu8}ښ[ s� <+ʃ�`1&ͭ/�aoKR:j[cd "3~Bgjc)[rZK/uN�1 Zf'7 qgbhܸ)6oެs�@rmF)Nځ}� RXxx8v];w%_n�D;fc1&MP*�?'cj2�)x< ԮYSLU} w$ ŊG3Rߍ> ˜ wMc"b0x4m['�kuNJcvR[X胘L CL!lh޼%S{Y;n θ�_e�oZXDfFZu0u~,V@D׭V:_33 �`,p;`"EPo$ àAѬi lٲ%�"TW`I^7�c;th@tm۶E bc}7+x\H��IDAT�cv`NIteS_Tx>&`xFML0'p'@"kkب!*Uq�S"9яOu�a��Х˫ Di;{Ç@f-u6O$(tM;J"{T�cL~NS\ ۶m5m>zm!'W�,GnP!+7f{f>!-xVBjתi?L Ț_-[θ�#39i�`6U*[KbAhҤ,�,'22ÇҖ�,13IicDkotGddvQؾ-[�O>(YvƵ2?����X[ЩӋDxiZ>mz. p'@"k_.hquaOLw�0D";@_@2e32-66enDdJ3RVV?1#1dŽ-,, G ҖDrY`7,K�Dѣ?GTTvʵ<|`GVm*ݍvigeԩ?3Ӽ,l6{)r{>y�1\׺v];([Μ9-Zao �=駫ig\�7LPvMBm[ڂMc!W] � ;jg24�c�>鮻B=3`ʔy;)z1t팴|ddx{=)]^YȲ*Wsga>KER*VYM�(m9N�gth {N!"�WDqL27TP;%U<ØqxL �"R�#GФIs9sF;T"b(ZvJZV9'1fƘ�zTX1|{h"0iOIeoL��H%0EokgQ[0~8+ZT;%=dkez�0 }oFxKꫝ3(@-V bŋkI`_7K3t�85nZ;,tҘ2eJ,.1ڻ ;9;XlH"DŢ f$*` "ao` /m,;bhL Q؈ĉ@!VFB='F+)3>=ӨnQι\DlNgkٳOCԤ;l*{{W(;bc{D�R*T'kf*W\~%?HȜ9-odQ?1.��DDT(XrbN9#H9͒B'zmN9{άEzV\Wh(LJ7i?5wa f1:ctRJ{V`-g EJŋߝ;$gov>prG/wgvx+Z`Ѣ+k,IjGy$})@]3�CS\ "IB…ZmvF4dW5�)j5f<\rɥjSne I[o#eaFDq͇Gī x4),{`zH(:<íX7V*P�RTqZ[/r[o"|^RjՌaszN<ù슁yň.Wi�RTzzYIZ{Q$I#4\uUw);tcg͍>i�JJ'XV^gѢOӟ4wI0p\u̘1=wxkK�PIR[5$Əv>^ JR8EY�J;`ZQ/_y뮻ի;$iBO>6.\��~�yzH)(Ko;$iW\q9ށ@b!��4x.9l-[կo.\n I0kL.bN;J$qGuo��S 窗W֭;LwTq$mE',\xQ;ײ3bQР QG98_=|/?@86JssaSrǩuYţ! �@Ji h'2K/o}lݺ5wIjY{N?38 ޟ;N,/‚dH�oW<o=mذ=}-fAIBGi N㎥X,TOOT-6�(tXJ`F*W摇?pV@AMfs7޹4“ۊm.�?\0.뭷+䩕Oj*zz6$IL<fȜ9sïhgi��*)K>j^x+bOvܑ$:̘9}脱VHN "*H�_Ek?\gY3ke#IҨttt0̚93gpaS+/D�y_ o3kxvͳKlG-nW3GlRifRĉL9Yf2}4cܑAuF,d87��L-r@l" @ޝ[ߝ 7nr\7R}-[oUSS*u0;Mk:;;?~oL4^_';p5JJ  &v>RDK=Ai�JJ'o1pL_<$U6y=wj| p$U[C� OXເwHM_Kw2M��lM Y$IH饈sga:d,$vv( �@Dl(I_ʝE4f!wZiK�TN鲀% $IYY]/RkM=R_"Ij{E8? ~pl, .(EZf`HD/ MZxڽC �lґposg$U+ri^)LMp_,FǏ->JJg'+H^ �_C4��)="IjZ?.Arj`H%�'8 wIRyUň ڲ�H)aag̝G"uD,ڶvJ[N(I2pg>N[J7�CRX%i *'�nx1wf5 a9iNȝETS倥ˈx%wf7!)|8:wIҨlQ[#b}0b6��)cr$/p[D;L )\1;$iNNX[siU6�Rڧ%r$,nX;L;؁R $8tf$Ic,턥.-aH)g;$-o\yڕ &U4$Vx"h,xBJi~00ܙ$E<`E56�5R*V/ݲ0�<# �IJGt %W9`] xa�9`m)"zre@)~SQ`!E`m/4$áß�l x6뀞=[ 耷NXk,&-*4B߯_1xnbp;zk{Gt;@iW.03]rWg[Ԗ4oap]zw 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-�tAS����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/icon-white.svg�������������������������������������������������0000664�0000000�0000000�00000013443�14401326716�0022216�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.5.8.1/ui/opensnitch/res/icon.png�������������������������������������������������������0000664�0000000�0000000�00000017144�14401326716�0021067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������x���tEXtSoftware�Adobe ImageReadyqe<��IDATxݽvFv�-}Q2;Ѷ`''l挣9CRhl]KpEQVSs,4ƽu)������������������������������������������������������������������������������������������������������������������������������l �(ުUu]r> �آ1O;Ƴ1qL 'H��s{=s6&׉� �1 |1 x7&gn$�@+=h$?1!ȟKZѪ_\I�(6GA}q<n'�"w^wAjIyv>=�SWD0'�ر\퇭 ,ܢ�l;~)sdx� t,@"�/� � C2J"p`!7;Y� 1v|�P9]mH�T+*k ~&�UWӞNT�vW $ׯ�B_K�4 m+ �ʵS]vTS�[$0]iH� �%͐�k)"Zr*Itnt�(O.Þ&˳! rՖNEY,&'1�e<{`=/cE2 �;?s]wGNZ7N�r yrlu]'^۰/MV �lh>/7G4]Սx4sO`jy#~ ��{ ;vm%�sA_2 �ksIܚ_W8K� yx$�uU$ �Z:vc]OeZi<�*wr1]U!wKv.R ~T=ZWADTP)j%?Oʵ+K%_N뀩~Oo&6yMrl>K9J~׻#_og@M9F�~<1BՀ'僶@~YI*s O*q(-=i>JeN tAc=!սzI`3QbzAݽ;>NMɟ viCZ_|Sn~JuJEw٠lq ݗ%Pי*ۏIy)<XJތ~NKIMJ"X R}( �*una0t׻T k޸F{IU0Ã-E5e[>0L0!'Ӓt.N| &Spڮ}-IIE=!BkxjGnkd|<ׯ0݉=~kϋ{ .<݁^LQWǤ?ؤ �mnqD@a;͸?ev&"P[d͸?st*T SP}ߜ}j40Y)N_[3,k{(Ü wzkJ!PeAl 7F{Lv"V#ګUh*[gK /*ᯜUx~|ޖx"|]b:MVG Wh9rl>c2z2'>"j߹,ck.yA'_Z哤IێɀS8EaU1QĨέOp]2 {Uj `,5_20?�vDf]]o2]~@s8Z.;7iz<6<*3S*(Rėm%Sg k : 4Y �Exi\h"Y �Vꤿc 8UΥެ^3cBx%2Њ+m<eS07g[RQ)\d3o]KADsW+05(9g[`BRSYYI'd? rpD{ "rj" 1{Ɇ@'a9+/Y|Ψ?[\"`Ϙu.sx0`pqß f@}=J�1Ex 47Z  `:<d?0CÞiLs.`&K@ЊÀX�ZgO20h ^�*�[XMfᅭ�kv<tYu�vi?Śi3\ �s$�z%i}�܃;uXȸ"yC$�T@}gUxPPHcz�H�&e�H�&`"z�H�&:q'[׀FDyM.~<I^d�] %�KR @ �+?&O&�*t`"V�n7*�*�4񰛪 0, �fR;p)(0)6,/9�a}^֥@]B3,8 T+̫XK l~pmxPm׆4S�j_$[R'u;� }c8sa $`"esh$hX- oCS @=7 �-1, m ] SU�*1{ZM(KaӥMEy|p@A"shRCgRV2Ԝb~`[b2;z�\fj�t N>oTN,!jg溕#[(YC a> qV] 0P>Y~A=1Ylnn�db�(a!V �l'K7�d).>9{c{cK~H&"&v Ց�T3$`�׺dX`Yz8*�POn|LT�LF�'�]88/e ߺa}Q[GQI Vz8s, gKoOJ11s~j_B6hS((0odX9VS]*�C �f[`7/�lvN 'ϗswj%D}A??l~ִa1Yj~MnB!h6=-8Nn$ pcJ�4fXsI�n§# % `ؓ. u*4E.oL\i*֖H5 -r&!i<}̱I%�M3,И96�񇵨�߳no<]2xY*[ti*J�=;M3,0Ek>y' @7f-e*�P_@r",;{o0]-j7iϯuMV>J6X6I[!<wۢN"`״Co7Հ;G.:$�o A8/fC EӔ P*\9Q.bv>[NŤ~Qd'.-4||OJ.tM4~J~g/ *a`09<h O & .}kG�ONI�܏$ P@o_=H|7?P(4~K0_\ MI@ ~)1^o ȕL|!t6~?/x.y|=|/Nf�jQ=߇s 1v6&�*j'Ǭ@ !H1a~7VTt2k_w\&�bc`"Ը&�<E,i@:O�ϲA)_bYAG��5,;@m:QxX3O}PK0Hif_R' zކ*�~v)P@xX@ r?S6p,&�CRg:]7[G)�<2xK\'wʛ�(AM$0b䡒 '�OMR_N�(R<KR?[�~"ScTFToUgn~POOILCz�~Ju(l[e?ԓ� IrE ?@ZD =o+CY r?� D:D@E&j.Yl�(SEl5@GH�iEc޿}`Ә(E{ B'�OL7R??w_Qq�UR#S[~ ud/f%ܞ1 <@<yAGf?ԤO$XxνD봁:ϊIvxJe G�O^%cQ3@=";*`wF#P/K�ntM6- OH^c~@]"̫| t¾_K ?\=pYgT2?v΀,J�#Cj?\g?-v{3{WD*&!9MAW,%�zDԧvs!k 'M2O,]2܇ ߈]dԧc�DA/�ap{ ̬Knގ03?68Ca@掷Ƿ77?�-^  Sr?0f ¬`Bê9vS 3r?3e ?r?0(eeXviHqf[�~.P?gCfr? /o< vre9+*s9آ!hw6IQQ�?_%R61zVS.+�>O�ky@rWt)x!  �r?!Og>3䀯 R*Q IϵC�nr?P߽ p?倯0A;�Ɇs^C�̭O@{B rWw3r?ж~ �S@Bt~ 0D؂L]r?N @ ?/> p � w>�\0pi�SXeGu$�L}j:@d^ �sxC2,�G �sZ&@{^8( iA:`K�^g�m|DRa{[ �57�;a6ǙwOHjX�(]bfC �z�D(7 �Sua@ɰ�Pɍ B5�Jp8?<s{` i{y (yIV^�YrrW,}�"Sxw? # [֥} ,}}pva\ ϑ9H�ؕ . �#J {ŽuaL#c{R)eX�(qgǧa) [R°�P.3)93X �zb؁$U( Ka@u<!db@7FY`S {X�,Fٶp߽Ds �e?Ò3K �ehtҿ�ؔ@τH^/1ja ~/taT .6EG`I U�x] �qEeT(.΃b'I�ѥ �1m;+�m �D,o=N~L,km:,�MG+R~ Ruia z= a @ZRǡ{=gX�a?d"܋T Ǐ �1 & �,~rC/n(z�‰&{)fcU�ү,S'Iq`Iē` �#uqГp@9ʹ.j|��6W@<AQ�^&?Zfs]s. �?Ŀ%#�(_ cDʖ"0+�%0I��0'I��eYCo 8y�� g?$I�m/]~_룟.1r,hQ/a_ 㰐j�r&BNm;{y(Fu쮠,+J%aa'@ sk1$�oJx]C~?�EƜ{MP?("EPL#msTP �k?7ܪ/i27� S.m7ڢU|V <@(P$�QKqQE�Lq17QEP"�b ;WzAMXʟ/&?y|^U�ֵ֥{ \|\jfpp=WDe�h9;B_n}|&8uo_d 8m8.Wb~\/=G=1:S+߭�I:rRp9~tﻜ9]Í{Q_ڛE�P˱~Xw~҂J PLz)cֲărY*h|;/K>Ր?�I:?\ɶ�/OW3LcYg2 �vEZЋ5}{!�?osVc%�4,<5~w?V]5!�v*50e~iA�6LW^J.]G@5�3k3I%#_<A0:7OGJr./GA�m߶|^Tt5,йHWr> 1ɯ6,�nUT>*�KYC}jp෺1X�=,Պ0_ �hTH�+_�oS!؆Ř;�!{2`'D@U�`V7zN`�SS$�1Id�`7! N �֕stH�J#!e1K�jՏIsJƜ> @D 'Ƅ`?:�7R�jOo$`.闵9o;��������������������������������������������������������������������������������������������������������������������������������6A__P����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/preferences.ui�������������������������������������������������0000664�0000000�0000000�00000143003�14401326716�0022263�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>442</height> </rect> </property> <property name="windowTitle"> <string>Preferences</string> </property> <layout class="QGridLayout" name="gridLayout"> <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="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <attribute name="title"> <string>Pop-ups</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> <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="2" column="0" colspan="4"> <widget class="QGroupBox" name="groupBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="title"> <string>Default options</string> </property> <layout class="QGridLayout" name="gridLayout_5" rowstretch="0,0,0,0,0,0,0,0" columnstretch="1,0"> <item row="7" 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="Fixed"> <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="Fixed"> <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="Fixed"> <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="0" column="1"> <widget class="QComboBox" name="comboUIAction"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <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> </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><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></string> </property> <property name="text"> <string>Action</string> </property> </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="Fixed"> <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="1" column="1"> <widget class="QComboBox" name="comboUIDuration"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <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="6" 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="2" column="1"> <widget class="QComboBox" name="comboUITarget"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <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="4" column="0" colspan="2"> <widget class="Line" name="line_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_5"> <property name="text"> <string>Default target</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="5" column="0"> <widget class="QLabel" name="label_17"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <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="5" column="1"> <widget class="QCheckBox" name="showAdvancedCheck"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" 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> </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="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="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="1" column="0" colspan="3"> <widget class="QLabel" name="label"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <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="0" column="0" colspan="3"> <widget class="QLabel" name="label_16"> <property name="text"> <string>Disable pop-ups, only display an notification</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </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="2" column="0"> <widget class="QCheckBox" name="checkUIRules"> <property name="toolTip"> <string><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></string> </property> <property name="text"> <string>Don't save rules of duration</string> </property> </widget> </item> <item row="3" 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>Desktop notifications</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> <item row="0" column="1"> <widget class="QComboBox" name="comboUITheme"> <item> <property name="text"> <string>System</string> </property> </item> </widget> </item> <item row="4" 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="title"> <string>Events tab columns</string> </property> <layout class="QGridLayout" name="gridLayout_7"> <item row="0" 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="6" column="0"> <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="0" 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>true</bool> </property> </widget> </item> <item row="4" 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="2" column="0"> <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="2" column="1"> <widget class="QCheckBox" name="checkHideDst"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Destination</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="4" column="1"> <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> </layout> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_21"> <property name="text"> <string>Theme</string> </property> </widget> </item> <item row="2" column="1"> <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> </widget> </item> <item row="1" column="0" colspan="2"> <widget class="QLabel" name="labelThemeError"> <property name="text"> <string/> </property> </widget> </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,0,0,0,0,0,0,0"> <item row="9" 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="QComboBox" name="comboNodes"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> </widget> </item> <item row="5" 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="8" column="2"> <widget class="QCheckBox" name="checkInterceptUnknown"> <property name="text"> <string/> </property> </widget> </item> <item row="7" 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="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="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="6" column="0"> <widget class="QLabel" name="label_10"> <property name="toolTip"> <string><html><head/><body><p>The default action will take place when there's no UI connected.</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="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> <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="9" column="2"> <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="7" column="2"> <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="4" 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="8" 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="6" column="2"> <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> </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="10" column="2"> <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="4" column="2"> <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> <item row="5" column="2"> <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="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="10" column="0"> <widget class="QLabel" name="label_14"> <property name="text"> <string>Default log level</string> </property> </widget> </item> <item row="3" column="0" colspan="3"> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> </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="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="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="label_20"> <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="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="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="label_19"> <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="text"> <string>Maximum days of events to keep</string> </property> </widget> </item> <item row="0" column="4"> <widget class="QPushButton" name="cmdDBMaxDaysDown"> <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="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="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> </layout> </widget> </widget> </item> <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="1" column="0"> <widget class="QLabel" name="statusLabel"> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> </layout> </widget> <resources/> <connections/> </ui> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/process_details.ui���������������������������������������������0000664�0000000�0000000�00000016637�14401326716�0023161�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.5.8.1/ui/opensnitch/res/prompt.ui������������������������������������������������������0000664�0000000�0000000�00000061520�14401326716�0021306�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>520</width> <height>308</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="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> </widget> </item> <item> <widget class="QLabel" name="appPathLabel"> <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="font"> <font> <pointsize>10</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> <widget class="QLabel" name="messageLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="maximumSize"> <size> <width>520</width> <height>40</height> </size> </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>5</number> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item> <layout class="QGridLayout" name="gridLayout"> <property name="topMargin"> <number>6</number> </property> <property name="verticalSpacing"> <number>3</number> </property> <item row="6" column="1"> <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> <family>Ubuntu</family> <pointsize>10</pointsize> <weight>75</weight> <italic>false</italic> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>User ID</string> </property> </widget> </item> <item row="1" column="2"> <spacer name="horizontalSpacer"> <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 row="0" column="1"> <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>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="0"> <widget class="QCheckBox" name="checkUserID"> <property name="text"> <string/> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="checkDstIP"> <property name="text"> <string/> </property> </widget> </item> <item row="6" column="3"> <widget class="QLabel" name="uidLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>90</width> <height>0</height> </size> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>TextLabel</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="1" column="3"> <widget class="QLabel" name="sourceIPLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>90</width> <height>0</height> </size> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>TextLabel</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="1" column="1"> <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> <family>Ubuntu</family> <pointsize>10</pointsize> <weight>75</weight> <italic>false</italic> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Source IP</string> </property> </widget> </item> <item row="7" column="1"> <widget class="QLabel" name="pidLabelWidget"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <family>Ubuntu</family> <pointsize>10</pointsize> <weight>75</weight> <italic>false</italic> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Process ID</string> </property> </widget> </item> <item row="5" column="3"> <widget class="QLabel" name="destPortLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>90</width> <height>0</height> </size> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>TextLabel</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="2" column="3"> <widget class="QLabel" name="destIPLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>90</width> <height>0</height> </size> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>TextLabel</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="2" column="2"> <widget class="QComboBox" name="whatIPCombo"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumContentsLength"> <number>0</number> </property> </widget> </item> <item row="0" column="3"> <widget class="QLabel" name="cwdLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>90</width> <height>0</height> </size> </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::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> <property name="textInteractionFlags"> <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="5" column="0"> <widget class="QCheckBox" name="checkDstPort"> <property name="text"> <string/> </property> </widget> </item> <item row="2" column="1"> <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> <family>Ubuntu</family> <pointsize>10</pointsize> <weight>75</weight> <italic>false</italic> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Destination IP</string> </property> </widget> </item> <item row="5" column="1"> <widget class="QLabel" name="destPortLabel_1"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="font"> <font> <family>Ubuntu</family> <pointsize>10</pointsize> <weight>75</weight> <italic>false</italic> <bold>true</bold> <kerning>true</kerning> </font> </property> <property name="text"> <string>Dst Port</string> </property> </widget> </item> <item row="7" column="3"> <widget class="QLabel" name="pidLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>90</width> <height>0</height> </size> </property> <property name="font"> <font> <pointsize>10</pointsize> <kerning>true</kerning> </font> </property> <property name="text"> <string>TextLabel</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> </layout> </item> <item> <widget class="QLabel" name="label_5"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string notr="true"/> </property> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout" stretch="3,2,0,0,0"> <property name="sizeConstraint"> <enum>QLayout::SetMinimumSize</enum> </property> <item> <widget class="QComboBox" name="whatCombo"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>97</width> <height>26</height> </size> </property> <property name="maximumSize"> <size> <width>204</width> <height>30</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="minimumSize"> <size> <width>97</width> <height>26</height> </size> </property> <property name="maximumSize"> <size> <width>204</width> <height>30</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="QPushButton" name="denyButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>80</width> <height>26</height> </size> </property> <property name="maximumSize"> <size> <width>16777215</width> <height>30</height> </size> </property> <property name="text"> <string>Deny</string> </property> <property name="icon"> <iconset theme="emblem-important"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="applyButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>80</width> <height>26</height> </size> </property> <property name="maximumSize"> <size> <width>204</width> <height>30</height> </size> </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> <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>24</width> <height>24</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> <resources> <include location="resources.qrc"/> <include location="../../../../../../../../.designer/backup/resources.qrc"/> </resources> <connections/> </ui> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/resources.qrc��������������������������������������������������0000664�0000000�0000000�00000000260�14401326716�0022141�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.5.8.1/ui/opensnitch/res/ruleseditor.ui�������������������������������������������������0000664�0000000�0000000�00000077337�14401326716�0022343�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>491</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="5" column="1"> <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>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>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="6" column="1"> <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="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QLineEdit" name="uidLine"> <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="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="0" column="0" colspan="2"> <widget class="QCheckBox" name="dstPortCheck"> <property name="text"> <string>To this port</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="dstHostCheck"> <property name="text"> <string>To this host</string> </property> </widget> </item> <item row="0" column="2" colspan="3"> <spacer name="horizontalSpacer_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>133</width> <height>20</height> </size> </property> </spacer> </item> <item row="1" 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>TCP</string> </property> </item> <item> <property name="text"> <string>UDP</string> </property> </item> <item> <property name="text"> <string>UDPLITE</string> </property> </item> <item> <property name="text"> <string>TCP6</string> </property> </item> <item> <property name="text"> <string>UDP6</string> </property> </item> <item> <property name="text"> <string>UDPLITE6</string> </property> </item> </widget> </item> <item row="0" column="5"> <widget class="QLineEdit" name="dstPortLine"> <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><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></string> </property> </widget> </item> <item row="1" column="0" colspan="4"> <widget class="QCheckBox" name="protoCheck"> <property name="text"> <string>Protocol</string> </property> </widget> </item> <item row="3" column="0"> <widget class="QCheckBox" name="dstIPCheck"> <property name="text"> <string>To this IP / Network</string> </property> </widget> </item> <item row="3" column="1"> <spacer name="horizontalSpacer_9"> <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="1"> <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 row="2" 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="3" 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="editable"> <bool>true</bool> </property> <item> <property name="text"> <string>LAN</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> </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="document-open"> <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="document-open"> <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="document-open"> <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="document-open"> <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"> <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="1"> <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="8" column="1"> <widget class="Line" name="line"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> <item row="7" column="1"> <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="1" column="1"> <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="3" column="1"> <widget class="QCheckBox" name="enableCheck"> <property name="text"> <string>Enable</string> </property> </widget> </item> <item row="4" column="1"> <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="QLabel" name="label"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>Name</string> </property> </widget> </item> <item row="0" column="1"> <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>leave blank to autocreate</string> </property> <property name="clearButtonEnabled"> <bool>true</bool> </property> </widget> </item> </layout> </widget> <resources/> <connections/> </ui> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/res/stats.ui�������������������������������������������������������0000664�0000000�0000000�00000153431�14401326716�0021126�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>600</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>.</normaloff>.</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> </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>.</normaloff>.</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>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QComboBox" name="limitCombo"> <property name="currentIndex"> <number>0</number> </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>.</normaloff>.</iconset> </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="saveButton"> <property name="toolTip"> <string>Save to CSV.</string> </property> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="document-save"> <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> </property> <property name="shortcut"> <string>Ctrl+S</string> </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> <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> <family>Ubuntu</family> <pointsize>11</pointsize> <weight>75</weight> <italic>false</italic> <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> </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> <attribute name="headerVisible"> <bool>false</bool> </attribute> <column> <property name="text"> <string notr="true">1</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="system"> <normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</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="GenericTableView" name="rulesTable"> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</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>.</normaloff>.</iconset> </property> </item> <item> <property name="text"> <string>Permanent</string> </property> <property name="icon"> <iconset theme="security-medium"> <normaloff>.</normaloff>.</iconset> </property> </item> <item> <property name="text"> <string>Temporary</string> </property> <property name="icon"> <iconset theme="edit-clear"> <normaloff>.</normaloff>.</iconset> </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="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> </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> </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> </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> </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="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="QPushButton" name="cmdProcDetails"> <property name="text"> <string/> </property> <property name="icon"> <iconset theme="system-search"> <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> </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="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="emblem-web"> <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> </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="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> </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> </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> <family>DejaVu Sans</family> <pointsize>8</pointsize> <weight>75</weight> <italic>false</italic> <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> <family>DejaVu Sans</family> <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> <family>DejaVu Sans</family> <pointsize>8</pointsize> <weight>75</weight> <italic>false</italic> <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> <family>DejaVu Sans</family> <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> <family>DejaVu Sans</family> <pointsize>8</pointsize> <weight>75</weight> <italic>false</italic> <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> <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> <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> <family>DejaVu Sans</family> <pointsize>8</pointsize> <weight>75</weight> <italic>false</italic> <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> <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> <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> <family>DejaVu Sans</family> <pointsize>8</pointsize> <weight>75</weight> <italic>false</italic> <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> </customwidgets> <resources> <include location="resources.qrc"/> </resources> <connections/> </ui> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/service.py���������������������������������������������������������0000664�0000000�0000000�00000075136�14401326716�0020657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from PyQt5 import QtWidgets, QtGui, QtCore from datetime import datetime, timedelta from threading import Thread, Lock, Event import grpc import os import sys import json path = os.path.abspath(os.path.dirname(__file__)) sys.path.append(path) from opensnitch import ui_pb2 from opensnitch import ui_pb2_grpc from opensnitch.dialogs.prompt import PromptDialog from opensnitch.dialogs.stats import StatsDialog from opensnitch.notifications import DesktopNotifications 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 from opensnitch.utils import Message 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) _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) # .desktop filename located under /usr/share/applications/ DESKTOP_FILENAME = "opensnitch_ui.desktop" def __init__(self, app, on_exit): super(UIService, self).__init__() self.MENU_ENTRY_STATS = QtCore.QCoreApplication.translate("contextual_menu", "Statistics") 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_status, db_error = self._db.initialize( dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY), dbfile=db_file ) 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._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._last_stats = {} self._last_items = { 'hosts':{}, 'procs':{}, 'addrs':{}, 'ports':{}, 'users':{} } self._show_gui_if_tray_not_available() self._cleaner = None if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST): self._start_db_cleaner() # 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._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._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.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) 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(): gui.show() QtCore.QTimer.singleShot(10000, __show_gui) 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, 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) def _show_systray_message(self, title, body, icon): 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: self._desktop_notifications.show(title, body, os.path.join(self._path, "res/icon-white.svg")) 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() 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": 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(proto, addr, 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 _node = self._nodes.get_node(proto+":"+addr) 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)", (addr, 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)), "%s:%s" % (proto, addr), 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, "%s:%s" % (proto, addr) ) 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): _default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) temp_cfg = json.loads(node_config) try: if _default_action == Config.ACTION_DENY_IDX: temp_cfg['DefaultAction'] = Config.ACTION_DENY else: temp_cfg['DefaultAction'] = Config.ACTION_ALLOW node_config = json.dumps(temp_cfg) except Exception as e: print("error parsing node's configuration:", e) return node_config @QtCore.pyqtSlot(dict) def _on_node_actions(self, kwargs): if kwargs['action'] == self.NODE_ADD: n = self._nodes.add(kwargs['peer'], kwargs['node_config']) if n != None: 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, str(rule.enabled), str(rule.precedence), rule.action, rule.duration, rule.operator.type, str(rule.operator.sensitive), rule.operator.operand, 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 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 proto, addr = self._get_peer(context.peer()) rule, timeout_triggered = self._prompt_dialog.promptUser(request, self._is_local_request(proto, addr), context.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) if rule.duration in Config.RULES_DURATION_FILTER: self._node_actions_trigger.emit( { 'action': self.DELETE_RULE, 'name': rule.name, 'addr': context.peer() } ) else: self._node_actions_trigger.emit( { 'action': self.ADD_RULE, 'peer': context.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: 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) except Exception as e: print("[Notifications] exception adding new node:", e) context.cancel() node_config.config = self._overwrite_nodes_config(node_config.config) return node_config 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) 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.5.8.1/ui/opensnitch/utils.py�����������������������������������������������������������0000664�0000000�0000000�00000022310�14401326716�0020341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ from PyQt5 import QtCore, QtWidgets, QtGui from opensnitch.version import 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 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): if not Themes.AVAILABLE: return 0, "" theme = self._cfg.getSettings(self._cfg.DEFAULT_THEME) if theme != "" and theme != None: # 0 == System return self.list_themes().index(theme)+1, theme return 0, "" def save_theme(self, theme_idx, theme): if not Themes.AVAILABLE: return 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 = 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. Themes.qtmaterial_apply_stylesheet(app, theme=theme_name, invert_secondary=invert) except Exception as e: print("Themes.load_theme() exception:", e) 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 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) ) 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 = version.split(".") rMayor, rMinor, rPatch = daemon_version.split(".") 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 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 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/opensnitch/version.py���������������������������������������������������������0000664�0000000�0000000�00000000022�14401326716�0020662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������version = '1.5.8' ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/requirements.txt��������������������������������������������������������������0000664�0000000�0000000�00000000121�14401326716�0017735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������grpcio-tools>=1.10.1 pyinotify==0.9.6 unicode_slugify==0.1.3 pyqt5>=5.6 protobuf �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/resources/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016471�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/resources/icons/��������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017604�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/resources/icons/48x48/��������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020403�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/resources/icons/48x48/opensnitch-ui.png���������������������������������������0000664�0000000�0000000�00000003452�14401326716�0023702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���0���0���W��� pHYs����od���tEXtSoftware�www.inkscape.org<��IDATh{PT?wƵ)H𴰘Dt]qbmjPIB:ͣ* B&Wh 4&$VScF&I)q\T`? 8w|{=HBnfn4V77}uo� 0x DP_oa �?�b_NHDZb.i@_By}1/q Lt ~w*KX,fbccEbb"$IC4h< M:`ho?޶O1utuwQ@-z|{jϽmv`=vn/m K s[qՠ V+k֮ Z^6�3BŖW?FY=L3aG2*" uuΈlSc`>*?�\9 (L#GZLgň �HMM@{`0HP! _F]4<ȋ/1`0x"]w7h>#æ  7߬wO><0j�8O23P\dx3g֯0jrLӷtfJPD3ֽk˴ErumubOCCCD$Xr;w mjZX&Mқg7?/KPcd;?j 7*<0TrwTE INI| ee.y~΃#ƧEfp[[BRC黎? zob<ibj4Q_} !Lס@)p ʚp8:Z[ru kBpp=>7خoUB3UѩHEQh8 ttvP'LJMhy! w᳛X466տ1~)_}@ GA 8w\$Y߮FUC}$Ib[5uRIBܰ\*)Ȏ;DŽ=={x0LGF�<0]BX:R/ v;GOۣΫ-{f6P utP,y9Xtɰ P__7/{`mWz̜ @kk먉B K0MN{Df3E/1L&o(vO>;M 5+ǜ9-$&%k&s懝+ez�l!Zh={|r}$-2Щ92˯J*wCXj%yys|m97,={ | ?7? 7Opr ة_l6J틁<Pќ9ɜUOP0ǔn7taZ)^޵ pIBxx�-^l `2zy&Nk[^nN@C�$%%QY)ƒ-F{EЩQ0Q[SKuu ---\@ш=…r}s1Tį�� -\[Q&@__DMf3fwXɄ2l#PwrC[P;61Μ@{$HgC«x` |LM!{6ٸ?fUƭn4x_]����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/resources/icons/64x64/��������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0020377�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/resources/icons/64x64/opensnitch-ui.png���������������������������������������0000664�0000000�0000000�00000004514�14401326716�0023676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���@���@���iq��� pHYs����od���tEXtSoftware�www.inkscape.org<��IDATx{P?veFv">%2(FҰhj5QѴ*L5FFؙb q8>P@N4 >Qhu|@5" ~cE]4|sgsν+Ȳ̏`l: Fm@!�7A�@ :7` @$F Eo8,CŁ8Кa�S^C"c@`AlSU流+f) ׮SU]MXh(:u䉎v$Kp[[,0XɅž=y9sANEbblhL|1QEQTu1 v{cOK8@h"l"P-C造/`sң"GhQ*cƌV2BfWdHx"c2X C>9합bTM ~T{oz~KN Ƴn}:`QeH'D>7&`˖?L-|+d0/R~Qޣ;QQQk'N23U$IbDFF>20D|1 exCM?SRRBccOE}$C7dpOǶKōa %(Ag99`)2(iUUU,J]ᕮ@B$No .RvxR0Q^^09X,|q9*pwr^9Av}}<{n\é*R;o) 7nʾ-BEˢrY{d<vFڵ 99Hx%o4: d*y{󤆆/M_KΝ}o:fp{�Y;NF(h49pY_E+Xub Q|\ꅨ{('… ط/=뱎C۵emr\/l|BٸXtnSN~H>F~~k[˭۷Ija.jk3ddΟ?ozZdlDvvViPH9={:ex[ 4K --Li5?2P "<<?,رq.FrsFD֥bXgJ3͐+4rY*9p۾̭̘½{, (%f"yRDiiN濷nQQ^Aq}:7F]:qߺ}5PPP~}{!!vă҃^�PFh[ڛ^sjVddl{\!UTIPpTRv^k2[*aŊU\t) vM|BH] ,S$Ц{ERRfRSrz}Et V,i€ؑ$$*Ȳ"+'UUU&qƍ4yٟd;DQ$$$f~aDE= hxYaczO~acVkNgG2RB<T8ln,YQ~@}}}t{˗/IQɡ"f߃t:̙kϡM[ Zor PYpArǗ*.;e4E]wnf-:Bk%;?@yEo:u$uQ* inϲm۪+fՠ Y}<àA|oitaB$vP\\ ټÀ#ưaCڍpFBy'|z6o?ߎoZ7ne;tڅ!~D=z[}6 _ SLŐl`С`sf̘Fc/-c dFv~Ǝ'A!C?t`괩v  p8d. kjHK[!)'NL'|:t:C WWx!VJ=dҀ'%z}6n`2 MOi4�Z8!]RK- OuƎ9!GHYio}VXŹZ@ ,,ٳg23٫3-uٕLGc{jƧ)-)x׆ݫɎO111qcFL`P$:M-B @\Q9Tup^A2|XkY\)8<])F' @ oKsw'=z"S=EHv\ FPL24 TP)yd;=:Z<c6 hu@ 6Zl1Mz����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/resources/icons/opensnitch-ui.svg���������������������������������������������0000664�0000000�0000000�00000013700�14401326716�0023113�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.5.8.1/ui/resources/io.github.evilsocket.opensnitch.appdata.xml�������������������������0000664�0000000�0000000�00000004224�14401326716�0027036�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.5.8.1/ui/resources/kcm_opensnitch.desktop����������������������������������������������0000664�0000000�0000000�00000000436�14401326716�0023073�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.5.8.1/ui/resources/opensnitch_ui.desktop�����������������������������������������������0000664�0000000�0000000�00000001073�14401326716�0022734�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Desktop Entry] Type=Application Name=OpenSnitch Exec=/bin/sh -c "pkill -15 opensnitch-ui; 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.5.8.1/ui/setup.py����������������������������������������������������������������������0000664�0000000�0000000�00000003271�14401326716�0016174�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.5.8.1/ui/tests/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015621�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/tests/README.md���������������������������������������������������������������0000664�0000000�0000000�00000001674�14401326716�0017110�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.5.8.1/ui/tests/__init__.py�������������������������������������������������������������0000664�0000000�0000000�00000000000�14401326716�0017720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/tests/dialogs/����������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017243�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/ui/tests/dialogs/__init__.py�����������������������������������������������������0000664�0000000�0000000�00000002123�14401326716�0021352�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, "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.5.8.1/ui/tests/dialogs/test_preferences.py���������������������������������������������0000664�0000000�0000000�00000013715�14401326716�0023164�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.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['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.5.8.1/ui/tests/dialogs/test_ruleseditor.py���������������������������������������������0000664�0000000�0000000�00000041546�14401326716�0023227�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.5.8.1/ui/tests/test_nodes.py�����������������������������������������������������������0000664�0000000�0000000�00000012555�14401326716�0020352�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.5.8.1/utils/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0015202�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/utils/legacy/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016446�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/utils/legacy/make_ads_rules.py���������������������������������������������������0000664�0000000�0000000�00000003367�14401326716�0022007�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", "http://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.5.8.1/utils/scripts/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0016671�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/utils/scripts/ads/���������������������������������������������������������������0000775�0000000�0000000�00000000000�14401326716�0017440�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������opensnitch-1.5.8.1/utils/scripts/ads/update_adlists.sh����������������������������������������������0000775�0000000�0000000�00000007256�14401326716�0023016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # opensnitch - 2021 # # 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://curben.gitlab.io/malware-filter/urlhaus-filter-hosts.txt" "https://raw.githubusercontent.com/Kees1958/W3C_annual_most_used_survey_blocklist/master/TOP_EU_US_Ads_Trackers_HOST" "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=plaintex") adsListNames=( "urlhaus-filter-hosts.txt" "top_eu_us_ads_trackers.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_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]}) if [ ! -z $remoteSize ]; then if [ "$remoteSize" != "$localSize" ]; then echo "[+] downloading new ads list... ${adsList[$idx]}, $remoteSize, $localSize" curl --silent "${adsList[$idx]}" -o $adsDir/${adsListNames[$idx]} reload=1 else echo "[-] ads list not updated yet: $remoteSize, $localSize - ${adsList[$idx]}" fi else echo "[!] No content-length header found: ${adsList[$idx]}" fi done } if [ ! -d $adsDir ]; then mkdir -p $adsDir fi cd $adsDir download_ads_list download_cname_trackers ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������